summaryrefslogtreecommitdiff
path: root/chromium/device
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-08-28 15:28:34 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-08-28 13:54:51 +0000
commit2a19c63448c84c1805fb1a585c3651318bb86ca7 (patch)
treeeb17888e8531aa6ee5e85721bd553b832a7e5156 /chromium/device
parentb014812705fc80bff0a5c120dfcef88f349816dc (diff)
downloadqtwebengine-chromium-2a19c63448c84c1805fb1a585c3651318bb86ca7.tar.gz
BASELINE: Update Chromium to 69.0.3497.70
Change-Id: I2b7b56e4e7a8b26656930def0d4575dc32b900a0 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/device')
-rw-r--r--chromium/device/BUILD.gn44
-rw-r--r--chromium/device/base/features.cc19
-rw-r--r--chromium/device/base/features.h11
-rw-r--r--chromium/device/bluetooth/BUILD.gn13
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter.h4
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_android.cc8
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_mac.h20
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_mac.mm29
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_unittest.cc64
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_win.cc9
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_win.h2
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_win_unittest.cc4
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_winrt.cc533
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_winrt.h46
-rw-r--r--chromium/device/bluetooth/bluetooth_advertisement.h5
-rw-r--r--chromium/device/bluetooth/bluetooth_advertisement_mac.h85
-rw-r--r--chromium/device/bluetooth/bluetooth_advertisement_mac.mm69
-rw-r--r--chromium/device/bluetooth/bluetooth_classic_win.cc75
-rw-r--r--chromium/device/bluetooth/bluetooth_classic_win.h22
-rw-r--r--chromium/device/bluetooth/bluetooth_classic_win_fake.cc20
-rw-r--r--chromium/device/bluetooth/bluetooth_classic_win_fake.h13
-rw-r--r--chromium/device/bluetooth/bluetooth_device.cc28
-rw-r--r--chromium/device/bluetooth/bluetooth_device.h52
-rw-r--r--chromium/device/bluetooth/bluetooth_device_unittest.cc230
-rw-r--r--chromium/device/bluetooth/bluetooth_device_winrt.cc281
-rw-r--r--chromium/device/bluetooth/bluetooth_device_winrt.h55
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_characteristic.h3
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_discoverer_winrt.cc143
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_discoverer_winrt.h65
-rw-r--r--chromium/device/bluetooth/bluetooth_local_gatt_characteristic_unittest.cc30
-rw-r--r--chromium/device/bluetooth/bluetooth_local_gatt_service.h32
-rw-r--r--chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.h62
-rw-r--r--chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.mm175
-rw-r--r--chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac_unittest.mm314
-rw-r--r--chromium/device/bluetooth/bluetooth_low_energy_peripheral_manager_delegate.h33
-rw-r--r--chromium/device/bluetooth/bluetooth_low_energy_peripheral_manager_delegate.mm72
-rw-r--r--chromium/device/bluetooth/bluetooth_low_energy_win.cc20
-rw-r--r--chromium/device/bluetooth/bluetooth_low_energy_win.h9
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_characteristic.cc101
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_characteristic.h64
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc22
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_android.h11
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h19
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm61
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_win.cc53
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_win.h19
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.cc78
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.h63
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_descriptor.h3
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_service.cc42
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_service.h23
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_service_android.cc26
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_service_android.h9
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_service_mac.h10
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_service_mac.mm74
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_service_win.cc78
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_service_win.h13
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_service_winrt.cc95
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_service_winrt.h63
-rw-r--r--chromium/device/bluetooth/bluetooth_task_manager_win.cc158
-rw-r--r--chromium/device/bluetooth/bluetooth_task_manager_win.h26
-rw-r--r--chromium/device/bluetooth/bluetooth_task_manager_win_unittest.cc1
-rw-r--r--chromium/device/bluetooth/bluetooth_uuid.cc35
-rw-r--r--chromium/device/bluetooth/bluetooth_uuid.h18
-rw-r--r--chromium/device/bluetooth/bluetooth_uuid_unittest.cc46
-rw-r--r--chromium/device/bluetooth/bluez/bluetooth_device_bluez.cc44
-rw-r--r--chromium/device/bluetooth/bluez/bluetooth_device_bluez.h16
-rw-r--r--chromium/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc155
-rw-r--r--chromium/device/bluetooth/bluez/bluetooth_local_gatt_characteristic_bluez.cc10
-rw-r--r--chromium/device/bluetooth/bluez/bluetooth_local_gatt_characteristic_bluez.h6
-rw-r--r--chromium/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.cc60
-rw-r--r--chromium/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h29
-rw-r--r--chromium/device/bluetooth/bluez/bluetooth_remote_gatt_service_bluez.cc42
-rw-r--r--chromium/device/bluetooth/bluez/bluetooth_remote_gatt_service_bluez.h16
-rw-r--r--chromium/device/bluetooth/cast/bluetooth_adapter_cast.cc52
-rw-r--r--chromium/device/bluetooth/cast/bluetooth_adapter_cast.h9
-rw-r--r--chromium/device/bluetooth/cast/bluetooth_device_cast.cc14
-rw-r--r--chromium/device/bluetooth/cast/bluetooth_device_cast.h4
-rw-r--r--chromium/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.cc25
-rw-r--r--chromium/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.h8
-rw-r--r--chromium/device/bluetooth/cast/bluetooth_remote_gatt_service_cast.cc28
-rw-r--r--chromium/device/bluetooth/cast/bluetooth_remote_gatt_service_cast.h8
-rw-r--r--chromium/device/bluetooth/chromeos/bluetooth_utils.cc6
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_device_client.cc46
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_device_client.h10
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_gatt_application_service_provider.cc27
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_gatt_attribute_helpers.cc34
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_gatt_attribute_helpers.h5
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_gatt_attribute_value_delegate.h21
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc42
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h17
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.cc26
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.h15
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.cc126
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.h5
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_gatt_descriptor_delegate_wrapper.h8
-rw-r--r--chromium/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.cc16
-rw-r--r--chromium/device/bluetooth/dbus/bluez_dbus_manager.cc7
-rw-r--r--chromium/device/bluetooth/dbus/fake_bluetooth_device_client.cc29
-rw-r--r--chromium/device/bluetooth/dbus/fake_bluetooth_device_client.h14
-rw-r--r--chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc46
-rw-r--r--chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.h14
-rw-r--r--chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_service_provider.cc38
-rw-r--r--chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_service_provider.h8
-rw-r--r--chromium/device/bluetooth/device_unittest.cc10
-rw-r--r--chromium/device/bluetooth/event_utils_winrt.h161
-rw-r--r--chromium/device/fido/BUILD.gn36
-rw-r--r--chromium/device/fido/OWNERS2
-rw-r--r--chromium/device/fido/attestation_object.cc11
-rw-r--r--chromium/device/fido/attestation_object.h24
-rw-r--r--chromium/device/fido/attestation_statement.h2
-rw-r--r--chromium/device/fido/attestation_statement_formats.cc (renamed from chromium/device/fido/fido_attestation_statement.cc)51
-rw-r--r--chromium/device/fido/attestation_statement_formats.h (renamed from chromium/device/fido/fido_attestation_statement.h)37
-rw-r--r--chromium/device/fido/attestation_statement_formats_unittest.cc104
-rw-r--r--chromium/device/fido/attested_credential_data.cc70
-rw-r--r--chromium/device/fido/attested_credential_data.h9
-rw-r--r--chromium/device/fido/authenticator_data.cc38
-rw-r--r--chromium/device/fido/authenticator_data.h24
-rw-r--r--chromium/device/fido/authenticator_get_assertion_response.cc60
-rw-r--r--chromium/device/fido/authenticator_get_assertion_response.h13
-rw-r--r--chromium/device/fido/authenticator_get_info_response.cc57
-rw-r--r--chromium/device/fido/authenticator_get_info_response.h22
-rw-r--r--chromium/device/fido/authenticator_make_credential_response.cc18
-rw-r--r--chromium/device/fido/authenticator_make_credential_response.h15
-rw-r--r--chromium/device/fido/authenticator_supported_options.cc53
-rw-r--r--chromium/device/fido/authenticator_supported_options.h7
-rw-r--r--chromium/device/fido/ctap2_device_operation.h79
-rw-r--r--chromium/device/fido/ctap_get_assertion_request.cc132
-rw-r--r--chromium/device/fido/ctap_get_assertion_request.h23
-rw-r--r--chromium/device/fido/ctap_make_credential_request.cc152
-rw-r--r--chromium/device/fido/ctap_make_credential_request.h19
-rw-r--r--chromium/device/fido/ctap_register_operation.cc46
-rw-r--r--chromium/device/fido/ctap_register_operation.h56
-rw-r--r--chromium/device/fido/ctap_request_unittest.cc222
-rw-r--r--chromium/device/fido/ctap_response_fuzzer.cc2
-rw-r--r--chromium/device/fido/ctap_response_unittest.cc385
-rw-r--r--chromium/device/fido/device_operation.h30
-rw-r--r--chromium/device/fido/device_response_converter.cc35
-rw-r--r--chromium/device/fido/fake_fido_discovery.cc2
-rw-r--r--chromium/device/fido/fake_fido_discovery_unittest.cc19
-rw-r--r--chromium/device/fido/fido_authenticator.h4
-rw-r--r--chromium/device/fido/fido_ble_connection.cc4
-rw-r--r--chromium/device/fido/fido_ble_connection.h9
-rw-r--r--chromium/device/fido/fido_ble_connection_unittest.cc7
-rw-r--r--chromium/device/fido/fido_ble_device.cc33
-rw-r--r--chromium/device/fido/fido_ble_device.h4
-rw-r--r--chromium/device/fido/fido_ble_device_unittest.cc87
-rw-r--r--chromium/device/fido/fido_ble_frames.cc1
-rw-r--r--chromium/device/fido/fido_ble_frames.h3
-rw-r--r--chromium/device/fido/fido_cable_device.cc116
-rw-r--r--chromium/device/fido/fido_cable_device.h23
-rw-r--r--chromium/device/fido/fido_cable_device_unittest.cc72
-rw-r--r--chromium/device/fido/fido_cable_discovery.cc77
-rw-r--r--chromium/device/fido/fido_cable_discovery.h30
-rw-r--r--chromium/device/fido/fido_cable_discovery_unittest.cc87
-rw-r--r--chromium/device/fido/fido_cable_handshake_handler.cc173
-rw-r--r--chromium/device/fido/fido_cable_handshake_handler.h62
-rw-r--r--chromium/device/fido/fido_cable_handshake_handler_fuzzer.cc38
-rw-r--r--chromium/device/fido/fido_cable_handshake_handler_unittest.cc411
-rw-r--r--chromium/device/fido/fido_constants.cc18
-rw-r--r--chromium/device/fido/fido_constants.h81
-rw-r--r--chromium/device/fido/fido_device.cc45
-rw-r--r--chromium/device/fido/fido_device.h20
-rw-r--r--chromium/device/fido/fido_device_authenticator.cc33
-rw-r--r--chromium/device/fido/fido_device_authenticator.h3
-rw-r--r--chromium/device/fido/fido_discovery.cc15
-rw-r--r--chromium/device/fido/fido_discovery.h6
-rw-r--r--chromium/device/fido/fido_discovery_unittest.cc29
-rw-r--r--chromium/device/fido/fido_hid_device.cc29
-rw-r--r--chromium/device/fido/fido_hid_device.h7
-rw-r--r--chromium/device/fido/fido_hid_device_unittest.cc6
-rw-r--r--chromium/device/fido/fido_parsing_utils.cc12
-rw-r--r--chromium/device/fido/fido_parsing_utils.h16
-rw-r--r--chromium/device/fido/fido_parsing_utils_unittest.cc32
-rw-r--r--chromium/device/fido/fido_request_handler.h25
-rw-r--r--chromium/device/fido/fido_request_handler_base.cc43
-rw-r--r--chromium/device/fido/fido_request_handler_base.h15
-rw-r--r--chromium/device/fido/fido_request_handler_unittest.cc82
-rw-r--r--chromium/device/fido/fido_task.cc31
-rw-r--r--chromium/device/fido/fido_task.h16
-rw-r--r--chromium/device/fido/fido_test_data.h607
-rw-r--r--chromium/device/fido/get_assertion_handler_unittest.cc112
-rw-r--r--chromium/device/fido/get_assertion_request_handler.cc63
-rw-r--r--chromium/device/fido/get_assertion_request_handler.h6
-rw-r--r--chromium/device/fido/get_assertion_task.cc96
-rw-r--r--chromium/device/fido/get_assertion_task.h19
-rw-r--r--chromium/device/fido/get_assertion_task_unittest.cc198
-rw-r--r--chromium/device/fido/mac/authenticator.h40
-rw-r--r--chromium/device/fido/mac/authenticator.mm103
-rw-r--r--chromium/device/fido/mac/browsing_data_deletion.h40
-rw-r--r--chromium/device/fido/mac/browsing_data_deletion.mm162
-rw-r--r--chromium/device/fido/mac/browsing_data_deletion_unittest.mm210
-rw-r--r--chromium/device/fido/mac/credential_metadata.cc303
-rw-r--r--chromium/device/fido/mac/credential_metadata.h151
-rw-r--r--chromium/device/fido/mac/credential_metadata_unittest.cc154
-rw-r--r--chromium/device/fido/mac/get_assertion_operation.h4
-rw-r--r--chromium/device/fido/mac/get_assertion_operation.mm46
-rw-r--r--chromium/device/fido/mac/get_assertion_operation_unittest_mac.mm7
-rw-r--r--chromium/device/fido/mac/keychain.h3
-rw-r--r--chromium/device/fido/mac/make_credential_operation.h5
-rw-r--r--chromium/device/fido/mac/make_credential_operation.mm67
-rw-r--r--chromium/device/fido/mac/make_credential_operation_unittest_mac.mm5
-rw-r--r--chromium/device/fido/mac/operation_base.h40
-rw-r--r--chromium/device/fido/mac/touch_id_context.h7
-rw-r--r--chromium/device/fido/mac/touch_id_context.mm42
-rw-r--r--chromium/device/fido/mac/util.h25
-rw-r--r--chromium/device/fido/mac/util.mm67
-rw-r--r--chromium/device/fido/mac/util_unittest.cc54
-rw-r--r--chromium/device/fido/make_credential_handler_unittest.cc240
-rw-r--r--chromium/device/fido/make_credential_request_handler.cc75
-rw-r--r--chromium/device/fido/make_credential_request_handler.h7
-rw-r--r--chromium/device/fido/make_credential_task.cc117
-rw-r--r--chromium/device/fido/make_credential_task.h16
-rw-r--r--chromium/device/fido/make_credential_task_unittest.cc235
-rw-r--r--chromium/device/fido/mock_fido_ble_connection.h3
-rw-r--r--chromium/device/fido/mock_fido_device.cc89
-rw-r--r--chromium/device/fido/mock_fido_device.h23
-rw-r--r--chromium/device/fido/public_key_credential_params.cc41
-rw-r--r--chromium/device/fido/public_key_credential_params.h4
-rw-r--r--chromium/device/fido/public_key_credential_rp_entity.cc47
-rw-r--r--chromium/device/fido/public_key_credential_rp_entity.h3
-rw-r--r--chromium/device/fido/public_key_credential_user_entity.cc30
-rw-r--r--chromium/device/fido/response_data.cc3
-rw-r--r--chromium/device/fido/response_data.h5
-rw-r--r--chromium/device/fido/scoped_virtual_fido_device.cc30
-rw-r--r--chromium/device/fido/scoped_virtual_fido_device.h3
-rw-r--r--chromium/device/fido/u2f_command_constructor.cc35
-rw-r--r--chromium/device/fido/u2f_command_constructor.h13
-rw-r--r--chromium/device/fido/u2f_command_constructor_unittest.cc75
-rw-r--r--chromium/device/fido/u2f_register.cc174
-rw-r--r--chromium/device/fido/u2f_register.h85
-rw-r--r--chromium/device/fido/u2f_register_operation.cc150
-rw-r--r--chromium/device/fido/u2f_register_operation.h61
-rw-r--r--chromium/device/fido/u2f_register_operation_unittest.cc264
-rw-r--r--chromium/device/fido/u2f_register_unittest.cc506
-rw-r--r--chromium/device/fido/u2f_request.cc198
-rw-r--r--chromium/device/fido/u2f_request.h137
-rw-r--r--chromium/device/fido/u2f_request_unittest.cc364
-rw-r--r--chromium/device/fido/u2f_sign.cc155
-rw-r--r--chromium/device/fido/u2f_sign.h67
-rw-r--r--chromium/device/fido/u2f_sign_operation.cc190
-rw-r--r--chromium/device/fido/u2f_sign_operation.h70
-rw-r--r--chromium/device/fido/u2f_sign_operation_unittest.cc385
-rw-r--r--chromium/device/fido/u2f_sign_unittest.cc490
-rw-r--r--chromium/device/fido/virtual_ctap2_device.cc375
-rw-r--r--chromium/device/fido/virtual_ctap2_device.h64
-rw-r--r--chromium/device/fido/virtual_fido_device.cc34
-rw-r--r--chromium/device/fido/virtual_fido_device.h20
-rw-r--r--chromium/device/fido/virtual_u2f_device.cc57
-rw-r--r--chromium/device/gamepad/BUILD.gn1
-rw-r--r--chromium/device/gamepad/game_controller_data_fetcher_mac.mm3
-rw-r--r--chromium/device/gamepad/gamepad_data_fetcher.cc7
-rw-r--r--chromium/device/gamepad/gamepad_data_fetcher.h4
-rw-r--r--chromium/device/gamepad/gamepad_device_linux.cc178
-rw-r--r--chromium/device/gamepad/gamepad_device_linux.h42
-rw-r--r--chromium/device/gamepad/gamepad_device_mac.h6
-rw-r--r--chromium/device/gamepad/gamepad_device_mac.mm229
-rw-r--r--chromium/device/gamepad/gamepad_monitor.cc2
-rw-r--r--chromium/device/gamepad/gamepad_platform_data_fetcher_android.cc2
-rw-r--r--chromium/device/gamepad/gamepad_platform_data_fetcher_win.cc2
-rw-r--r--chromium/device/gamepad/gamepad_provider.cc21
-rw-r--r--chromium/device/gamepad/gamepad_provider.h13
-rw-r--r--chromium/device/gamepad/gamepad_provider_unittest.cc54
-rw-r--r--chromium/device/gamepad/gamepad_service.cc9
-rw-r--r--chromium/device/gamepad/gamepad_service.h7
-rw-r--r--chromium/device/gamepad/gamepad_shared_buffer.cc17
-rw-r--r--chromium/device/gamepad/gamepad_shared_buffer.h11
-rw-r--r--chromium/device/gamepad/gamepad_standard_mappings_linux.cc43
-rw-r--r--chromium/device/gamepad/gamepad_standard_mappings_mac.mm29
-rw-r--r--chromium/device/gamepad/gamepad_standard_mappings_win.cc41
-rw-r--r--chromium/device/gamepad/gamepad_user_gesture.cc9
-rw-r--r--chromium/device/gamepad/public/cpp/BUILD.gn33
-rw-r--r--chromium/device/gamepad/public/cpp/OWNERS4
-rw-r--r--chromium/device/gamepad/public/cpp/gamepad.h7
-rw-r--r--chromium/device/gamepad/public/cpp/gamepad.typemap (renamed from chromium/device/gamepad/public/mojom/gamepad.typemap)9
-rw-r--r--chromium/device/gamepad/public/cpp/gamepad_features.cc63
-rw-r--r--chromium/device/gamepad/public/cpp/gamepad_features.h19
-rw-r--r--chromium/device/gamepad/public/cpp/gamepad_mojom_traits.cc (renamed from chromium/device/gamepad/public/mojom/gamepad_mojom_traits.cc)2
-rw-r--r--chromium/device/gamepad/public/cpp/gamepad_mojom_traits.h (renamed from chromium/device/gamepad/public/mojom/gamepad_mojom_traits.h)43
-rw-r--r--chromium/device/gamepad/public/cpp/gamepad_switches.cc14
-rw-r--r--chromium/device/gamepad/public/cpp/gamepad_switches.h16
-rw-r--r--chromium/device/gamepad/public/cpp/typemaps.gni (renamed from chromium/device/gamepad/public/mojom/typemaps.gni)2
-rw-r--r--chromium/device/gamepad/public/mojom/BUILD.gn9
-rw-r--r--chromium/device/gamepad/public/mojom/OWNERS4
-rw-r--r--chromium/device/gamepad/public/mojom/gamepad.mojom9
-rw-r--r--chromium/device/gamepad/public/mojom/gamepad_hardware_buffer.h17
-rw-r--r--chromium/device/gamepad/public/mojom/gamepad_mojom_traits_unittest.cc3
-rw-r--r--chromium/device/gamepad/raw_input_gamepad_device_win.cc156
-rw-r--r--chromium/device/gamepad/raw_input_gamepad_device_win.h15
-rw-r--r--chromium/device/gamepad/switch_pro_controller_base.cc3
-rw-r--r--chromium/device/gamepad/switch_pro_controller_base.h1
-rw-r--r--chromium/device/gamepad/xbox_controller_mac.h1
-rw-r--r--chromium/device/gamepad/xbox_controller_mac.mm54
-rw-r--r--chromium/device/gamepad/xbox_data_fetcher_mac.cc6
-rw-r--r--chromium/device/geolocation/BUILD.gn212
-rw-r--r--chromium/device/geolocation/DEPS7
-rw-r--r--chromium/device/geolocation/OWNERS5
-rw-r--r--chromium/device/geolocation/empty_wifi_data_provider.cc26
-rw-r--r--chromium/device/geolocation/empty_wifi_data_provider.h32
-rw-r--r--chromium/device/geolocation/fake_location_provider.cc63
-rw-r--r--chromium/device/geolocation/fake_location_provider.h52
-rw-r--r--chromium/device/geolocation/geolocation_config.cc28
-rw-r--r--chromium/device/geolocation/geolocation_config.h34
-rw-r--r--chromium/device/geolocation/geolocation_context.cc57
-rw-r--r--chromium/device/geolocation/geolocation_context.h52
-rw-r--r--chromium/device/geolocation/geolocation_export.h28
-rw-r--r--chromium/device/geolocation/geolocation_impl.cc174
-rw-r--r--chromium/device/geolocation/geolocation_impl.h78
-rw-r--r--chromium/device/geolocation/geolocation_provider.h75
-rw-r--r--chromium/device/geolocation/geolocation_provider_impl.cc226
-rw-r--r--chromium/device/geolocation/geolocation_provider_impl.h132
-rw-r--r--chromium/device/geolocation/geolocation_provider_impl_unittest.cc261
-rw-r--r--chromium/device/geolocation/location_api_adapter_android.cc149
-rw-r--r--chromium/device/geolocation/location_api_adapter_android.h81
-rw-r--r--chromium/device/geolocation/location_arbitrator.cc218
-rw-r--r--chromium/device/geolocation/location_arbitrator.h141
-rw-r--r--chromium/device/geolocation/location_arbitrator_unittest.cc451
-rw-r--r--chromium/device/geolocation/location_provider_android.cc65
-rw-r--r--chromium/device/geolocation/location_provider_android.h44
-rw-r--r--chromium/device/geolocation/network_location_provider.cc294
-rw-r--r--chromium/device/geolocation/network_location_provider.h144
-rw-r--r--chromium/device/geolocation/network_location_provider_unittest.cc767
-rw-r--r--chromium/device/geolocation/network_location_request.cc434
-rw-r--r--chromium/device/geolocation/network_location_request.h85
-rw-r--r--chromium/device/geolocation/public/cpp/BUILD.gn29
-rw-r--r--chromium/device/geolocation/public/cpp/geoposition.cc15
-rw-r--r--chromium/device/geolocation/public/cpp/geoposition.h16
-rw-r--r--chromium/device/geolocation/public/cpp/location_provider.h52
-rw-r--r--chromium/device/geolocation/public/cpp/scoped_geolocation_overrider.cc205
-rw-r--r--chromium/device/geolocation/public/cpp/scoped_geolocation_overrider.h41
-rw-r--r--chromium/device/geolocation/wifi_data.cc55
-rw-r--r--chromium/device/geolocation/wifi_data.h54
-rw-r--r--chromium/device/geolocation/wifi_data_provider.cc53
-rw-r--r--chromium/device/geolocation/wifi_data_provider.h74
-rw-r--r--chromium/device/geolocation/wifi_data_provider_chromeos.cc184
-rw-r--r--chromium/device/geolocation/wifi_data_provider_chromeos.h67
-rw-r--r--chromium/device/geolocation/wifi_data_provider_chromeos_unittest.cc100
-rw-r--r--chromium/device/geolocation/wifi_data_provider_common.cc85
-rw-r--r--chromium/device/geolocation/wifi_data_provider_common.h83
-rw-r--r--chromium/device/geolocation/wifi_data_provider_common_unittest.cc213
-rw-r--r--chromium/device/geolocation/wifi_data_provider_common_win.cc56
-rw-r--r--chromium/device/geolocation/wifi_data_provider_common_win.h24
-rw-r--r--chromium/device/geolocation/wifi_data_provider_linux.cc373
-rw-r--r--chromium/device/geolocation/wifi_data_provider_linux.h42
-rw-r--r--chromium/device/geolocation/wifi_data_provider_linux_unittest.cc233
-rw-r--r--chromium/device/geolocation/wifi_data_provider_mac.h32
-rw-r--r--chromium/device/geolocation/wifi_data_provider_mac.mm137
-rw-r--r--chromium/device/geolocation/wifi_data_provider_manager.cc98
-rw-r--r--chromium/device/geolocation/wifi_data_provider_manager.h97
-rw-r--r--chromium/device/geolocation/wifi_data_provider_win.cc256
-rw-r--r--chromium/device/geolocation/wifi_data_provider_win.h31
-rw-r--r--chromium/device/geolocation/wifi_data_provider_win_unittest.cc24
-rw-r--r--chromium/device/geolocation/wifi_polling_policy.cc37
-rw-r--r--chromium/device/geolocation/wifi_polling_policy.h137
-rw-r--r--chromium/device/geolocation/wifi_polling_policy_unittest.cc145
-rw-r--r--chromium/device/vr/BUILD.gn7
-rw-r--r--chromium/device/vr/OWNERS3
-rw-r--r--chromium/device/vr/PRESUBMIT.py24
-rw-r--r--chromium/device/vr/android/gvr/cardboard_gamepad_data_fetcher.cc2
-rw-r--r--chromium/device/vr/android/gvr/gvr_delegate_provider.h9
-rw-r--r--chromium/device/vr/android/gvr/gvr_device.cc97
-rw-r--r--chromium/device/vr/android/gvr/gvr_device.h31
-rw-r--r--chromium/device/vr/android/gvr/gvr_device_provider.cc9
-rw-r--r--chromium/device/vr/android/gvr/gvr_device_provider.h9
-rw-r--r--chromium/device/vr/android/gvr/gvr_gamepad_data_fetcher.cc2
-rw-r--r--chromium/device/vr/buildflags/buildflags.gni15
-rw-r--r--chromium/device/vr/oculus/OWNERS3
-rw-r--r--chromium/device/vr/oculus/oculus_device.cc158
-rw-r--r--chromium/device/vr/oculus/oculus_device.h54
-rw-r--r--chromium/device/vr/oculus/oculus_device_provider.cc34
-rw-r--r--chromium/device/vr/oculus/oculus_device_provider.h7
-rw-r--r--chromium/device/vr/oculus/oculus_gamepad_data_fetcher.cc50
-rw-r--r--chromium/device/vr/oculus/oculus_gamepad_data_fetcher.h35
-rw-r--r--chromium/device/vr/oculus/oculus_render_loop.cc199
-rw-r--r--chromium/device/vr/oculus/oculus_render_loop.h43
-rw-r--r--chromium/device/vr/openvr/OWNERS3
-rw-r--r--chromium/device/vr/openvr/openvr_api_wrapper.cc113
-rw-r--r--chromium/device/vr/openvr/openvr_api_wrapper.h52
-rw-r--r--chromium/device/vr/openvr/openvr_device.cc169
-rw-r--r--chromium/device/vr/openvr/openvr_device.h52
-rw-r--r--chromium/device/vr/openvr/openvr_device_provider.cc37
-rw-r--r--chromium/device/vr/openvr/openvr_device_provider.h12
-rw-r--r--chromium/device/vr/openvr/openvr_gamepad_data_fetcher.cc50
-rw-r--r--chromium/device/vr/openvr/openvr_gamepad_data_fetcher.h41
-rw-r--r--chromium/device/vr/openvr/openvr_render_loop.cc238
-rw-r--r--chromium/device/vr/openvr/openvr_render_loop.h36
-rw-r--r--chromium/device/vr/openvr/openvr_type_converters.cc17
-rw-r--r--chromium/device/vr/orientation/orientation_device.cc26
-rw-r--r--chromium/device/vr/orientation/orientation_device.h11
-rw-r--r--chromium/device/vr/orientation/orientation_device_provider.cc12
-rw-r--r--chromium/device/vr/orientation/orientation_device_provider.h10
-rw-r--r--chromium/device/vr/orientation/orientation_device_provider_unittest.cc80
-rw-r--r--chromium/device/vr/orientation/orientation_device_unittest.cc8
-rw-r--r--chromium/device/vr/public/mojom/BUILD.gn1
-rw-r--r--chromium/device/vr/public/mojom/OWNERS3
-rw-r--r--chromium/device/vr/public/mojom/README.md57
-rw-r--r--chromium/device/vr/public/mojom/isolated_xr_service.mojom83
-rw-r--r--chromium/device/vr/public/mojom/vr_service.mojom159
-rw-r--r--chromium/device/vr/vr_device.h57
-rw-r--r--chromium/device/vr/vr_device_base.cc124
-rw-r--r--chromium/device/vr/vr_device_base.h89
-rw-r--r--chromium/device/vr/vr_device_base_unittest.cc57
-rw-r--r--chromium/device/vr/vr_device_provider.h9
-rw-r--r--chromium/device/vr/vr_display_impl.cc94
-rw-r--r--chromium/device/vr/vr_display_impl.h59
-rw-r--r--chromium/device/vr/vr_display_impl_unittest.cc100
-rw-r--r--chromium/device/vr/windows/d3d11_texture_helper.cc7
-rw-r--r--chromium/device/vr/windows/d3d11_texture_helper.h2
408 files changed, 14348 insertions, 13397 deletions
diff --git a/chromium/device/BUILD.gn b/chromium/device/BUILD.gn
index e0df5c725c0..17eb38aef83 100644
--- a/chromium/device/BUILD.gn
+++ b/chromium/device/BUILD.gn
@@ -30,6 +30,7 @@ test("device_unittests") {
"bluetooth/bluetooth_local_gatt_characteristic_unittest.cc",
"bluetooth/bluetooth_local_gatt_descriptor_unittest.cc",
"bluetooth/bluetooth_local_gatt_service_unittest.cc",
+ "bluetooth/bluetooth_low_energy_advertisement_manager_mac_unittest.mm",
"bluetooth/bluetooth_low_energy_win_unittest.cc",
"bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc",
"bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc",
@@ -64,6 +65,7 @@ test("device_unittests") {
"bluetooth/test/test_bluetooth_local_gatt_service_delegate.cc",
"bluetooth/test/test_bluetooth_local_gatt_service_delegate.h",
"bluetooth/uribeacon/uri_encoder_unittest.cc",
+ "fido/attestation_statement_formats_unittest.cc",
"fido/ctap_request_unittest.cc",
"fido/ctap_response_unittest.cc",
"fido/fake_fido_discovery_unittest.cc",
@@ -72,21 +74,24 @@ test("device_unittests") {
"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/mac/browsing_data_deletion_unittest.mm",
+ "fido/mac/credential_metadata_unittest.cc",
"fido/mac/get_assertion_operation_unittest_mac.mm",
"fido/mac/make_credential_operation_unittest_mac.mm",
+ "fido/mac/util_unittest.cc",
"fido/make_credential_handler_unittest.cc",
"fido/make_credential_task_unittest.cc",
"fido/test_callback_receiver_unittest.cc",
"fido/u2f_command_constructor_unittest.cc",
- "fido/u2f_register_unittest.cc",
- "fido/u2f_request_unittest.cc",
- "fido/u2f_sign_unittest.cc",
+ "fido/u2f_register_operation_unittest.cc",
+ "fido/u2f_sign_operation_unittest.cc",
"gamepad/abstract_haptic_gamepad_unittest.cc",
"gamepad/gamepad_provider_unittest.cc",
"gamepad/gamepad_service_unittest.cc",
@@ -112,8 +117,7 @@ test("device_unittests") {
"//device/gamepad/public/cpp:shared_with_blink",
"//device/gamepad/public/mojom",
"//device/gamepad/public/mojom:gamepad_mojom_traits_test",
- "//device/geolocation:unittests",
- "//mojo/edk",
+ "//mojo/core/embedder",
"//mojo/public/cpp/bindings",
"//net",
"//testing/gmock",
@@ -196,8 +200,6 @@ test("device_unittests") {
deps += [
":bluetooth_test_java",
":bluetooth_test_jni_headers",
- "//device/geolocation:geolocation_java",
- "//device/geolocation:geolocation_java_test_support",
"//device/usb:java",
]
}
@@ -285,8 +287,26 @@ 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_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",
+ "bluetooth/test/fake_bluetooth_le_advertisement_watcher_winrt.h",
+ "bluetooth/test/fake_bluetooth_le_advertisement_winrt.cc",
+ "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_device_information_winrt.cc",
"bluetooth/test/fake_device_information_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_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_radio_winrt.cc",
+ "bluetooth/test/fake_radio_winrt.h",
]
}
@@ -348,18 +368,14 @@ if (is_android) {
"//components/location/android:location_java",
"//device/bluetooth:java",
"//third_party/android_tools:android_support_annotations_java",
+ "//third_party/android_tools:android_test_mock_java",
]
- deps += android_extra_test_deps
-
srcjar_deps = [ ":bluetooth_test_javagen" ]
}
junit_binary("device_junit_tests") {
- java_files = [
- "gamepad/android/junit/src/org/chromium/device/gamepad/GamepadMappingsTest.java",
- "geolocation/android/junit/src/org/chromium/device/geolocation/LocationProviderTest.java",
- ]
+ java_files = [ "gamepad/android/junit/src/org/chromium/device/gamepad/GamepadMappingsTest.java" ]
deps = [
"$google_play_services_package:google_play_services_base_java",
"$google_play_services_package:google_play_services_basement_java",
@@ -368,8 +384,6 @@ if (is_android) {
"//base:base_java_test_support",
"//base:base_junit_test_support",
"//device/gamepad:java",
- "//device/geolocation:geolocation_java",
- "//device/geolocation:geolocation_java_test_support",
"//mojo/public/java:bindings_java",
"//third_party/android_tools:android_support_annotations_java",
]
diff --git a/chromium/device/base/features.cc b/chromium/device/base/features.cc
index b38d28dbae4..eaf9cd93ffb 100644
--- a/chromium/device/base/features.cc
+++ b/chromium/device/base/features.cc
@@ -15,10 +15,25 @@ const base::Feature kNewBLEWinImplementation{"NewBLEWinImplementation",
base::FEATURE_DISABLED_BY_DEFAULT};
#endif // defined(OS_WIN)
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if defined(OS_CHROMEOS)
// Enables or disables the use of newblue Bluetooth daemon on Chrome OS.
const base::Feature kNewblueDaemon{"Newblue",
base::FEATURE_DISABLED_BY_DEFAULT};
-#endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
+// Shows all Bluetooth devices in UI (System Tray/Settings Page).
+// Needed for working on the early integration with NewBlue.
+// TODO(crbug.com/862492): Remove this feature once NewBlue gets stable.
+const base::Feature kUnfilteredBluetoothDevices{
+ "UnfilteredBluetoothDevices", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif // defined(OS_CHROMEOS)
+
+#if defined(OS_MACOSX)
+// Controls whether the CTAP2 implementation should use a built-in platform
+// authenticator, where available.
+const base::Feature kWebAuthTouchId{"WebAuthenticationTouchId",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+#endif // defined(OS_MACOSX)
+
+const base::Feature kNewCtap2Device{"WebAuthenticationCtap2",
+ base::FEATURE_ENABLED_BY_DEFAULT};
} // namespace device
diff --git a/chromium/device/base/features.h b/chromium/device/base/features.h
index c25697a021e..565df3ae1bc 100644
--- a/chromium/device/base/features.h
+++ b/chromium/device/base/features.h
@@ -16,9 +16,16 @@ DEVICE_BASE_EXPORT extern const base::Feature kNewUsbBackend;
DEVICE_BASE_EXPORT extern const base::Feature kNewBLEWinImplementation;
#endif // defined(OS_WIN)
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if defined(OS_CHROMEOS)
DEVICE_BASE_EXPORT extern const base::Feature kNewblueDaemon;
-#endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
+DEVICE_BASE_EXPORT extern const base::Feature kUnfilteredBluetoothDevices;
+#endif // defined(OS_CHROMEOS)
+
+DEVICE_BASE_EXPORT extern const base::Feature kNewCtap2Device;
+
+#if defined(OS_MACOSX)
+DEVICE_BASE_EXPORT extern const base::Feature kWebAuthTouchId;
+#endif // defined(OS_MACOSX)
} // namespace device
diff --git a/chromium/device/bluetooth/BUILD.gn b/chromium/device/bluetooth/BUILD.gn
index 56b36d78949..b18288d0070 100644
--- a/chromium/device/bluetooth/BUILD.gn
+++ b/chromium/device/bluetooth/BUILD.gn
@@ -103,6 +103,8 @@ component("bluetooth") {
"bluetooth_adapter_win.h",
"bluetooth_advertisement.cc",
"bluetooth_advertisement.h",
+ "bluetooth_advertisement_mac.h",
+ "bluetooth_advertisement_mac.mm",
"bluetooth_channel_mac.h",
"bluetooth_channel_mac.mm",
"bluetooth_classic_device_mac.h",
@@ -146,6 +148,8 @@ component("bluetooth") {
"bluetooth_local_gatt_descriptor.h",
"bluetooth_local_gatt_service.cc",
"bluetooth_local_gatt_service.h",
+ "bluetooth_low_energy_advertisement_manager_mac.h",
+ "bluetooth_low_energy_advertisement_manager_mac.mm",
"bluetooth_low_energy_central_manager_delegate.h",
"bluetooth_low_energy_central_manager_delegate.mm",
"bluetooth_low_energy_defs_win.cc",
@@ -156,6 +160,8 @@ component("bluetooth") {
"bluetooth_low_energy_discovery_manager_mac.mm",
"bluetooth_low_energy_peripheral_delegate.h",
"bluetooth_low_energy_peripheral_delegate.mm",
+ "bluetooth_low_energy_peripheral_manager_delegate.h",
+ "bluetooth_low_energy_peripheral_manager_delegate.mm",
"bluetooth_low_energy_win.cc",
"bluetooth_low_energy_win.h",
"bluetooth_remote_gatt_characteristic.cc",
@@ -242,6 +248,13 @@ component("bluetooth") {
"bluetooth_adapter_winrt.h",
"bluetooth_device_winrt.cc",
"bluetooth_device_winrt.h",
+ "bluetooth_gatt_discoverer_winrt.cc",
+ "bluetooth_gatt_discoverer_winrt.h",
+ "bluetooth_remote_gatt_characteristic_winrt.cc",
+ "bluetooth_remote_gatt_characteristic_winrt.h",
+ "bluetooth_remote_gatt_service_winrt.cc",
+ "bluetooth_remote_gatt_service_winrt.h",
+ "event_utils_winrt.h",
]
libs = [
diff --git a/chromium/device/bluetooth/bluetooth_adapter.h b/chromium/device/bluetooth/bluetooth_adapter.h
index 389dae23bf2..b1399a64300 100644
--- a/chromium/device/bluetooth/bluetooth_adapter.h
+++ b/chromium/device/bluetooth/bluetooth_adapter.h
@@ -358,6 +358,10 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapter
//
// Platforms that natively support a callback based API (e.g. BlueZ and Win)
// should override this method and provide their own implementation instead.
+ //
+ // Due to an issue with non-native APIs on Windows 10, both IsPowered() and
+ // SetPowered() don't work correctly when run from a x86 Chrome on a x64 CPU.
+ // See https://github.com/Microsoft/cppwinrt/issues/47 for more details.
virtual void SetPowered(bool powered,
const base::Closure& callback,
const ErrorCallback& error_callback);
diff --git a/chromium/device/bluetooth/bluetooth_adapter_android.cc b/chromium/device/bluetooth/bluetooth_adapter_android.cc
index 3d2b667cd5f..ad1446ad689 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_android.cc
+++ b/chromium/device/bluetooth/bluetooth_adapter_android.cc
@@ -234,11 +234,13 @@ void BluetoothAdapterAndroid::CreateOrUpdateDeviceOnScan(
int8_t clamped_tx_power = BluetoothDevice::ClampPower(tx_power);
device_android->UpdateAdvertisementData(
- BluetoothDevice::ClampPower(rssi), std::move(advertised_bluetooth_uuids),
- service_data_map, manufacturer_data_map,
+ BluetoothDevice::ClampPower(rssi), base::nullopt /* flags */,
+ std::move(advertised_bluetooth_uuids),
// Android uses INT32_MIN to indicate no Advertised Tx Power.
// https://developer.android.com/reference/android/bluetooth/le/ScanRecord.html#getTxPowerLevel()
- tx_power == INT32_MIN ? nullptr : &clamped_tx_power);
+ tx_power == INT32_MIN ? base::nullopt
+ : base::make_optional(clamped_tx_power),
+ service_data_map, manufacturer_data_map);
if (is_new_device) {
devices_[device_address] = std::move(device_android_owner);
diff --git a/chromium/device/bluetooth/bluetooth_adapter_mac.h b/chromium/device/bluetooth/bluetooth_adapter_mac.h
index a31b33b1b54..3c13e213006 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_mac.h
+++ b/chromium/device/bluetooth/bluetooth_adapter_mac.h
@@ -21,6 +21,7 @@
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_discovery_manager_mac.h"
#include "device/bluetooth/bluetooth_export.h"
+#include "device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.h"
#include "device/bluetooth/bluetooth_low_energy_device_mac.h"
#include "device/bluetooth/bluetooth_low_energy_discovery_manager_mac.h"
#include "device/bluetooth/bluetooth_uuid.h"
@@ -30,7 +31,9 @@
@class NSArray;
@class NSDate;
+@class BluetoothAdvertisementMac;
@class BluetoothLowEnergyCentralManagerDelegate;
+@class BluetoothLowEnergyPeripheralManagerDelegate;
namespace device {
@@ -151,6 +154,9 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterMac
// Returns the CBCentralManager instance.
CBCentralManager* GetCentralManager();
+ // Returns the CBPeripheralManager instance.
+ CBPeripheralManager* GetPeripheralManager();
+
// Allow the mocking out of getting the HostController state for testing.
void SetHostControllerStateFunctionForTesting(
HostControllerStateFunction controller_state_function);
@@ -167,6 +173,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterMac
friend class BluetoothTestMac;
friend class BluetoothAdapterMacTest;
friend class BluetoothLowEnergyCentralManagerBridge;
+ friend class BluetoothLowEnergyPeripheralManagerBridge;
BluetoothAdapterMac();
~BluetoothAdapterMac() override;
@@ -209,6 +216,10 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterMac
// Updates |devices_| when there is a change to the CBCentralManager's state.
void LowEnergyCentralManagerUpdatedState();
+ // Updates |advertisements_| when there is a change to the
+ // CBPeripheralManager's state.
+ void LowEnergyPeripheralManagerUpdatedState();
+
// Updates |devices_| to include the currently paired devices and notifies
// observers.
void AddPairedDevices();
@@ -259,11 +270,20 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterMac
std::unique_ptr<BluetoothLowEnergyDiscoveryManagerMac>
low_energy_discovery_manager_;
+ // Advertisement manager for Bluetooth Low Energy.
+ std::unique_ptr<BluetoothLowEnergyAdvertisementManagerMac>
+ low_energy_advertisement_manager_;
+
// Underlying CoreBluetooth CBCentralManager and its delegate.
base::scoped_nsobject<CBCentralManager> low_energy_central_manager_;
base::scoped_nsobject<BluetoothLowEnergyCentralManagerDelegate>
low_energy_central_manager_delegate_;
+ // Underlying CoreBluetooth CBPeripheralManager and its delegate.
+ base::scoped_nsobject<CBPeripheralManager> low_energy_peripheral_manager_;
+ base::scoped_nsobject<BluetoothLowEnergyPeripheralManagerDelegate>
+ low_energy_peripheral_manager_delegate_;
+
base::WeakPtrFactory<BluetoothAdapterMac> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(BluetoothAdapterMac);
diff --git a/chromium/device/bluetooth/bluetooth_adapter_mac.mm b/chromium/device/bluetooth/bluetooth_adapter_mac.mm
index bb996bfd087..8b2092ec78f 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_mac.mm
+++ b/chromium/device/bluetooth/bluetooth_adapter_mac.mm
@@ -25,11 +25,13 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "device/bluetooth/bluetooth_adapter_mac_metrics.h"
+#include "device/bluetooth/bluetooth_advertisement_mac.h"
#include "device/bluetooth/bluetooth_classic_device_mac.h"
#include "device/bluetooth/bluetooth_common.h"
#include "device/bluetooth/bluetooth_discovery_session.h"
#include "device/bluetooth/bluetooth_discovery_session_outcome.h"
#include "device/bluetooth/bluetooth_low_energy_central_manager_delegate.h"
+#include "device/bluetooth/bluetooth_low_energy_peripheral_manager_delegate.h"
#include "device/bluetooth/bluetooth_socket_mac.h"
extern "C" {
@@ -136,6 +138,16 @@ BluetoothAdapterMac::BluetoothAdapterMac()
queue:dispatch_get_main_queue()]);
low_energy_discovery_manager_->SetCentralManager(
low_energy_central_manager_);
+
+ low_energy_advertisement_manager_.reset(
+ new BluetoothLowEnergyAdvertisementManagerMac());
+ low_energy_peripheral_manager_delegate_.reset(
+ [[BluetoothLowEnergyPeripheralManagerDelegate alloc]
+ initWithAdvertisementManager:low_energy_advertisement_manager_.get()
+ andAdapter:this]);
+ low_energy_peripheral_manager_.reset([[CBPeripheralManager alloc]
+ initWithDelegate:low_energy_peripheral_manager_delegate_
+ queue:dispatch_get_main_queue()]);
}
DCHECK(classic_discovery_manager_);
}
@@ -265,8 +277,8 @@ void BluetoothAdapterMac::RegisterAdvertisement(
std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data,
const CreateAdvertisementCallback& callback,
const AdvertisementErrorCallback& error_callback) {
- NOTIMPLEMENTED();
- error_callback.Run(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM);
+ low_energy_advertisement_manager_->RegisterAdvertisement(
+ std::move(advertisement_data), callback, error_callback);
}
BluetoothLocalGattService* BluetoothAdapterMac::GetGattService(
@@ -337,6 +349,10 @@ CBCentralManager* BluetoothAdapterMac::GetCentralManager() {
return low_energy_central_manager_;
}
+CBPeripheralManager* BluetoothAdapterMac::GetPeripheralManager() {
+ return low_energy_peripheral_manager_;
+}
+
void BluetoothAdapterMac::SetHostControllerStateFunctionForTesting(
HostControllerStateFunction controller_state_function) {
controller_state_function_ = std::move(controller_state_function);
@@ -474,6 +490,8 @@ bool BluetoothAdapterMac::StartDiscovery(
void BluetoothAdapterMac::Init() {
ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
+ low_energy_advertisement_manager_->Init(ui_task_runner_,
+ low_energy_peripheral_manager_);
PollAdapter();
}
@@ -621,9 +639,10 @@ void BluetoothAdapterMac::LowEnergyDeviceUpdated(
int8_t clamped_tx_power = BluetoothDevice::ClampPower([tx_power intValue]);
device_mac->UpdateAdvertisementData(
- BluetoothDevice::ClampPower(rssi), std::move(advertised_uuids),
- std::move(service_data_map), std::move(manufacturer_data_map),
- tx_power == nil ? nullptr : &clamped_tx_power);
+ BluetoothDevice::ClampPower(rssi), base::nullopt /* flags */,
+ std::move(advertised_uuids),
+ tx_power == nil ? base::nullopt : base::make_optional(clamped_tx_power),
+ std::move(service_data_map), std::move(manufacturer_data_map));
if (is_new_device) {
std::string device_address =
diff --git a/chromium/device/bluetooth/bluetooth_adapter_unittest.cc b/chromium/device/bluetooth/bluetooth_adapter_unittest.cc
index 292c6734ba7..fe16beb92ea 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_unittest.cc
+++ b/chromium/device/bluetooth/bluetooth_adapter_unittest.cc
@@ -32,6 +32,7 @@
#elif defined(OS_MACOSX)
#include "device/bluetooth/test/bluetooth_test_mac.h"
#elif defined(OS_WIN)
+#include "base/win/windows_version.h"
#include "device/bluetooth/test/bluetooth_test_win.h"
#elif defined(USE_CAST_BLUETOOTH_ADAPTER)
#include "device/bluetooth/test/bluetooth_test_cast.h"
@@ -624,13 +625,17 @@ TEST_F(BluetoothTest, NoLocationServices) {
}
#endif // defined(OS_ANDROID)
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_DiscoverLowEnergyDevice DiscoverLowEnergyDevice
#else
#define MAYBE_DiscoverLowEnergyDevice DISABLED_DiscoverLowEnergyDevice
#endif
// Discovers a device.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrt, DiscoverLowEnergyDevice) {
+#else
TEST_F(BluetoothTest, MAYBE_DiscoverLowEnergyDevice) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -646,13 +651,17 @@ TEST_F(BluetoothTest, MAYBE_DiscoverLowEnergyDevice) {
EXPECT_TRUE(device);
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_DiscoverLowEnergyDeviceTwice DiscoverLowEnergyDeviceTwice
#else
#define MAYBE_DiscoverLowEnergyDeviceTwice DISABLED_DiscoverLowEnergyDeviceTwice
#endif
// Discovers the same device multiple times.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrt, DiscoverLowEnergyDeviceTwice) {
+#else
TEST_F(BluetoothTest, MAYBE_DiscoverLowEnergyDeviceTwice) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -685,7 +694,11 @@ TEST_F(BluetoothTest, MAYBE_DiscoverLowEnergyDeviceTwice) {
// Discovers a device, and then again with new Service UUIDs.
// Makes sure we don't create another device when we've found the
// device in the past.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrt, DiscoverLowEnergyDeviceWithUpdatedUUIDs) {
+#else
TEST_F(BluetoothTest, MAYBE_DiscoverLowEnergyDeviceWithUpdatedUUIDs) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -713,14 +726,18 @@ TEST_F(BluetoothTest, MAYBE_DiscoverLowEnergyDeviceWithUpdatedUUIDs) {
EXPECT_EQ(1u, adapter_->GetDevices().size());
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_DiscoverMultipleLowEnergyDevices DiscoverMultipleLowEnergyDevices
#else
#define MAYBE_DiscoverMultipleLowEnergyDevices \
DISABLED_DiscoverMultipleLowEnergyDevices
#endif
// Discovers multiple devices when addresses vary.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrt, DiscoverMultipleLowEnergyDevices) {
+#else
TEST_F(BluetoothTest, MAYBE_DiscoverMultipleLowEnergyDevices) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -736,19 +753,17 @@ TEST_F(BluetoothTest, MAYBE_DiscoverMultipleLowEnergyDevices) {
EXPECT_EQ(2u, adapter_->GetDevices().size());
}
-// TODO(https://crbug.com/804356): Enable this test on Windows as well.
+// TODO(https://crbug.com/804356): Enable this test on old Windows versions as
+// well.
#if defined(OS_WIN)
-#define MAYBE_TogglePowerFakeAdapter DISABLED_TogglePowerFakeAdapter
+TEST_P(BluetoothTestWinrtOnly, TogglePowerFakeAdapter) {
#else
-#define MAYBE_TogglePowerFakeAdapter TogglePowerFakeAdapter
+TEST_F(BluetoothTest, TogglePowerFakeAdapter) {
#endif
-TEST_F(BluetoothTest, MAYBE_TogglePowerFakeAdapter) {
-#if defined(OS_MACOSX)
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
-#endif // defined(OS_MACOSX)
InitWithFakeAdapter();
TestBluetoothAdapterObserver observer(adapter_);
@@ -777,10 +792,15 @@ TEST_F(BluetoothTest, MAYBE_TogglePowerFakeAdapter) {
#else
#define MAYBE_TogglePowerFakeAdapter_Twice DISABLED_TogglePowerFakeAdapter_Twice
#endif
-// These tests are not relevant for BlueZ and Windows. On these platforms the
-// corresponding system APIs are blocking or use callbacks, so that it is not
-// necessary to store pending callbacks and wait for the appropriate events.
+// These tests are not relevant for BlueZ and old Windows versions. On these
+// platforms the corresponding system APIs are blocking or use callbacks, so
+// that it is not necessary to store pending callbacks and wait for the
+// appropriate events.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, TogglePowerFakeAdapter_Twice) {
+#else
TEST_F(BluetoothTest, MAYBE_TogglePowerFakeAdapter_Twice) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -821,7 +841,12 @@ TEST_F(BluetoothTest, MAYBE_TogglePowerFakeAdapter_Twice) {
#define MAYBE_TogglePowerFakeAdapter_WithinCallback_On_Off \
DISABLED_TogglePowerFakeAdapter_WithinCallback_On_Off
#endif
+
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, TogglePowerFakeAdapter_WithinCallback_On_Off) {
+#else
TEST_F(BluetoothTest, MAYBE_TogglePowerFakeAdapter_WithinCallback_On_Off) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -853,7 +878,12 @@ TEST_F(BluetoothTest, MAYBE_TogglePowerFakeAdapter_WithinCallback_On_Off) {
#define MAYBE_TogglePowerFakeAdapter_WithinCallback_Off_On \
DISABLED_TogglePowerFakeAdapter_WithinCallback_Off_On
#endif
+
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, TogglePowerFakeAdapter_WithinCallback_Off_On) {
+#else
TEST_F(BluetoothTest, MAYBE_TogglePowerFakeAdapter_WithinCallback_Off_On) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -892,7 +922,12 @@ TEST_F(BluetoothTest, MAYBE_TogglePowerFakeAdapter_WithinCallback_Off_On) {
#define MAYBE_TogglePowerFakeAdapter_DestroyWithPending \
DISABLED_TogglePowerFakeAdapter_DestroyWithPending
#endif
+
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, TogglePowerFakeAdapter_DestroyWithPending) {
+#else
TEST_F(BluetoothTest, MAYBE_TogglePowerFakeAdapter_DestroyWithPending) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1345,6 +1380,11 @@ INSTANTIATE_TEST_CASE_P(
/* no prefix */,
BluetoothTestWinrt,
::testing::Bool());
+
+INSTANTIATE_TEST_CASE_P(
+ /* no prefix */,
+ BluetoothTestWinrtOnly,
+ ::testing::Values(true));
#endif // defined(OS_WIN)
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_adapter_win.cc b/chromium/device/bluetooth/bluetooth_adapter_win.cc
index 32e7fc89f50..213a76c28b3 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_win.cc
+++ b/chromium/device/bluetooth/bluetooth_adapter_win.cc
@@ -19,6 +19,7 @@
#include "base/win/windows_version.h"
#include "device/base/features.h"
#include "device/bluetooth/bluetooth_adapter_winrt.h"
+#include "device/bluetooth/bluetooth_classic_win.h"
#include "device/bluetooth/bluetooth_device_win.h"
#include "device/bluetooth/bluetooth_discovery_session_outcome.h"
#include "device/bluetooth/bluetooth_socket_thread.h"
@@ -354,19 +355,21 @@ void BluetoothAdapterWin::Init() {
ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
socket_thread_ = BluetoothSocketThread::Get();
task_manager_ =
- new BluetoothTaskManagerWin(ui_task_runner_);
+ base::MakeRefCounted<BluetoothTaskManagerWin>(ui_task_runner_);
task_manager_->AddObserver(this);
task_manager_->Initialize();
}
void BluetoothAdapterWin::InitForTest(
+ std::unique_ptr<win::BluetoothClassicWrapper> classic_wrapper,
+ std::unique_ptr<win::BluetoothLowEnergyWrapper> le_wrapper,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
scoped_refptr<base::SequencedTaskRunner> bluetooth_task_runner) {
ui_task_runner_ = ui_task_runner;
if (!ui_task_runner_)
ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
- task_manager_ =
- new BluetoothTaskManagerWin(ui_task_runner_);
+ task_manager_ = BluetoothTaskManagerWin::CreateForTesting(
+ std::move(classic_wrapper), std::move(le_wrapper), ui_task_runner_);
task_manager_->AddObserver(this);
task_manager_->InitializeWithBluetoothTaskRunner(bluetooth_task_runner);
}
diff --git a/chromium/device/bluetooth/bluetooth_adapter_win.h b/chromium/device/bluetooth/bluetooth_adapter_win.h
index 88ea310b7f0..dd996e8878e 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_win.h
+++ b/chromium/device/bluetooth/bluetooth_adapter_win.h
@@ -129,6 +129,8 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterWin
void Init();
void InitForTest(
+ std::unique_ptr<win::BluetoothClassicWrapper> classic_wrapper,
+ std::unique_ptr<win::BluetoothLowEnergyWrapper> le_wrapper,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
scoped_refptr<base::SequencedTaskRunner> bluetooth_task_runner);
diff --git a/chromium/device/bluetooth/bluetooth_adapter_win_unittest.cc b/chromium/device/bluetooth/bluetooth_adapter_win_unittest.cc
index 7a991c94481..881611a5785 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_win_unittest.cc
+++ b/chromium/device/bluetooth/bluetooth_adapter_win_unittest.cc
@@ -13,6 +13,7 @@
#include "base/test/test_simple_task_runner.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_adapter_win.h"
+#include "device/bluetooth/bluetooth_classic_win.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_discovery_session_outcome.h"
#include "device/bluetooth/bluetooth_task_manager_win.h"
@@ -57,7 +58,8 @@ class BluetoothAdapterWinTest : public testing::Test {
adapter_win_(static_cast<BluetoothAdapterWin*>(adapter_.get())),
observer_(adapter_),
init_callback_called_(false) {
- adapter_win_->InitForTest(ui_task_runner_, bluetooth_task_runner_);
+ adapter_win_->InitForTest(nullptr, nullptr, ui_task_runner_,
+ bluetooth_task_runner_);
}
void SetUp() override {
diff --git a/chromium/device/bluetooth/bluetooth_adapter_winrt.cc b/chromium/device/bluetooth/bluetooth_adapter_winrt.cc
index 94cde0ca5a6..7660b563075 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_winrt.cc
+++ b/chromium/device/bluetooth/bluetooth_adapter_winrt.cc
@@ -4,6 +4,7 @@
#include "device/bluetooth/bluetooth_adapter_winrt.h"
+#include <windows.foundation.collections.h>
#include <windows.foundation.h>
#include <wrl/event.h>
@@ -13,14 +14,19 @@
#include "base/bind.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/single_thread_task_runner.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_device_winrt.h"
#include "device/bluetooth/bluetooth_discovery_filter.h"
+#include "device/bluetooth/bluetooth_discovery_session_outcome.h"
+#include "device/bluetooth/event_utils_winrt.h"
namespace device {
@@ -31,14 +37,37 @@ namespace {
namespace uwp {
using ABI::Windows::Devices::Bluetooth::BluetoothAdapter;
} // namespace uwp
+using ABI::Windows::Devices::Bluetooth::Advertisement::
+ BluetoothLEAdvertisementWatcherStatus;
+using ABI::Windows::Devices::Bluetooth::Advertisement::
+ BluetoothLEAdvertisementWatcherStatus_Aborted;
+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::
+ IBluetoothLEAdvertisementReceivedEventArgs;
+using ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEAdvertisementWatcher;
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::Radios::IRadio;
+using ABI::Windows::Devices::Radios::IRadioStatics;
+using ABI::Windows::Devices::Radios::Radio;
+using ABI::Windows::Devices::Radios::RadioAccessStatus;
+using ABI::Windows::Devices::Radios::RadioAccessStatus_Allowed;
+using ABI::Windows::Devices::Radios::RadioAccessStatus_DeniedBySystem;
+using ABI::Windows::Devices::Radios::RadioAccessStatus_DeniedByUser;
+using ABI::Windows::Devices::Radios::RadioAccessStatus_Unspecified;
+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::IAsyncOperation;
-using ABI::Windows::Foundation::IAsyncOperationCompletedHandler;
-using Microsoft::WRL::Callback;
using Microsoft::WRL::ComPtr;
bool ResolveCoreWinRT() {
@@ -47,86 +76,109 @@ bool ResolveCoreWinRT() {
}
// Utility functions to pretty print enum values.
-constexpr const char* ToCString(AsyncStatus async_status) {
- switch (async_status) {
- case AsyncStatus::Started:
- return "AsyncStatus::Started";
- case AsyncStatus::Completed:
- return "AsyncStatus::Completed";
- case AsyncStatus::Canceled:
- return "AsyncStatus::Canceled";
- case AsyncStatus::Error:
- return "AsyncStatus::Error";
+constexpr const char* ToCString(RadioAccessStatus access_status) {
+ switch (access_status) {
+ case RadioAccessStatus_Unspecified:
+ return "RadioAccessStatus::Unspecified";
+ case RadioAccessStatus_Allowed:
+ return "RadioAccessStatus::Allowed";
+ case RadioAccessStatus_DeniedByUser:
+ return "RadioAccessStatus::DeniedByUser";
+ case RadioAccessStatus_DeniedBySystem:
+ return "RadioAccessStatus::DeniedBySystem";
}
NOTREACHED();
return "";
}
-template <typename T>
-using AsyncAbiT = typename ABI::Windows::Foundation::Internal::GetAbiType<
- typename IAsyncOperation<T>::TResult_complex>::type;
-
-// Compile time switch to decide what container to use for the async results for
-// |T|. Depends on whether the underlying Abi type is a pointer to IUnknown or
-// not. It queries the internals of Windows::Foundation to obtain this
-// information.
-template <typename T>
-using AsyncResultsT =
- std::conditional_t<std::is_convertible<AsyncAbiT<T>, IUnknown*>::value,
- ComPtr<std::remove_pointer_t<AsyncAbiT<T>>>,
- AsyncAbiT<T>>;
-
-// Obtains the results of the provided async operation. Returns it if the
-// operation completed successfully.
-template <typename T>
-AsyncResultsT<T> GetAsyncResults(IAsyncOperation<T>* async_op,
- AsyncStatus async_status) {
- if (async_status != AsyncStatus::Completed) {
- VLOG(2) << "Got unexpected AsyncStatus: " << ToCString(async_status);
- return {};
- }
-
- AsyncResultsT<T> results;
- HRESULT hr = async_op->GetResults(&results);
+base::Optional<BluetoothDevice::UUIDList> ExtractAdvertisedUUIDs(
+ IBluetoothLEAdvertisement* advertisement) {
+ ComPtr<IVector<GUID>> service_uuids;
+ HRESULT hr = advertisement->get_ServiceUuids(&service_uuids);
if (FAILED(hr)) {
- VLOG(2) << "GotAsyncResults failed: "
+ VLOG(2) << "get_ServiceUuids() failed: "
<< logging::SystemErrorCodeToString(hr);
+ return base::nullopt;
}
- return results;
-}
-
-// This method registers a completion handler for |async_op| and will post the
-// results to |callback| on |task_runner|. While a Callback can be constructed
-// from callable types such as a lambda or std::function objects, it cannot be
-// directly constructed from a base::OnceCallback. Thus the callback is moved
-// into a capturing lambda, which then posts the callback once it is run.
-// Posting the results to the TaskRunner is required, since the completion
-// callback might be invoked on an arbitrary thread. Lastly, the lambda takes
-// ownership of |async_op|, as this needs to be kept alive until GetAsyncResults
-// can be invoked. Once that is done it is safe to release the |async_op|.
-template <typename T>
-HRESULT PostAsyncResults(ComPtr<IAsyncOperation<T>> async_op,
- scoped_refptr<base::TaskRunner> task_runner,
- base::OnceCallback<void(AsyncResultsT<T>)> callback) {
- auto async_op_raw = async_op.Get();
- return async_op_raw->put_Completed(
- Callback<IAsyncOperationCompletedHandler<T>>([
- async_op(std::move(async_op)), task_runner(std::move(task_runner)),
- callback(std::move(callback))
- ](IAsyncOperation<T> * async_op_raw, AsyncStatus async_status) mutable {
- // Note: We are using |async_op_raw| instead of async_op.Get(), as this
- // could be executed on any thread and only |async_op_raw| is guaranteed
- // to be in the correct COM apartment.
- task_runner->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(callback),
- GetAsyncResults(async_op_raw, async_status)));
- async_op.Reset();
- return S_OK;
- })
- .Get());
+ unsigned num_service_uuids;
+ hr = service_uuids->get_Size(&num_service_uuids);
+ if (FAILED(hr)) {
+ VLOG(2) << "get_Size() failed: " << logging::SystemErrorCodeToString(hr);
+ return base::nullopt;
+ }
+
+ BluetoothDevice::UUIDList advertised_uuids;
+ for (size_t i = 0; i < num_service_uuids; ++i) {
+ GUID service_uuid;
+ hr = service_uuids->GetAt(i, &service_uuid);
+ if (FAILED(hr)) {
+ VLOG(2) << "GetAt(" << i
+ << ") failed: " << logging::SystemErrorCodeToString(hr);
+ return base::nullopt;
+ }
+
+ advertised_uuids.emplace_back(service_uuid);
+ }
+
+ return advertised_uuids;
+}
+
+ComPtr<IBluetoothLEAdvertisement> GetAdvertisement(
+ IBluetoothLEAdvertisementReceivedEventArgs* received) {
+ ComPtr<IBluetoothLEAdvertisement> advertisement;
+ HRESULT hr = received->get_Advertisement(&advertisement);
+ if (FAILED(hr)) {
+ VLOG(2) << "get_Advertisement() failed: "
+ << logging::SystemErrorCodeToString(hr);
+ }
+
+ return advertisement;
+}
+
+base::Optional<std::string> GetDeviceName(
+ IBluetoothLEAdvertisementReceivedEventArgs* received) {
+ ComPtr<IBluetoothLEAdvertisement> advertisement = GetAdvertisement(received);
+ if (!advertisement)
+ return base::nullopt;
+
+ HSTRING local_name;
+ HRESULT hr = advertisement->get_LocalName(&local_name);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting Local Name failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return base::nullopt;
+ }
+
+ return base::win::ScopedHString(local_name).GetAsUTF8();
+}
+
+void ExtractAndUpdateAdvertisementData(
+ IBluetoothLEAdvertisementReceivedEventArgs* received,
+ BluetoothDevice* device) {
+ int16_t rssi;
+ 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());
}
} // namespace
@@ -150,25 +202,26 @@ bool BluetoothAdapterWinrt::IsInitialized() const {
}
bool BluetoothAdapterWinrt::IsPresent() const {
- return is_present_;
+ // Obtaining the default adapter will fail if no physical adapter is present.
+ // Thus a non-zero |adapter| implies that a physical adapter is present.
+ return adapter_ != nullptr;
}
bool BluetoothAdapterWinrt::IsPowered() const {
- // Note: While the UWP APIs can provide access to the underlying radio [1] and
- // its power state [2], this feature is not available for x86 Apps [3]. Thus
- // we simply assume the adapter is powered if it is present.
- //
- // [1]
- // https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothadapter.getradioasync
- // [2]
- // https://docs.microsoft.com/en-us/uwp/api/windows.devices.radios.radiostate
- // [3] https://github.com/Microsoft/cppwinrt/issues/47#issuecomment-335181782
- if (IsPresent()) {
- LOG(WARNING) << "Optimistically assuming the adapter is powered since it "
- "is present. This might not actually be true.";
+ // 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;
+
+ RadioState state;
+ HRESULT hr = radio_->get_State(&state);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting Radio State failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return false;
}
- return IsPresent();
+ return state == RadioState_On;
}
bool BluetoothAdapterWinrt::IsDiscoverable() const {
@@ -262,7 +315,7 @@ void BluetoothAdapterWinrt::Init(InitCallback init_cb) {
}
hr = PostAsyncResults(
- std::move(get_default_adapter_op), ui_task_runner_,
+ std::move(get_default_adapter_op),
base::BindOnce(&BluetoothAdapterWinrt::OnGetDefaultAdapter,
weak_ptr_factory_.GetWeakPtr(), std::move(on_init)));
@@ -273,22 +326,148 @@ void BluetoothAdapterWinrt::Init(InitCallback init_cb) {
}
bool BluetoothAdapterWinrt::SetPoweredImpl(bool powered) {
- NOTIMPLEMENTED();
- return false;
+ // 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;
+
+ const RadioState state = powered ? RadioState_On : RadioState_Off;
+ ComPtr<IAsyncOperation<RadioAccessStatus>> set_state_op;
+ HRESULT hr = radio_->SetStateAsync(state, &set_state_op);
+ if (FAILED(hr)) {
+ VLOG(2) << "Radio::SetStateAsync failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+
+ hr = PostAsyncResults(std::move(set_state_op),
+ base::BindOnce(&BluetoothAdapterWinrt::OnSetState,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ if (FAILED(hr)) {
+ VLOG(2) << "PostAsyncResults failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+
+ return true;
}
void BluetoothAdapterWinrt::AddDiscoverySession(
BluetoothDiscoveryFilter* discovery_filter,
const base::Closure& callback,
DiscoverySessionErrorCallback error_callback) {
- NOTIMPLEMENTED();
+ if (num_discovery_sessions_ > 0) {
+ ui_task_runner_->PostTask(FROM_HERE, std::move(callback));
+ return;
+ }
+
+ HRESULT hr = ActivateBluetoothAdvertisementLEWatcherInstance(
+ &ble_advertisement_watcher_);
+ if (FAILED(hr)) {
+ VLOG(2) << "ActivateBluetoothAdvertisementLEWatcherInstance failed: "
+ << logging::SystemErrorCodeToString(hr);
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(error_callback),
+ UMABluetoothDiscoverySessionOutcome::UNKNOWN));
+ return;
+ }
+
+ hr = ble_advertisement_watcher_->put_ScanningMode(
+ BluetoothLEScanningMode_Active);
+ if (FAILED(hr)) {
+ VLOG(2) << "Setting ScanningMode to Active failed: "
+ << logging::SystemErrorCodeToString(hr);
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(error_callback),
+ UMABluetoothDiscoverySessionOutcome::UNKNOWN));
+ return;
+ }
+
+ auto advertisement_received_token = AddTypedEventHandler(
+ ble_advertisement_watcher_.Get(),
+ &IBluetoothLEAdvertisementWatcher::add_Received,
+ base::BindRepeating(&BluetoothAdapterWinrt::OnAdvertisementReceived,
+ weak_ptr_factory_.GetWeakPtr()));
+ if (!advertisement_received_token) {
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(error_callback),
+ UMABluetoothDiscoverySessionOutcome::UNKNOWN));
+ return;
+ }
+
+ advertisement_received_token_ = *advertisement_received_token;
+
+ hr = ble_advertisement_watcher_->Start();
+ if (FAILED(hr)) {
+ VLOG(2) << "Starting the Advertisement Watcher failed: "
+ << logging::SystemErrorCodeToString(hr);
+ RemoveAdvertisementReceivedHandler();
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(error_callback),
+ UMABluetoothDiscoverySessionOutcome::UNKNOWN));
+ return;
+ }
+
+ BluetoothLEAdvertisementWatcherStatus watcher_status;
+ hr = ble_advertisement_watcher_->get_Status(&watcher_status);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting the Watcher Status failed: "
+ << logging::SystemErrorCodeToString(hr);
+ } else if (watcher_status == BluetoothLEAdvertisementWatcherStatus_Aborted) {
+ VLOG(2)
+ << "Starting Advertisement Watcher failed, it is in the Aborted state.";
+ RemoveAdvertisementReceivedHandler();
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(error_callback),
+ UMABluetoothDiscoverySessionOutcome::UNKNOWN));
+ return;
+ }
+
+ ++num_discovery_sessions_;
+ ui_task_runner_->PostTask(FROM_HERE, std::move(callback));
}
void BluetoothAdapterWinrt::RemoveDiscoverySession(
BluetoothDiscoveryFilter* discovery_filter,
const base::Closure& callback,
DiscoverySessionErrorCallback error_callback) {
- NOTIMPLEMENTED();
+ if (num_discovery_sessions_ == 0) {
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(error_callback),
+ UMABluetoothDiscoverySessionOutcome::UNKNOWN));
+ return;
+ }
+
+ if (num_discovery_sessions_ > 1) {
+ --num_discovery_sessions_;
+ ui_task_runner_->PostTask(FROM_HERE, std::move(callback));
+ return;
+ }
+
+ RemoveAdvertisementReceivedHandler();
+ HRESULT hr = ble_advertisement_watcher_->Stop();
+ if (FAILED(hr)) {
+ VLOG(2) << "Stopped the Advertisement Watcher failed: "
+ << logging::SystemErrorCodeToString(hr);
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(error_callback),
+ UMABluetoothDiscoverySessionOutcome::UNKNOWN));
+ return;
+ }
+
+ for (auto& device : devices_)
+ device.second->ClearAdvertisementData();
+ ble_advertisement_watcher_.Reset();
+ --num_discovery_sessions_;
+ ui_task_runner_->PostTask(FROM_HERE, std::move(callback));
}
void BluetoothAdapterWinrt::SetDiscoveryFilter(
@@ -317,6 +496,47 @@ HRESULT BluetoothAdapterWinrt::GetDeviceInformationStaticsActivationFactory(
RuntimeClass_Windows_Devices_Enumeration_DeviceInformation>(statics);
}
+HRESULT BluetoothAdapterWinrt::GetRadioStaticsActivationFactory(
+ IRadioStatics** statics) const {
+ return base::win::GetActivationFactory<
+ IRadioStatics, RuntimeClass_Windows_Devices_Radios_Radio>(statics);
+}
+
+HRESULT
+BluetoothAdapterWinrt::ActivateBluetoothAdvertisementLEWatcherInstance(
+ IBluetoothLEAdvertisementWatcher** instance) const {
+ auto watcher_hstring = base::win::ScopedHString::Create(
+ RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEAdvertisementWatcher);
+ if (!watcher_hstring.is_valid())
+ return E_FAIL;
+
+ ComPtr<IInspectable> inspectable;
+ HRESULT hr =
+ base::win::RoActivateInstance(watcher_hstring.get(), &inspectable);
+ if (FAILED(hr)) {
+ VLOG(2) << "RoActivateInstance failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return hr;
+ }
+
+ ComPtr<IBluetoothLEAdvertisementWatcher> watcher;
+ hr = inspectable.As(&watcher);
+ if (FAILED(hr)) {
+ VLOG(2) << "As IBluetoothLEAdvertisementWatcher failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return hr;
+ }
+
+ return watcher.CopyTo(instance);
+}
+
+std::unique_ptr<BluetoothDeviceWinrt> BluetoothAdapterWinrt::CreateDevice(
+ uint64_t raw_address,
+ base::Optional<std::string> local_name) {
+ return std::make_unique<BluetoothDeviceWinrt>(this, raw_address,
+ std::move(local_name));
+}
+
void BluetoothAdapterWinrt::OnGetDefaultAdapter(
base::ScopedClosureRunner on_init,
ComPtr<IBluetoothAdapter> adapter) {
@@ -326,11 +546,9 @@ void BluetoothAdapterWinrt::OnGetDefaultAdapter(
return;
}
- // Obtaining the default adapter will fail if no physical adapter is present.
- // Thus a non-zero |adapter| implies that a physical adapter is present.
- is_present_ = true;
+ adapter_ = std::move(adapter);
uint64_t raw_address;
- HRESULT hr = adapter->get_BluetoothAddress(&raw_address);
+ HRESULT hr = adapter_->get_BluetoothAddress(&raw_address);
if (FAILED(hr)) {
VLOG(2) << "Getting BluetoothAddress failed: "
<< logging::SystemErrorCodeToString(hr);
@@ -342,7 +560,7 @@ void BluetoothAdapterWinrt::OnGetDefaultAdapter(
DCHECK(!address_.empty());
HSTRING device_id;
- hr = adapter->get_DeviceId(&device_id);
+ hr = adapter_->get_DeviceId(&device_id);
if (FAILED(hr)) {
VLOG(2) << "Getting DeviceId failed: "
<< logging::SystemErrorCodeToString(hr);
@@ -368,7 +586,7 @@ void BluetoothAdapterWinrt::OnGetDefaultAdapter(
}
hr = PostAsyncResults(
- std::move(create_from_id_op), ui_task_runner_,
+ std::move(create_from_id_op),
base::BindOnce(&BluetoothAdapterWinrt::OnCreateFromIdAsync,
weak_ptr_factory_.GetWeakPtr(), std::move(on_init)));
if (FAILED(hr)) {
@@ -394,6 +612,127 @@ void BluetoothAdapterWinrt::OnCreateFromIdAsync(
}
name_ = base::win::ScopedHString(name).GetAsUTF8();
+
+ ComPtr<IRadioStatics> radio_statics;
+ hr = GetRadioStaticsActivationFactory(&radio_statics);
+ if (FAILED(hr)) {
+ VLOG(2) << "GetRadioStaticsActivationFactory failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return;
+ }
+
+ ComPtr<IAsyncOperation<RadioAccessStatus>> request_access_op;
+ hr = radio_statics->RequestAccessAsync(&request_access_op);
+ if (FAILED(hr)) {
+ VLOG(2) << "RequestAccessAsync failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return;
+ }
+
+ hr = PostAsyncResults(
+ std::move(request_access_op),
+ base::BindOnce(&BluetoothAdapterWinrt::OnRequestAccess,
+ weak_ptr_factory_.GetWeakPtr(), std::move(on_init)));
+
+ if (FAILED(hr)) {
+ VLOG(2) << "PostAsyncResults failed: "
+ << logging::SystemErrorCodeToString(hr);
+ }
+}
+
+void BluetoothAdapterWinrt::OnRequestAccess(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: "
+ << ToCString(access_status);
+ return;
+ }
+
+ ComPtr<IAsyncOperation<Radio*>> get_radio_op;
+ HRESULT hr = adapter_->GetRadioAsync(&get_radio_op);
+ if (FAILED(hr)) {
+ VLOG(2) << "GetRadioAsync failed: " << logging::SystemErrorCodeToString(hr);
+ return;
+ }
+
+ hr = PostAsyncResults(
+ std::move(get_radio_op),
+ base::BindOnce(&BluetoothAdapterWinrt::OnGetRadio,
+ weak_ptr_factory_.GetWeakPtr(), std::move(on_init)));
+
+ if (FAILED(hr)) {
+ VLOG(2) << "PostAsyncResults failed: "
+ << logging::SystemErrorCodeToString(hr);
+ }
+}
+
+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.";
+ return;
+ }
+
+ radio_ = std::move(radio);
+}
+
+void BluetoothAdapterWinrt::OnSetState(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());
+ }
+
+ DidChangePoweredState();
+}
+
+void BluetoothAdapterWinrt::OnAdvertisementReceived(
+ IBluetoothLEAdvertisementWatcher* watcher,
+ IBluetoothLEAdvertisementReceivedEventArgs* received) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ uint64_t raw_bluetooth_address;
+ HRESULT hr = received->get_BluetoothAddress(&raw_bluetooth_address);
+ if (FAILED(hr)) {
+ VLOG(2) << "get_BluetoothAddress() failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return;
+ }
+
+ std::string bluetooth_address =
+ BluetoothDeviceWinrt::CanonicalizeAddress(raw_bluetooth_address);
+ auto it = devices_.find(bluetooth_address);
+ const bool is_new_device = (it == devices_.end());
+ if (is_new_device) {
+ bool was_inserted = false;
+ std::tie(it, was_inserted) = devices_.emplace(
+ std::move(bluetooth_address),
+ CreateDevice(raw_bluetooth_address, GetDeviceName(received)));
+ DCHECK(was_inserted);
+ }
+
+ BluetoothDevice* const device = it->second.get();
+ ExtractAndUpdateAdvertisementData(received, device);
+
+ for (auto& observer : observers_) {
+ is_new_device ? observer.DeviceAdded(this, device)
+ : observer.DeviceChanged(this, device);
+ }
+}
+
+void BluetoothAdapterWinrt::RemoveAdvertisementReceivedHandler() {
+ DCHECK(ble_advertisement_watcher_);
+ HRESULT hr = ble_advertisement_watcher_->remove_Received(
+ advertisement_received_token_);
+ if (FAILED(hr)) {
+ VLOG(2) << "Removing the Received Handler failed: "
+ << logging::SystemErrorCodeToString(hr);
+ }
}
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_adapter_winrt.h b/chromium/device/bluetooth/bluetooth_adapter_winrt.h
index a607e31927d..cb60c4c84f1 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_winrt.h
+++ b/chromium/device/bluetooth/bluetooth_adapter_winrt.h
@@ -7,6 +7,7 @@
#include <windows.devices.bluetooth.h>
#include <windows.devices.enumeration.h>
+#include <windows.devices.radios.h>
#include <wrl/client.h>
#include <memory>
@@ -24,6 +25,8 @@ class ScopedClosureRunner;
namespace device {
+class BluetoothDeviceWinrt;
+
class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterWinrt : public BluetoothAdapter {
public:
// BluetoothAdapter:
@@ -60,7 +63,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterWinrt : public BluetoothAdapter {
protected:
friend class BluetoothAdapterWin;
- friend class BluetoothTestWin;
+ friend class BluetoothTestWinrt;
BluetoothAdapterWinrt();
~BluetoothAdapterWinrt() override;
@@ -88,10 +91,21 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterWinrt : public BluetoothAdapter {
virtual HRESULT GetBluetoothAdapterStaticsActivationFactory(
ABI::Windows::Devices::Bluetooth::IBluetoothAdapterStatics** statics)
const;
+
virtual HRESULT GetDeviceInformationStaticsActivationFactory(
ABI::Windows::Devices::Enumeration::IDeviceInformationStatics** statics)
const;
+ virtual HRESULT GetRadioStaticsActivationFactory(
+ ABI::Windows::Devices::Radios::IRadioStatics** statics) const;
+
+ virtual HRESULT ActivateBluetoothAdvertisementLEWatcherInstance(
+ ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEAdvertisementWatcher** instance) const;
+ virtual std::unique_ptr<BluetoothDeviceWinrt> CreateDevice(
+ uint64_t raw_address,
+ base::Optional<std::string> local_name);
+
private:
void OnGetDefaultAdapter(
base::ScopedClosureRunner on_init,
@@ -104,11 +118,39 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterWinrt : public BluetoothAdapter {
ABI::Windows::Devices::Enumeration::IDeviceInformation>
device_information);
+ void OnRequestAccess(
+ base::ScopedClosureRunner on_init,
+ ABI::Windows::Devices::Radios::RadioAccessStatus access_status);
+
+ void OnGetRadio(
+ base::ScopedClosureRunner on_init,
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Radios::IRadio> radio);
+
+ void OnSetState(
+ ABI::Windows::Devices::Radios::RadioAccessStatus access_status);
+
+ void OnAdvertisementReceived(
+ ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEAdvertisementWatcher* watcher,
+ ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEAdvertisementReceivedEventArgs* received);
+
+ void RemoveAdvertisementReceivedHandler();
+
bool is_initialized_ = false;
- bool is_present_ = false;
std::string address_;
std::string name_;
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::IBluetoothAdapter>
+ adapter_;
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Radios::IRadio> radio_;
+
+ size_t num_discovery_sessions_ = 0;
+ EventRegistrationToken advertisement_received_token_;
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::Advertisement::
+ IBluetoothLEAdvertisementWatcher>
+ ble_advertisement_watcher_;
+
THREAD_CHECKER(thread_checker_);
// Note: This should remain the last member so it'll be destroyed and
diff --git a/chromium/device/bluetooth/bluetooth_advertisement.h b/chromium/device/bluetooth/bluetooth_advertisement.h
index d379f72453a..f17bb025bfd 100644
--- a/chromium/device/bluetooth/bluetooth_advertisement.h
+++ b/chromium/device/bluetooth/bluetooth_advertisement.h
@@ -38,10 +38,13 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdvertisement
// is not registered.
ERROR_ADVERTISEMENT_INVALID_LENGTH, // Advertisement is not of a valid
// length.
+ ERROR_STARTING_ADVERTISEMENT, // Error when starting the advertisement
+ // through a platform API.
+ ERROR_RESET_ADVERTISING, // Error while resetting advertising.
+ ERROR_ADAPTER_POWERED_OFF, // Error because the adapter is off
#if defined(OS_LINUX)
ERROR_INVALID_ADVERTISEMENT_INTERVAL, // Advertisement interval specified
// is out of valid range.
- ERROR_RESET_ADVERTISING, // Error while resetting advertising.
#endif
INVALID_ADVERTISEMENT_ERROR_CODE
};
diff --git a/chromium/device/bluetooth/bluetooth_advertisement_mac.h b/chromium/device/bluetooth/bluetooth_advertisement_mac.h
new file mode 100644
index 00000000000..c3efb960614
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_advertisement_mac.h
@@ -0,0 +1,85 @@
+// 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_MAC_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_ADVERTISEMENT_MAC_H_
+
+#import <CoreBluetooth/CoreBluetooth.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "dbus/object_path.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_advertisement.h"
+#include "device/bluetooth/bluetooth_export.h"
+
+namespace device {
+
+class BluetoothLowEnergyAdvertisementManagerMac;
+
+// Simple implementation of BluetoothAdvertisement for Mac. The primary logic is
+// currently handled in BluetoothLowEnergyAdvertisementManagerMac.
+class DEVICE_BLUETOOTH_EXPORT BluetoothAdvertisementMac
+ : public BluetoothAdvertisement {
+ public:
+ enum Status {
+ WAITING_FOR_ADAPTER,
+ ADVERTISEMENT_PENDING,
+ ADVERTISING,
+ ERROR_ADVERTISING,
+ UNREGISTERED,
+ };
+
+ BluetoothAdvertisementMac(
+ std::unique_ptr<BluetoothAdvertisement::UUIDList> service_uuids,
+ const BluetoothAdapter::CreateAdvertisementCallback& callback,
+ const BluetoothAdapter::AdvertisementErrorCallback& error_callback,
+ BluetoothLowEnergyAdvertisementManagerMac* advertisement_manager);
+
+ // BluetoothAdvertisement overrides:
+ void Unregister(const SuccessCallback& success_callback,
+ const ErrorCallback& error_callback) override;
+
+ Status status() const { return status_; }
+
+ bool is_waiting_for_adapter() { return status_ == WAITING_FOR_ADAPTER; }
+
+ bool is_advertising() { return status_ == ADVERTISING; }
+
+ bool is_advertisement_pending() { return status_ == ADVERTISEMENT_PENDING; }
+
+ BluetoothAdvertisement::UUIDList service_uuids() { return *service_uuids_; }
+
+ private:
+ friend class BluetoothLowEnergyAdvertisementManagerMac;
+
+ ~BluetoothAdvertisementMac() override;
+
+ // Called by BluetoothLowEnergyAdvertisementManagerMac.
+ void OnAdvertisementPending();
+ void OnAdvertisementError(base::SingleThreadTaskRunner* task_runner,
+ BluetoothAdvertisement::ErrorCode error_code);
+ void OnAdvertisementSuccess(base::SingleThreadTaskRunner* task_runner);
+ void OnAdapterReset();
+ void OnAdvertisementRestarted();
+
+ void InvokeSuccessCallback();
+
+ BluetoothAdapter::CreateAdvertisementCallback success_callback() {
+ return success_callback_;
+ }
+
+ std::unique_ptr<BluetoothAdvertisement::UUIDList> service_uuids_;
+ BluetoothAdapter::CreateAdvertisementCallback success_callback_;
+ BluetoothAdapter::AdvertisementErrorCallback error_callback_;
+ BluetoothLowEnergyAdvertisementManagerMac* advertisement_manager_;
+ Status status_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothAdvertisementMac);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_ADVERTISEMENT_MAC_H_
diff --git a/chromium/device/bluetooth/bluetooth_advertisement_mac.mm b/chromium/device/bluetooth/bluetooth_advertisement_mac.mm
new file mode 100644
index 00000000000..64516095341
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_advertisement_mac.mm
@@ -0,0 +1,69 @@
+// 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_mac.h"
+
+#include "base/bind.h"
+#include "device/bluetooth/bluetooth_adapter_mac.h"
+
+namespace device {
+
+BluetoothAdvertisementMac::BluetoothAdvertisementMac(
+ std::unique_ptr<BluetoothAdvertisement::UUIDList> service_uuids,
+ const BluetoothAdapter::CreateAdvertisementCallback& success_callback,
+ const BluetoothAdapter::AdvertisementErrorCallback& error_callback,
+ BluetoothLowEnergyAdvertisementManagerMac* advertisement_manager)
+ : service_uuids_(std::move(service_uuids)),
+ success_callback_(success_callback),
+ error_callback_(error_callback),
+ advertisement_manager_(advertisement_manager),
+ status_(BluetoothAdvertisementMac::WAITING_FOR_ADAPTER) {}
+
+void BluetoothAdvertisementMac::Unregister(
+ const SuccessCallback& success_callback,
+ const ErrorCallback& error_callback) {
+ if (status_ == Status::UNREGISTERED) {
+ success_callback.Run();
+ return;
+ }
+
+ status_ = Status::UNREGISTERED;
+ advertisement_manager_->UnregisterAdvertisement(this, success_callback,
+ error_callback);
+}
+
+BluetoothAdvertisementMac::~BluetoothAdvertisementMac() {
+ // This object should be owned by BluetoothLowEnergyAdvertisementManagerMac,
+ // and will be cleaned up there.
+}
+
+void BluetoothAdvertisementMac::OnAdvertisementPending() {
+ status_ = Status::ADVERTISEMENT_PENDING;
+}
+
+void BluetoothAdvertisementMac::OnAdvertisementError(
+ base::SingleThreadTaskRunner* task_runner,
+ BluetoothAdvertisement::ErrorCode error_code) {
+ status_ = Status::ERROR_ADVERTISING;
+ task_runner->PostTask(FROM_HERE,
+ base::BindOnce(std::move(error_callback_), error_code));
+}
+
+void BluetoothAdvertisementMac::OnAdvertisementSuccess(
+ base::SingleThreadTaskRunner* task_runner) {
+ status_ = Status::ADVERTISING;
+ task_runner->PostTask(
+ FROM_HERE,
+ base::BindOnce(&BluetoothAdvertisementMac::InvokeSuccessCallback, this));
+}
+
+void BluetoothAdvertisementMac::OnAdapterReset() {
+ status_ = Status::UNREGISTERED;
+}
+
+void BluetoothAdvertisementMac::InvokeSuccessCallback() {
+ success_callback_.Run(this);
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_classic_win.cc b/chromium/device/bluetooth/bluetooth_classic_win.cc
index e2680938dd4..83aaec9bc82 100644
--- a/chromium/device/bluetooth/bluetooth_classic_win.cc
+++ b/chromium/device/bluetooth/bluetooth_classic_win.cc
@@ -4,74 +4,37 @@
#include "device/bluetooth/bluetooth_classic_win.h"
-namespace {
-static device::win::BluetoothClassicWrapper* g_instance_ = nullptr;
-} // namespace
-
namespace device {
namespace win {
-BluetoothClassicWrapper* BluetoothClassicWrapper::GetInstance() {
- if (g_instance_ == nullptr) {
- g_instance_ = new BluetoothClassicWrapper();
- }
- return g_instance_;
-}
-
-void BluetoothClassicWrapper::DeleteInstance() {
- delete g_instance_;
- g_instance_ = nullptr;
-}
-
-void BluetoothClassicWrapper::SetInstanceForTest(
- BluetoothClassicWrapper* instance) {
- delete g_instance_;
- g_instance_ = instance;
-}
-
BluetoothClassicWrapper::BluetoothClassicWrapper() {}
BluetoothClassicWrapper::~BluetoothClassicWrapper() {}
HBLUETOOTH_RADIO_FIND BluetoothClassicWrapper::FindFirstRadio(
- const BLUETOOTH_FIND_RADIO_PARAMS* params,
- HANDLE* out_handle) {
+ const BLUETOOTH_FIND_RADIO_PARAMS* params) {
HANDLE radio_handle = INVALID_HANDLE_VALUE;
HBLUETOOTH_RADIO_FIND radio_find_handle =
BluetoothFindFirstRadio(params, &radio_handle);
if (radio_find_handle) {
- // TODO(crbug.com/820864): At some point, we crash when we attempt to close
- // the handle. This and the related checks in this file are designed to
- // see if our handle becomes garbage (i.e., "valid", but not closeable) at
- // some point.
- DWORD info;
- CHECK(!base::win::HandleTraits::IsHandleValid(radio_handle) ||
- ::GetHandleInformation(radio_handle, &info));
+ DCHECK_NE(radio_handle, INVALID_HANDLE_VALUE);
opened_radio_handle_.Set(radio_handle);
- *out_handle = opened_radio_handle_.Get();
}
return radio_find_handle;
}
DWORD BluetoothClassicWrapper::GetRadioInfo(
- HANDLE handle,
PBLUETOOTH_RADIO_INFO out_radio_info) {
- DWORD ret = BluetoothGetRadioInfo(handle, out_radio_info);
- DWORD info;
- // TODO(crbug.com/820864): Remove this check.
- CHECK(::GetHandleInformation(handle, &info));
- return ret;
+ DCHECK(opened_radio_handle_.IsValid());
+ return BluetoothGetRadioInfo(opened_radio_handle_.Get(), out_radio_info);
}
BOOL BluetoothClassicWrapper::FindRadioClose(HBLUETOOTH_RADIO_FIND handle) {
return BluetoothFindRadioClose(handle);
}
-BOOL BluetoothClassicWrapper::IsConnectable(HANDLE handle) {
- DWORD ret = BluetoothIsConnectable(handle);
- DWORD info;
- // TODO(crbug.com/820864): Remove this check.
- CHECK(::GetHandleInformation(handle, &info));
- return ret;
+BOOL BluetoothClassicWrapper::IsConnectable() {
+ DCHECK(opened_radio_handle_.IsValid());
+ return BluetoothIsConnectable(opened_radio_handle_.Get());
}
HBLUETOOTH_DEVICE_FIND BluetoothClassicWrapper::FindFirstDevice(
@@ -90,26 +53,24 @@ BOOL BluetoothClassicWrapper::FindDeviceClose(HBLUETOOTH_DEVICE_FIND handle) {
return BluetoothFindDeviceClose(handle);
}
-BOOL BluetoothClassicWrapper::EnableDiscovery(HANDLE handle, BOOL is_enable) {
- DWORD ret = BluetoothEnableDiscovery(handle, is_enable);
- DWORD info;
- // TODO(crbug.com/820864): Remove this check.
- CHECK(::GetHandleInformation(handle, &info));
- return ret;
+BOOL BluetoothClassicWrapper::EnableDiscovery(BOOL is_enable) {
+ DCHECK(opened_radio_handle_.IsValid());
+ return BluetoothEnableDiscovery(opened_radio_handle_.Get(), is_enable);
}
-BOOL BluetoothClassicWrapper::EnableIncomingConnections(HANDLE handle,
- BOOL is_enable) {
- DWORD ret = BluetoothEnableIncomingConnections(handle, is_enable);
- DWORD info;
- // TODO(crbug.com/820864): Remove this check.
- CHECK(::GetHandleInformation(handle, &info));
- return ret;
+BOOL BluetoothClassicWrapper::EnableIncomingConnections(BOOL is_enable) {
+ DCHECK(opened_radio_handle_.IsValid());
+ return BluetoothEnableIncomingConnections(opened_radio_handle_.Get(),
+ is_enable);
}
DWORD BluetoothClassicWrapper::LastError() {
return GetLastError();
}
+bool BluetoothClassicWrapper::HasHandle() {
+ return opened_radio_handle_.IsValid();
+}
+
} // namespace win
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_classic_win.h b/chromium/device/bluetooth/bluetooth_classic_win.h
index e909db35f8f..c7bee1a3785 100644
--- a/chromium/device/bluetooth/bluetooth_classic_win.h
+++ b/chromium/device/bluetooth/bluetooth_classic_win.h
@@ -17,30 +17,24 @@ namespace win {
// tests.
class DEVICE_BLUETOOTH_EXPORT BluetoothClassicWrapper {
public:
- static BluetoothClassicWrapper* GetInstance();
- static void DeleteInstance();
- static void SetInstanceForTest(BluetoothClassicWrapper* instance);
+ BluetoothClassicWrapper();
+ virtual ~BluetoothClassicWrapper();
virtual HBLUETOOTH_RADIO_FIND FindFirstRadio(
- const BLUETOOTH_FIND_RADIO_PARAMS* params,
- HANDLE* out_handle);
- virtual DWORD GetRadioInfo(HANDLE handle,
- PBLUETOOTH_RADIO_INFO out_radio_info);
+ const BLUETOOTH_FIND_RADIO_PARAMS* params);
+ virtual DWORD GetRadioInfo(PBLUETOOTH_RADIO_INFO out_radio_info);
virtual BOOL FindRadioClose(HBLUETOOTH_RADIO_FIND handle);
- virtual BOOL IsConnectable(HANDLE handle);
+ virtual BOOL IsConnectable();
virtual HBLUETOOTH_DEVICE_FIND FindFirstDevice(
const BLUETOOTH_DEVICE_SEARCH_PARAMS* params,
BLUETOOTH_DEVICE_INFO* out_device_info);
virtual BOOL FindNextDevice(HBLUETOOTH_DEVICE_FIND handle,
BLUETOOTH_DEVICE_INFO* out_device_info);
virtual BOOL FindDeviceClose(HBLUETOOTH_DEVICE_FIND handle);
- virtual BOOL EnableDiscovery(HANDLE handle, BOOL is_enable);
- virtual BOOL EnableIncomingConnections(HANDLE handle, BOOL is_enable);
+ virtual BOOL EnableDiscovery(BOOL is_enable);
+ virtual BOOL EnableIncomingConnections(BOOL is_enable);
virtual DWORD LastError();
-
- protected:
- BluetoothClassicWrapper();
- virtual ~BluetoothClassicWrapper();
+ virtual bool HasHandle();
private:
base::win::ScopedHandle opened_radio_handle_;
diff --git a/chromium/device/bluetooth/bluetooth_classic_win_fake.cc b/chromium/device/bluetooth/bluetooth_classic_win_fake.cc
index 013711f53fd..5a8672e4769 100644
--- a/chromium/device/bluetooth/bluetooth_classic_win_fake.cc
+++ b/chromium/device/bluetooth/bluetooth_classic_win_fake.cc
@@ -14,19 +14,16 @@ BluetoothClassicWrapperFake::BluetoothClassicWrapperFake()
BluetoothClassicWrapperFake::~BluetoothClassicWrapperFake() {}
HBLUETOOTH_RADIO_FIND BluetoothClassicWrapperFake::FindFirstRadio(
- const BLUETOOTH_FIND_RADIO_PARAMS* params,
- HANDLE* out_handle) {
+ const BLUETOOTH_FIND_RADIO_PARAMS* params) {
if (simulated_radios_) {
- *out_handle = (PVOID)simulated_radios_.get();
last_error_ = ERROR_SUCCESS;
- return *out_handle;
+ return (PVOID)simulated_radios_.get();
}
last_error_ = ERROR_NO_MORE_ITEMS;
return NULL;
}
DWORD BluetoothClassicWrapperFake::GetRadioInfo(
- HANDLE handle,
PBLUETOOTH_RADIO_INFO out_radio_info) {
if (simulated_radios_) {
*out_radio_info = simulated_radios_->radio_info;
@@ -38,10 +35,11 @@ DWORD BluetoothClassicWrapperFake::GetRadioInfo(
}
BOOL BluetoothClassicWrapperFake::FindRadioClose(HBLUETOOTH_RADIO_FIND handle) {
+ DCHECK_EQ(handle, (PVOID)simulated_radios_.get());
return TRUE;
}
-BOOL BluetoothClassicWrapperFake::IsConnectable(HANDLE handle) {
+BOOL BluetoothClassicWrapperFake::IsConnectable() {
if (simulated_radios_) {
last_error_ = ERROR_SUCCESS;
return simulated_radios_->is_connectable;
@@ -69,13 +67,11 @@ BOOL BluetoothClassicWrapperFake::FindDeviceClose(
return TRUE;
}
-BOOL BluetoothClassicWrapperFake::EnableDiscovery(HANDLE handle,
- BOOL is_enable) {
+BOOL BluetoothClassicWrapperFake::EnableDiscovery(BOOL is_enable) {
return TRUE;
}
-BOOL BluetoothClassicWrapperFake::EnableIncomingConnections(HANDLE handle,
- BOOL is_enable) {
+BOOL BluetoothClassicWrapperFake::EnableIncomingConnections(BOOL is_enable) {
return TRUE;
}
@@ -83,6 +79,10 @@ DWORD BluetoothClassicWrapperFake::LastError() {
return last_error_;
}
+bool BluetoothClassicWrapperFake::HasHandle() {
+ return bool(simulated_radios_);
+}
+
BluetoothRadio* BluetoothClassicWrapperFake::SimulateARadio(
base::string16 name,
BLUETOOTH_ADDRESS address) {
diff --git a/chromium/device/bluetooth/bluetooth_classic_win_fake.h b/chromium/device/bluetooth/bluetooth_classic_win_fake.h
index e2f86de2b67..c620bfa7f00 100644
--- a/chromium/device/bluetooth/bluetooth_classic_win_fake.h
+++ b/chromium/device/bluetooth/bluetooth_classic_win_fake.h
@@ -25,21 +25,20 @@ class BluetoothClassicWrapperFake : public BluetoothClassicWrapper {
~BluetoothClassicWrapperFake() override;
HBLUETOOTH_RADIO_FIND FindFirstRadio(
- const BLUETOOTH_FIND_RADIO_PARAMS* params,
- HANDLE* out_handle) override;
- DWORD GetRadioInfo(HANDLE handle,
- PBLUETOOTH_RADIO_INFO out_radio_info) override;
+ const BLUETOOTH_FIND_RADIO_PARAMS* params) override;
+ DWORD GetRadioInfo(PBLUETOOTH_RADIO_INFO out_radio_info) override;
BOOL FindRadioClose(HBLUETOOTH_RADIO_FIND handle) override;
- BOOL IsConnectable(HANDLE handle) override;
+ BOOL IsConnectable() override;
HBLUETOOTH_DEVICE_FIND FindFirstDevice(
const BLUETOOTH_DEVICE_SEARCH_PARAMS* params,
BLUETOOTH_DEVICE_INFO* out_device_info) override;
BOOL FindNextDevice(HBLUETOOTH_DEVICE_FIND handle,
BLUETOOTH_DEVICE_INFO* out_device_info) override;
BOOL FindDeviceClose(HBLUETOOTH_DEVICE_FIND handle) override;
- BOOL EnableDiscovery(HANDLE handle, BOOL is_enable) override;
- BOOL EnableIncomingConnections(HANDLE handle, BOOL is_enable) override;
+ BOOL EnableDiscovery(BOOL is_enable) override;
+ BOOL EnableIncomingConnections(BOOL is_enable) override;
DWORD LastError() override;
+ bool HasHandle() override;
BluetoothRadio* SimulateARadio(base::string16 name,
BLUETOOTH_ADDRESS address);
diff --git a/chromium/device/bluetooth/bluetooth_device.cc b/chromium/device/bluetooth/bluetooth_device.cc
index b8988d232b7..57134686533 100644
--- a/chromium/device/bluetooth/bluetooth_device.cc
+++ b/chromium/device/bluetooth/bluetooth_device.cc
@@ -7,6 +7,7 @@
#include <iterator>
#include <memory>
#include <string>
+#include <utility>
#include "base/memory/ptr_util.h"
#include "base/strings/string_util.h"
@@ -332,14 +333,14 @@ base::Optional<int8_t> BluetoothDevice::GetInquiryRSSI() const {
return inquiry_rssi_;
}
-base::Optional<int8_t> BluetoothDevice::GetInquiryTxPower() const {
- return inquiry_tx_power_;
-}
-
base::Optional<uint8_t> BluetoothDevice::GetAdvertisingDataFlags() const {
return advertising_data_flags_;
}
+base::Optional<int8_t> BluetoothDevice::GetInquiryTxPower() const {
+ return inquiry_tx_power_;
+}
+
void BluetoothDevice::CreateGattConnection(
const GattConnectionCallback& callback,
const ConnectErrorCallback& error_callback) {
@@ -415,31 +416,28 @@ std::string BluetoothDevice::GetIdentifier() const { return GetAddress(); }
void BluetoothDevice::UpdateAdvertisementData(
int8_t rssi,
+ base::Optional<uint8_t> flags,
UUIDList advertised_uuids,
+ base::Optional<int8_t> tx_power,
ServiceDataMap service_data,
- ManufacturerDataMap manufacturer_data,
- const int8_t* tx_power) {
+ ManufacturerDataMap manufacturer_data) {
UpdateTimestamp();
inquiry_rssi_ = rssi;
-
+ advertising_data_flags_ = std::move(flags);
device_uuids_.ReplaceAdvertisedUUIDs(std::move(advertised_uuids));
+ inquiry_tx_power_ = std::move(tx_power);
service_data_ = std::move(service_data);
manufacturer_data_ = std::move(manufacturer_data);
-
- if (tx_power != nullptr) {
- inquiry_tx_power_ = *tx_power;
- } else {
- inquiry_tx_power_ = base::nullopt;
- }
}
void BluetoothDevice::ClearAdvertisementData() {
- inquiry_rssi_ = base::nullopt;
+ inquiry_rssi_.reset();
+ advertising_data_flags_.reset();
+ inquiry_tx_power_.reset();
device_uuids_.ClearAdvertisedUUIDs();
service_data_.clear();
manufacturer_data_.clear();
- inquiry_tx_power_ = base::nullopt;
GetAdapter()->NotifyDeviceChanged(this);
}
diff --git a/chromium/device/bluetooth/bluetooth_device.h b/chromium/device/bluetooth/bluetooth_device.h
index 69efd038853..e0044d638a4 100644
--- a/chromium/device/bluetooth/bluetooth_device.h
+++ b/chromium/device/bluetooth/bluetooth_device.h
@@ -284,14 +284,16 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDevice {
// Returns the set of UUIDs that this device supports.
// * For classic Bluetooth devices this data is collected from both the EIR
// data and SDP tables.
- // * For non-connected Low Energy Devices this returns the latest advertised
- // UUIDs.
- // * For connected Low Energy Devices for which services have not been
- // discovered returns an empty list.
+ // * For non-connected and connected Low Energy Devices for which services
+ // have not been discovered returns the latest advertised UUIDs.
// * For connected Low Energy Devices for which services have been discovered
- // returns the UUIDs of the device's services.
+ // returns the UUIDs of the device's services and the latest advertised
+ // UUIDs.
// * For dual mode devices this may be collected from both.
//
+ // Note: On Android, Mac and WinRT advertised UUIDs are cleared when the
+ // adapter stops discovering, as otherwise stale data might be returned.
+ //
// Note: On ChromeOS and Linux, BlueZ persists all services meaning if
// a device stops advertising a service this function will still return
// its UUID.
@@ -543,12 +545,15 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDevice {
virtual base::Time GetLastUpdateTime() const;
// Called by BluetoothAdapter when a new Advertisement is seen for this
- // device. This replaces previously seen Advertisement Data.
+ // device. This replaces previously seen Advertisement Data. The order of
+ // arguments matches the order of their corresponding Data Type specified in
+ // https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile.
void UpdateAdvertisementData(int8_t rssi,
+ base::Optional<uint8_t> flags,
UUIDList advertised_uuids,
+ base::Optional<int8_t> tx_power,
ServiceDataMap service_data,
- ManufacturerDataMap manufacturer_data,
- const int8_t* tx_power);
+ ManufacturerDataMap manufacturer_data);
// Called by BluetoothAdapter when it stops discoverying.
void ClearAdvertisementData();
@@ -561,6 +566,20 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDevice {
std::vector<BluetoothRemoteGattService*> GetPrimaryServicesByUUID(
const BluetoothUUID& service_uuid);
+#if defined(OS_CHROMEOS)
+ typedef base::Callback<void(device::BluetoothGattService::GattErrorCode)>
+ ExecuteWriteErrorCallback;
+ typedef base::Callback<void(device::BluetoothGattService::GattErrorCode)>
+ AbortWriteErrorCallback;
+ // Executes all the previous prepare writes in a reliable write session.
+ virtual void ExecuteWrite(
+ const base::Closure& callback,
+ const ExecuteWriteErrorCallback& error_callback) = 0;
+ // Aborts all the previous prepare writes in a reliable write session.
+ virtual void AbortWrite(const base::Closure& callback,
+ const AbortWriteErrorCallback& error_callback) = 0;
+#endif
+
protected:
// BluetoothGattConnection is a friend to call Add/RemoveGattConnection.
friend BluetoothGattConnection;
@@ -578,6 +597,17 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDevice {
FRIEND_TEST_ALL_PREFIXES(BluetoothTest, RemoveOutdatedDevices);
FRIEND_TEST_ALL_PREFIXES(BluetoothTest, RemoveOutdatedDeviceGattConnect);
+ FRIEND_TEST_ALL_PREFIXES(
+ BluetoothTestWinrtOnly,
+ BluetoothGattConnection_DisconnectGatt_SimulateConnect);
+ FRIEND_TEST_ALL_PREFIXES(
+ BluetoothTestWinrtOnly,
+ BluetoothGattConnection_DisconnectGatt_SimulateDisconnect);
+ FRIEND_TEST_ALL_PREFIXES(BluetoothTestWinrtOnly,
+ BluetoothGattConnection_ErrorAfterConnection);
+ FRIEND_TEST_ALL_PREFIXES(BluetoothTestWinrtOnly,
+ BluetoothGattConnection_DisconnectGatt_Cleanup);
+
// Helper class to easily update the sets of UUIDs and keep them in sync with
// the set of all the device's UUIDs.
class DeviceUUIDs {
@@ -660,12 +690,12 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDevice {
// Received Signal Strength Indicator of the advertisement received.
base::Optional<int8_t> inquiry_rssi_;
- // Tx Power advertised by the device.
- base::Optional<int8_t> inquiry_tx_power_;
-
// Advertising Data flags of the device.
base::Optional<uint8_t> advertising_data_flags_;
+ // Tx Power advertised by the device.
+ base::Optional<int8_t> inquiry_tx_power_;
+
// Class that holds the union of Advertised UUIDs and Service UUIDs.
DeviceUUIDs device_uuids_;
diff --git a/chromium/device/bluetooth/bluetooth_device_unittest.cc b/chromium/device/bluetooth/bluetooth_device_unittest.cc
index 7bcb5142a96..83924cb6713 100644
--- a/chromium/device/bluetooth/bluetooth_device_unittest.cc
+++ b/chromium/device/bluetooth/bluetooth_device_unittest.cc
@@ -47,46 +47,6 @@ using UUIDSet = BluetoothDevice::UUIDSet;
using ServiceDataMap = BluetoothDevice::ServiceDataMap;
using ManufacturerDataMap = BluetoothDevice::ManufacturerDataMap;
-class BluetoothGetServiceTest : public BluetoothTest {
- public:
- BluetoothGetServiceTest()
- : unique_service_uuid_(kTestUUIDGenericAccess),
- duplicate_service_uuid_(kTestUUIDHeartRate) {}
-
- // Creates |device_|.
- void FakeServiceBoilerplate() {
- InitWithFakeAdapter();
- StartLowEnergyDiscoverySession();
- device_ = SimulateLowEnergyDevice(3);
- EXPECT_FALSE(device_->IsConnected());
-
- // Connect to the device.
- ResetEventCounts();
- device_->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
- GetConnectErrorCallback(Call::NOT_EXPECTED));
- TestBluetoothAdapterObserver observer(adapter_);
- SimulateGattConnection(device_);
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(device_->IsGattConnected());
-
- // Discover services.
- std::vector<std::string> service_uuids;
- service_uuids.push_back(unique_service_uuid_.canonical_value());
- // 2 duplicate UUIDs creating 2 instances.
- service_uuids.push_back(duplicate_service_uuid_.canonical_value());
- service_uuids.push_back(duplicate_service_uuid_.canonical_value());
- SimulateGattServicesDiscovered(device_, service_uuids);
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(device_->IsGattServicesDiscoveryComplete());
- }
-
- protected:
- BluetoothUUID unique_service_uuid_;
- BluetoothUUID duplicate_service_uuid_;
-
- BluetoothDevice* device_ = nullptr;
-};
-
TEST(BluetoothDeviceTest, CanonicalizeAddressFormat_AcceptsAllValidFormats) {
// There are three valid separators (':', '-', and none).
// Case shouldn't matter.
@@ -135,7 +95,11 @@ TEST(BluetoothDeviceTest, CanonicalizeAddressFormat_RejectsInvalidFormats) {
}
// Verifies basic device properties, e.g. GetAddress, GetName, ...
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrt, LowEnergyDeviceProperties) {
+#else
TEST_F(BluetoothTest, LowEnergyDeviceProperties) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -162,7 +126,11 @@ TEST_F(BluetoothTest, LowEnergyDeviceProperties) {
}
// Device with no advertised Service UUIDs.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrt, LowEnergyDeviceNoUUIDs) {
+#else
TEST_F(BluetoothTest, LowEnergyDeviceNoUUIDs) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -395,7 +363,11 @@ TEST_F(BluetoothTest, MAYBE_AdvertisementData_Discovery) {
#define MAYBE_GetUUIDs_Connection DISABLED_GetUUIDs_Connection
#endif
// Tests Advertisement Data is updated correctly during a connection.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, GetUUIDs_Connection) {
+#else
TEST_F(BluetoothTest, MAYBE_GetUUIDs_Connection) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -434,8 +406,8 @@ TEST_F(BluetoothTest, MAYBE_GetUUIDs_Connection) {
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess)}),
device->GetUUIDs());
-#if defined(OS_MACOSX)
- // TODO(ortuno): Enable in Android and Windows.
+#if defined(OS_MACOSX) || defined(OS_WIN)
+ // TODO(ortuno): Enable in Android and classic Windows.
// Android and Windows don't yet support service changed events.
// http://crbug.com/548280
// http://crbug.com/579202
@@ -460,7 +432,7 @@ TEST_F(BluetoothTest, MAYBE_GetUUIDs_Connection) {
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess)}),
device->GetUUIDs());
-#endif // defined(OS_MACOSX)
+#endif // defined(OS_MACOSX) || defined(OS_WIN)
observer.Reset();
@@ -897,7 +869,11 @@ TEST_F(BluetoothTest, MAYBE_GetName_NullName) {
#define MAYBE_CreateGattConnection DISABLED_CreateGattConnection
#endif
// Basic CreateGattConnection test.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, CreateGattConnection) {
+#else
TEST_F(BluetoothTest, MAYBE_CreateGattConnection) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -924,7 +900,11 @@ TEST_F(BluetoothTest, MAYBE_CreateGattConnection) {
#define MAYBE_DisconnectionNotifiesDeviceChanged \
DISABLED_DisconnectionNotifiesDeviceChanged
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, DisconnectionNotifiesDeviceChanged) {
+#else
TEST_F(BluetoothTest, MAYBE_DisconnectionNotifiesDeviceChanged) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -956,7 +936,11 @@ TEST_F(BluetoothTest, MAYBE_DisconnectionNotifiesDeviceChanged) {
#endif
// Creates BluetoothGattConnection instances and tests that the interface
// functions even when some Disconnect and the BluetoothDevice is destroyed.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, BluetoothGattConnection) {
+#else
TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -970,6 +954,7 @@ TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection) {
ResetEventCounts();
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
+ base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_connection_attempts_);
SimulateGattConnection(device);
@@ -1077,7 +1062,11 @@ TEST_F(BluetoothTest,
DISABLED_BluetoothGattConnection_AlreadyConnected
#endif
// Calls CreateGattConnection after already connected.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, BluetoothGattConnection_AlreadyConnected) {
+#else
TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection_AlreadyConnected) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1109,8 +1098,13 @@ TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection_AlreadyConnected) {
DISABLED_BluetoothGattConnection_NewConnectionLeavesPreviousDisconnected
#endif
// Creates BluetoothGattConnection after one exists that has disconnected.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly,
+ BluetoothGattConnection_NewConnectionLeavesPreviousDisconnected) {
+#else
TEST_F(BluetoothTest,
MAYBE_BluetoothGattConnection_NewConnectionLeavesPreviousDisconnected) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1150,8 +1144,13 @@ TEST_F(BluetoothTest,
DISABLED_BluetoothGattConnection_DisconnectWhenObjectsDestroyed
#endif
// Deletes BluetoothGattConnection causing disconnection.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly,
+ BluetoothGattConnection_DisconnectWhenObjectsDestroyed) {
+#else
TEST_F(BluetoothTest,
MAYBE_BluetoothGattConnection_DisconnectWhenObjectsDestroyed) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1182,7 +1181,11 @@ TEST_F(BluetoothTest,
DISABLED_BluetoothGattConnection_DisconnectInProgress
#endif
// Starts process of disconnecting and then calls BluetoothGattConnection.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, BluetoothGattConnection_DisconnectInProgress) {
+#else
TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection_DisconnectInProgress) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1231,7 +1234,11 @@ TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection_DisconnectInProgress) {
#endif
// Calls CreateGattConnection but receives notice that the device disconnected
// before it ever connects.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, BluetoothGattConnection_SimulateDisconnect) {
+#else
TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection_SimulateDisconnect) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1243,6 +1250,7 @@ TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection_SimulateDisconnect) {
ResetEventCounts();
device->CreateGattConnection(GetGattConnectionCallback(Call::NOT_EXPECTED),
GetConnectErrorCallback(Call::EXPECTED));
+ base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_connection_attempts_);
SimulateGattDisconnection(device);
base::RunLoop().RunUntilIdle();
@@ -1259,8 +1267,13 @@ TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection_SimulateDisconnect) {
DISABLED_BluetoothGattConnection_DisconnectGatt_SimulateConnect
#endif
// Calls CreateGattConnection & DisconnectGatt, then simulates connection.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly,
+ BluetoothGattConnection_DisconnectGatt_SimulateConnect) {
+#else
TEST_F(BluetoothTest,
MAYBE_BluetoothGattConnection_DisconnectGatt_SimulateConnect) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1272,9 +1285,14 @@ TEST_F(BluetoothTest,
ResetEventCounts();
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
- device->DisconnectGatt();
+ base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_connection_attempts_);
+#if !defined(OS_WIN)
+ // On Windows there is currently no way to cancel a pending GATT connection
+ // from the callers site.
+ device->DisconnectGatt();
EXPECT_EQ(1, gatt_disconnection_attempts_);
+#endif
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
@@ -1297,8 +1315,13 @@ TEST_F(BluetoothTest,
DISABLED_BluetoothGattConnection_DisconnectGatt_SimulateDisconnect
#endif
// Calls CreateGattConnection & DisconnectGatt, then simulates disconnection.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly,
+ BluetoothGattConnection_DisconnectGatt_SimulateDisconnect) {
+#else
TEST_F(BluetoothTest,
MAYBE_BluetoothGattConnection_DisconnectGatt_SimulateDisconnect) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1310,6 +1333,7 @@ TEST_F(BluetoothTest,
ResetEventCounts();
device->CreateGattConnection(GetGattConnectionCallback(Call::NOT_EXPECTED),
GetConnectErrorCallback(Call::EXPECTED));
+ base::RunLoop().RunUntilIdle();
device->DisconnectGatt();
EXPECT_EQ(1, gatt_connection_attempts_);
EXPECT_EQ(1, gatt_disconnection_attempts_);
@@ -1329,7 +1353,11 @@ TEST_F(BluetoothTest,
#endif
// Calls CreateGattConnection & DisconnectGatt, then checks that gatt services
// have been cleaned up.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, BluetoothGattConnection_DisconnectGatt_Cleanup) {
+#else
TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection_DisconnectGatt_Cleanup) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1390,7 +1418,11 @@ TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection_DisconnectGatt_Cleanup) {
#endif
// Calls CreateGattConnection, but simulate errors connecting. Also, verifies
// multiple errors should only invoke callbacks once.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, BluetoothGattConnection_ErrorAfterConnection) {
+#else
TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection_ErrorAfterConnection) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1402,15 +1434,17 @@ TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection_ErrorAfterConnection) {
ResetEventCounts();
device->CreateGattConnection(GetGattConnectionCallback(Call::NOT_EXPECTED),
GetConnectErrorCallback(Call::EXPECTED));
+ base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_connection_attempts_);
SimulateGattConnectionError(device, BluetoothDevice::ERROR_AUTH_FAILED);
SimulateGattConnectionError(device, BluetoothDevice::ERROR_FAILED);
base::RunLoop().RunUntilIdle();
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_WIN)
// TODO: Change to ERROR_AUTH_FAILED. We should be getting a callback
// only with the first error, but our android framework doesn't yet
// support sending different errors.
// http://crbug.com/578191
+ // On Windows, any GattConnectioError will result in ERROR_FAILED.
EXPECT_EQ(BluetoothDevice::ERROR_FAILED, last_connect_error_code_);
#else
EXPECT_EQ(BluetoothDevice::ERROR_AUTH_FAILED, last_connect_error_code_);
@@ -1424,7 +1458,11 @@ TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection_ErrorAfterConnection) {
#else
#define MAYBE_GattServices_ObserversCalls DISABLED_GattServices_ObserversCalls
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, GattServices_ObserversCalls) {
+#else
TEST_F(BluetoothTest, MAYBE_GattServices_ObserversCalls) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1454,7 +1492,11 @@ TEST_F(BluetoothTest, MAYBE_GattServices_ObserversCalls) {
#define MAYBE_GattServicesDiscovered_Success \
DISABLED_GattServicesDiscovered_Success
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, GattServicesDiscovered_Success) {
+#else
TEST_F(BluetoothTest, MAYBE_GattServicesDiscovered_Success) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1558,8 +1600,12 @@ TEST_F(BluetoothTest, MAYBE_GattServicesDiscoveredError_AfterDeleted) {
#define MAYBE_GattServicesDiscovered_AfterDisconnection \
DISABLED_GattServicesDiscovered_AfterDisconnection
#endif
-// Windows does not support disconnection.
+// Classic Windows does not support disconnection.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, GattServicesDiscovered_AfterDisconnection) {
+#else
TEST_F(BluetoothTest, MAYBE_GattServicesDiscovered_AfterDisconnection) {
+#endif
// Tests that we don't crash if there was an error discovering services after
// the device disconnects.
if (!PlatformSupportsLowEnergy()) {
@@ -1596,7 +1642,11 @@ TEST_F(BluetoothTest, MAYBE_GattServicesDiscovered_AfterDisconnection) {
DISABLED_GattServicesDiscoveredError_AfterDisconnection
#endif
// Windows does not support disconnecting.
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, GattServicesDiscoveredError_AfterDisconnection) {
+#else
TEST_F(BluetoothTest, MAYBE_GattServicesDiscoveredError_AfterDisconnection) {
+#endif
// Tests that we don't crash if services are discovered after
// the device disconnects.
if (!PlatformSupportsLowEnergy()) {
@@ -1622,14 +1672,18 @@ TEST_F(BluetoothTest, MAYBE_GattServicesDiscoveredError_AfterDisconnection) {
EXPECT_EQ(0u, device->GetGattServices().size());
}
-#if defined(OS_ANDROID) || defined(OS_WIN) || defined(OS_MACOSX)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetGattServices_and_GetGattService \
GetGattServices_and_GetGattService
#else
#define MAYBE_GetGattServices_and_GetGattService \
DISABLED_GetGattServices_and_GetGattService
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrt, GetGattServices_and_GetGattService) {
+#else
TEST_F(BluetoothTest, MAYBE_GetGattServices_and_GetGattService) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1667,7 +1721,11 @@ TEST_F(BluetoothTest, MAYBE_GetGattServices_and_GetGattService) {
#define MAYBE_GetGattServices_DiscoveryError \
DISABLED_GetGattServices_DiscoveryError
#endif
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrtOnly, GetGattServices_DiscoveryError) {
+#else
TEST_F(BluetoothTest, MAYBE_GetGattServices_DiscoveryError) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
@@ -1707,51 +1765,97 @@ TEST_F(BluetoothTest, GetDeviceTransportType) {
}
#endif // defined(OS_CHROMEOS) || defined(OS_LINUX)
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetPrimaryServices GetPrimaryServices
#else
#define MAYBE_GetPrimaryServices DISABLED_GetPrimaryServices
#endif
-TEST_F(BluetoothGetServiceTest, MAYBE_GetPrimaryServices) {
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrt, GetPrimaryServices) {
+#else
+TEST_F(BluetoothTest, MAYBE_GetPrimaryServices) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
- ASSERT_NO_FATAL_FAILURE(FakeServiceBoilerplate());
- EXPECT_EQ(3u, device_->GetPrimaryServices().size());
+ InitWithFakeAdapter();
+ StartLowEnergyDiscoverySession();
+ BluetoothDevice* device = SimulateLowEnergyDevice(3);
+ EXPECT_FALSE(device->IsConnected());
+
+ // Connect to the device.
+ ResetEventCounts();
+ device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
+ GetConnectErrorCallback(Call::NOT_EXPECTED));
+ SimulateGattConnection(device);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(device->IsGattConnected());
+
+ // Discover services: Two unique UUIDs, of which the second is duplicated.
+ SimulateGattServicesDiscovered(
+ device, {kTestUUIDGenericAccess, kTestUUIDHeartRate, kTestUUIDHeartRate});
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(device->IsGattServicesDiscoveryComplete());
+
+ EXPECT_EQ(3u, device->GetPrimaryServices().size());
}
-#if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetPrimaryServicesByUUID GetPrimaryServicesByUUID
#else
#define MAYBE_GetPrimaryServicesByUUID DISABLED_GetPrimaryServicesByUUID
#endif
-TEST_F(BluetoothGetServiceTest, MAYBE_GetPrimaryServicesByUUID) {
+#if defined(OS_WIN)
+TEST_P(BluetoothTestWinrt, GetPrimaryServicesByUUID) {
+#else
+TEST_F(BluetoothTest, MAYBE_GetPrimaryServicesByUUID) {
+#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
- ASSERT_NO_FATAL_FAILURE(FakeServiceBoilerplate());
+
+ InitWithFakeAdapter();
+ StartLowEnergyDiscoverySession();
+ BluetoothDevice* device = SimulateLowEnergyDevice(3);
+ EXPECT_FALSE(device->IsConnected());
+
+ // Connect to the device.
+ ResetEventCounts();
+ device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
+ GetConnectErrorCallback(Call::NOT_EXPECTED));
+ SimulateGattConnection(device);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(device->IsGattConnected());
+
+ // Discover services: Two unique UUIDs, of which the second is duplicated.
+ SimulateGattServicesDiscovered(
+ device, {kTestUUIDGenericAccess, kTestUUIDHeartRate, kTestUUIDHeartRate});
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(device->IsGattServicesDiscoveryComplete());
{
+ const BluetoothUUID unique_service_uuid(kTestUUIDGenericAccess);
std::vector<BluetoothRemoteGattService*> services =
- device_->GetPrimaryServicesByUUID(unique_service_uuid_);
+ device->GetPrimaryServicesByUUID(unique_service_uuid);
EXPECT_EQ(1u, services.size());
- EXPECT_EQ(unique_service_uuid_, services[0]->GetUUID());
+ EXPECT_EQ(unique_service_uuid, services[0]->GetUUID());
}
{
+ const BluetoothUUID duplicate_service_uuid(kTestUUIDHeartRate);
std::vector<BluetoothRemoteGattService*> services =
- device_->GetPrimaryServicesByUUID(duplicate_service_uuid_);
+ device->GetPrimaryServicesByUUID(duplicate_service_uuid);
EXPECT_EQ(2u, services.size());
- EXPECT_EQ(duplicate_service_uuid_, services[0]->GetUUID());
- EXPECT_EQ(duplicate_service_uuid_, services[1]->GetUUID());
+ EXPECT_EQ(duplicate_service_uuid, services[0]->GetUUID());
+ EXPECT_EQ(duplicate_service_uuid, services[1]->GetUUID());
- EXPECT_TRUE(device_
- ->GetPrimaryServicesByUUID(BluetoothUUID(
- BluetoothTestBase::kTestUUIDGenericAttribute))
- .empty());
+ EXPECT_TRUE(
+ device
+ ->GetPrimaryServicesByUUID(BluetoothUUID(kTestUUIDGenericAttribute))
+ .empty());
EXPECT_NE(services[0]->GetIdentifier(), services[1]->GetIdentifier());
}
diff --git a/chromium/device/bluetooth/bluetooth_device_winrt.cc b/chromium/device/bluetooth/bluetooth_device_winrt.cc
index 932f48c65ef..bae8fb99a9a 100644
--- a/chromium/device/bluetooth/bluetooth_device_winrt.cc
+++ b/chromium/device/bluetooth/bluetooth_device_winrt.cc
@@ -4,15 +4,98 @@
#include "device/bluetooth/bluetooth_device_winrt.h"
+#include <windows.foundation.h>
+
+#include <utility>
+
+#include "base/bind_helpers.h"
#include "base/logging.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_remote_gatt_service_winrt.h"
+#include "device/bluetooth/event_utils_winrt.h"
namespace device {
-BluetoothDeviceWinrt::BluetoothDeviceWinrt(BluetoothAdapterWinrt* adapter)
- : BluetoothDevice(adapter) {}
+namespace {
+
+using ABI::Windows::Devices::Bluetooth::BluetoothConnectionStatus;
+using ABI::Windows::Devices::Bluetooth::BluetoothConnectionStatus_Connected;
+using ABI::Windows::Devices::Bluetooth::BluetoothLEDevice;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattCommunicationStatus;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattCommunicationStatus_Success;
+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::IBluetoothLEDevice3;
+using ABI::Windows::Devices::Bluetooth::IBluetoothLEDeviceStatics;
+using ABI::Windows::Foundation::IAsyncOperation;
+using ABI::Windows::Foundation::IClosable;
+using Microsoft::WRL::ComPtr;
+
+void CloseDevice(ComPtr<IBluetoothLEDevice> ble_device) {
+ if (!ble_device)
+ return;
+
+ ComPtr<IClosable> closable;
+ HRESULT hr = ble_device.As(&closable);
+ if (FAILED(hr)) {
+ VLOG(2) << "As IClosable failed: " << logging::SystemErrorCodeToString(hr);
+ return;
+ }
+
+ hr = closable->Close();
+ if (FAILED(hr)) {
+ VLOG(2) << "IClosable::close() failed: "
+ << logging::SystemErrorCodeToString(hr);
+ }
+}
+
+void RemoveConnectionStatusHandler(IBluetoothLEDevice* ble_device,
+ EventRegistrationToken token) {
+ HRESULT hr = ble_device->remove_ConnectionStatusChanged(token);
+ if (FAILED(hr)) {
+ VLOG(2) << "Removing ConnectionStatus Handler failed: "
+ << logging::SystemErrorCodeToString(hr);
+ }
+}
+
+void RemoveGattServicesChangedHandler(IBluetoothLEDevice* ble_device,
+ EventRegistrationToken token) {
+ HRESULT hr = ble_device->remove_GattServicesChanged(token);
+ if (FAILED(hr)) {
+ VLOG(2) << "Removing Gatt Services Changed Handler failed: "
+ << logging::SystemErrorCodeToString(hr);
+ }
+}
+
+} // namespace
+
+BluetoothDeviceWinrt::BluetoothDeviceWinrt(
+ BluetoothAdapterWinrt* adapter,
+ uint64_t raw_address,
+ base::Optional<std::string> local_name)
+ : BluetoothDevice(adapter),
+ raw_address_(raw_address),
+ address_(CanonicalizeAddress(raw_address)),
+ local_name_(std::move(local_name)),
+ weak_ptr_factory_(this) {}
-BluetoothDeviceWinrt::~BluetoothDeviceWinrt() = default;
+BluetoothDeviceWinrt::~BluetoothDeviceWinrt() {
+ CloseDevice(ble_device_);
+ if (!connection_changed_token_)
+ return;
+
+ RemoveConnectionStatusHandler(ble_device_.Get(), *connection_changed_token_);
+}
uint32_t BluetoothDeviceWinrt::GetBluetoothClass() const {
NOTIMPLEMENTED();
@@ -20,8 +103,7 @@ uint32_t BluetoothDeviceWinrt::GetBluetoothClass() const {
}
std::string BluetoothDeviceWinrt::GetAddress() const {
- NOTIMPLEMENTED();
- return std::string();
+ return address_;
}
BluetoothDevice::VendorIDSource BluetoothDeviceWinrt::GetVendorIDSource()
@@ -51,8 +133,17 @@ uint16_t BluetoothDeviceWinrt::GetAppearance() const {
}
base::Optional<std::string> BluetoothDeviceWinrt::GetName() const {
- NOTIMPLEMENTED();
- return base::nullopt;
+ if (!ble_device_)
+ return local_name_;
+
+ HSTRING name;
+ HRESULT hr = ble_device_->get_Name(&name);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting Name failed: " << logging::SystemErrorCodeToString(hr);
+ return local_name_;
+ }
+
+ return base::win::ScopedHString(name).GetAsUTF8();
}
bool BluetoothDeviceWinrt::IsPaired() const {
@@ -61,13 +152,22 @@ bool BluetoothDeviceWinrt::IsPaired() const {
}
bool BluetoothDeviceWinrt::IsConnected() const {
- NOTIMPLEMENTED();
- return false;
+ return IsGattConnected();
}
bool BluetoothDeviceWinrt::IsGattConnected() const {
- NOTIMPLEMENTED();
- return false;
+ if (!ble_device_)
+ return false;
+
+ BluetoothConnectionStatus status;
+ HRESULT hr = ble_device_->get_ConnectionStatus(&status);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting ConnectionStatus failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return false;
+ }
+
+ return status == BluetoothConnectionStatus_Connected;
}
bool BluetoothDeviceWinrt::IsConnectable() const {
@@ -157,12 +257,167 @@ void BluetoothDeviceWinrt::ConnectToServiceInsecurely(
NOTIMPLEMENTED();
}
+// static
+std::string BluetoothDeviceWinrt::CanonicalizeAddress(uint64_t address) {
+ std::string bluetooth_address = BluetoothDevice::CanonicalizeAddress(
+ base::StringPrintf("%012llX", address));
+ DCHECK(!bluetooth_address.empty());
+ return bluetooth_address;
+}
+
void BluetoothDeviceWinrt::CreateGattConnectionImpl() {
- NOTIMPLEMENTED();
+ ComPtr<IBluetoothLEDeviceStatics> device_statics;
+ HRESULT hr = GetBluetoothLEDeviceStaticsActivationFactory(&device_statics);
+ if (FAILED(hr)) {
+ VLOG(2) << "GetBluetoothLEDeviceStaticsActivationFactory failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&BluetoothDeviceWinrt::DidFailToConnectGatt,
+ weak_ptr_factory_.GetWeakPtr(),
+ ConnectErrorCode::ERROR_FAILED));
+ return;
+ }
+
+ // Note: Even though we might have obtained a BluetoothLEDevice instance in
+ // the past, we need to request a new instance as the old device might have
+ // been closed. See also
+ // https://docs.microsoft.com/en-us/windows/uwp/devices-sensors/gatt-client#connecting-to-the-device
+ ComPtr<IAsyncOperation<BluetoothLEDevice*>> from_bluetooth_address_op;
+ hr = device_statics->FromBluetoothAddressAsync(raw_address_,
+ &from_bluetooth_address_op);
+ if (FAILED(hr)) {
+ VLOG(2) << "BluetoothLEDevice::FromBluetoothAddressAsync failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&BluetoothDeviceWinrt::DidFailToConnectGatt,
+ weak_ptr_factory_.GetWeakPtr(),
+ ConnectErrorCode::ERROR_FAILED));
+ return;
+ }
+
+ hr = PostAsyncResults(
+ std::move(from_bluetooth_address_op),
+ base::BindOnce(&BluetoothDeviceWinrt::OnFromBluetoothAddress,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ if (FAILED(hr)) {
+ VLOG(2) << "PostAsyncResults failed: "
+ << logging::SystemErrorCodeToString(hr);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&BluetoothDeviceWinrt::DidFailToConnectGatt,
+ weak_ptr_factory_.GetWeakPtr(),
+ ConnectErrorCode::ERROR_FAILED));
+ }
}
void BluetoothDeviceWinrt::DisconnectGatt() {
- NOTIMPLEMENTED();
+ CloseDevice(ble_device_);
+}
+
+HRESULT BluetoothDeviceWinrt::GetBluetoothLEDeviceStaticsActivationFactory(
+ IBluetoothLEDeviceStatics** statics) const {
+ return base::win::GetActivationFactory<
+ IBluetoothLEDeviceStatics,
+ RuntimeClass_Windows_Devices_Bluetooth_BluetoothLEDevice>(statics);
+}
+
+void BluetoothDeviceWinrt::OnFromBluetoothAddress(
+ ComPtr<IBluetoothLEDevice> ble_device) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ if (!ble_device) {
+ VLOG(2) << "Getting Device From Bluetooth Address failed.";
+ DidFailToConnectGatt(ConnectErrorCode::ERROR_FAILED);
+ return;
+ }
+
+ if (connection_changed_token_) {
+ // As we are about to replace |ble_device_| with |ble_device| we will also
+ // unregister the existing event handler and add a new one to the new
+ // device.
+ RemoveConnectionStatusHandler(ble_device_.Get(),
+ *connection_changed_token_);
+ }
+
+ ble_device_ = std::move(ble_device);
+ connection_changed_token_ = AddTypedEventHandler(
+ ble_device_.Get(), &IBluetoothLEDevice::add_ConnectionStatusChanged,
+ base::BindRepeating(&BluetoothDeviceWinrt::OnConnectionStatusChanged,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ if (gatt_services_changed_token_) {
+ RemoveGattServicesChangedHandler(ble_device_.Get(),
+ *gatt_services_changed_token_);
+ }
+
+ gatt_services_changed_token_ = AddTypedEventHandler(
+ ble_device_.Get(), &IBluetoothLEDevice::add_GattServicesChanged,
+ base::BindRepeating(&BluetoothDeviceWinrt::OnGattServicesChanged,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ gatt_discoverer_ =
+ std::make_unique<BluetoothGattDiscovererWinrt>(ble_device_);
+ // Initiating the GATT discovery will result in a GATT connection attempt as
+ // well and triggers OnConnectionStatusChanged on success.
+ gatt_discoverer_->StartGattDiscovery(
+ base::BindOnce(&BluetoothDeviceWinrt::OnGattDiscoveryComplete,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void BluetoothDeviceWinrt::OnConnectionStatusChanged(
+ IBluetoothLEDevice* ble_device,
+ IInspectable* object) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ if (IsGattConnected()) {
+ DidConnectGatt();
+ } else {
+ gatt_discoverer_.reset();
+ gatt_services_.clear();
+ device_uuids_.ClearServiceUUIDs();
+ SetGattServicesDiscoveryComplete(false);
+ DidDisconnectGatt();
+ }
+}
+
+void BluetoothDeviceWinrt::OnGattServicesChanged(IBluetoothLEDevice* ble_device,
+ IInspectable* object) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ device_uuids_.ClearServiceUUIDs();
+ SetGattServicesDiscoveryComplete(false);
+ adapter_->NotifyDeviceChanged(this);
+ if (IsGattConnected()) {
+ // In order to stop a potential ongoing GATT discovery, the GattDiscoverer
+ // is reset and a new discovery is initiated.
+ gatt_discoverer_ =
+ std::make_unique<BluetoothGattDiscovererWinrt>(ble_device);
+ gatt_discoverer_->StartGattDiscovery(
+ base::BindOnce(&BluetoothDeviceWinrt::OnGattDiscoveryComplete,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+}
+
+void BluetoothDeviceWinrt::OnGattDiscoveryComplete(bool success) {
+ if (!success) {
+ if (!IsGattConnected())
+ DidFailToConnectGatt(ConnectErrorCode::ERROR_FAILED);
+ return;
+ }
+
+ 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));
+ }
+
+ device_uuids_.ReplaceServiceUUIDs(gatt_services_);
+ SetGattServicesDiscoveryComplete(true);
+ adapter_->NotifyGattServicesDiscovered(this);
+ adapter_->NotifyDeviceChanged(this);
+ gatt_discoverer_.reset();
}
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_device_winrt.h b/chromium/device/bluetooth/bluetooth_device_winrt.h
index df656cbd7e4..2b7b4378909 100644
--- a/chromium/device/bluetooth/bluetooth_device_winrt.h
+++ b/chromium/device/bluetooth/bluetooth_device_winrt.h
@@ -5,21 +5,32 @@
#ifndef DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_WINRT_H_
#define DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_WINRT_H_
+#include <windows.devices.bluetooth.h>
+#include <wrl/client.h>
+
+#include <stdint.h>
+
+#include <memory>
#include <string>
#include "base/callback_forward.h"
#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
#include "base/optional.h"
+#include "base/threading/thread_checker.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_export.h"
namespace device {
class BluetoothAdapterWinrt;
+class BluetoothGattDiscovererWinrt;
class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceWinrt : public BluetoothDevice {
public:
- explicit BluetoothDeviceWinrt(BluetoothAdapterWinrt* adapter);
+ BluetoothDeviceWinrt(BluetoothAdapterWinrt* adapter,
+ uint64_t raw_address,
+ base::Optional<std::string> local_name);
~BluetoothDeviceWinrt() override;
// BluetoothDevice:
@@ -64,11 +75,53 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceWinrt : public BluetoothDevice {
const ConnectToServiceCallback& callback,
const ConnectToServiceErrorCallback& error_callback) override;
+ // Returns the |address| in the canonical format: XX:XX:XX:XX:XX:XX, where
+ // each 'X' is a hex digit.
+ static std::string CanonicalizeAddress(uint64_t address);
+
protected:
// BluetoothDevice:
void CreateGattConnectionImpl() override;
void DisconnectGatt() override;
+ // This is declared virtual so that they can be overridden by tests.
+ virtual HRESULT GetBluetoothLEDeviceStaticsActivationFactory(
+ ABI::Windows::Devices::Bluetooth::IBluetoothLEDeviceStatics** statics)
+ const;
+
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice>
+ ble_device_;
+
+ private:
+ void OnFromBluetoothAddress(
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice> ble_device);
+
+ void OnConnectionStatusChanged(
+ ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice* ble_device,
+ IInspectable* object);
+
+ void OnGattServicesChanged(
+ ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice* ble_device,
+ IInspectable* object);
+
+ void OnGattDiscoveryComplete(bool success);
+
+ uint64_t raw_address_;
+ std::string address_;
+ base::Optional<std::string> local_name_;
+
+ std::unique_ptr<BluetoothGattDiscovererWinrt> gatt_discoverer_;
+
+ base::Optional<EventRegistrationToken> connection_changed_token_;
+ base::Optional<EventRegistrationToken> gatt_services_changed_token_;
+
+ THREAD_CHECKER(thread_checker_);
+
+ // Note: This should remain the last member so it'll be destroyed and
+ // invalidate its weak pointers before any other members are destroyed.
+ base::WeakPtrFactory<BluetoothDeviceWinrt> weak_ptr_factory_;
+
DISALLOW_COPY_AND_ASSIGN(BluetoothDeviceWinrt);
};
diff --git a/chromium/device/bluetooth/bluetooth_gatt_characteristic.h b/chromium/device/bluetooth/bluetooth_gatt_characteristic.h
index 94af976b958..2c37124f4f8 100644
--- a/chromium/device/bluetooth/bluetooth_gatt_characteristic.h
+++ b/chromium/device/bluetooth/bluetooth_gatt_characteristic.h
@@ -79,6 +79,9 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothGattCharacteristic {
};
typedef uint32_t Permissions;
+ // Bluetooth Spec Vol 3, Part G, 3.3.3.3 Client Characteristic Configuration.
+ enum class NotificationType { kNotification = 1, kIndication };
+
// The ErrorCallback is used by methods to asynchronously report errors.
typedef base::Callback<void(BluetoothGattService::GattErrorCode)>
ErrorCallback;
diff --git a/chromium/device/bluetooth/bluetooth_gatt_discoverer_winrt.cc b/chromium/device/bluetooth/bluetooth_gatt_discoverer_winrt.cc
new file mode 100644
index 00000000000..aacaf680a62
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_gatt_discoverer_winrt.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/bluetooth/bluetooth_gatt_discoverer_winrt.h"
+
+#include <windows.foundation.collections.h>
+
+#include <utility>
+
+#include "base/logging.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::
+ GattDeviceService;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ GattDeviceServicesResult;
+using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattDeviceService;
+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::Foundation::Collections::IVectorView;
+using Microsoft::WRL::ComPtr;
+
+} // namespace
+
+BluetoothGattDiscovererWinrt::BluetoothGattDiscovererWinrt(
+ ComPtr<IBluetoothLEDevice> ble_device)
+ : ble_device_(std::move(ble_device)), weak_ptr_factory_(this) {}
+
+BluetoothGattDiscovererWinrt::~BluetoothGattDiscovererWinrt() = default;
+
+void BluetoothGattDiscovererWinrt::StartGattDiscovery(
+ GattDiscoveryCallback callback) {
+ callback_ = std::move(callback);
+ ComPtr<IBluetoothLEDevice3> ble_device_3;
+ HRESULT hr = ble_device_.As(&ble_device_3);
+ if (FAILED(hr)) {
+ VLOG(2) << "Obtaining IBluetoothLEDevice3 failed: "
+ << logging::SystemErrorCodeToString(hr);
+ std::move(callback_).Run(false);
+ return;
+ }
+
+ ComPtr<IAsyncOperation<GattDeviceServicesResult*>> get_gatt_services_op;
+ hr = ble_device_3->GetGattServicesAsync(&get_gatt_services_op);
+ if (FAILED(hr)) {
+ VLOG(2) << "BluetoothLEDevice::GetGattServicesAsync failed: "
+ << logging::SystemErrorCodeToString(hr);
+ std::move(callback_).Run(false);
+ return;
+ }
+
+ hr = PostAsyncResults(
+ std::move(get_gatt_services_op),
+ base::BindOnce(&BluetoothGattDiscovererWinrt::OnGetGattServices,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ if (FAILED(hr)) {
+ VLOG(2) << "PostAsyncResults failed: "
+ << logging::SystemErrorCodeToString(hr);
+ std::move(callback_).Run(false);
+ }
+}
+
+const BluetoothGattDiscovererWinrt::GattServiceList&
+BluetoothGattDiscovererWinrt::GetGattServices() const {
+ return gatt_services_;
+}
+
+void BluetoothGattDiscovererWinrt::OnGetGattServices(
+ ComPtr<IGattDeviceServicesResult> services_result) {
+ if (!services_result) {
+ VLOG(2) << "Getting GATT Services failed.";
+ std::move(callback_).Run(false);
+ return;
+ }
+
+ GattCommunicationStatus status;
+ HRESULT hr = services_result->get_Status(&status);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting GATT Communication Status 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".
+ 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);
+ std::move(callback_).Run(false);
+ return;
+ }
+
+ unsigned size;
+ hr = services->get_Size(&size);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting Size of GATT Services 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);
+ if (FAILED(hr)) {
+ VLOG(2) << "GetAt(" << i
+ << ") failed: " << logging::SystemErrorCodeToString(hr);
+ std::move(callback_).Run(false);
+ return;
+ }
+
+ gatt_services_.push_back(std::move(service));
+ }
+
+ 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
new file mode 100644
index 00000000000..6f53c819a35
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_gatt_discoverer_winrt.h
@@ -0,0 +1,65 @@
+// 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_GATT_DISCOVERER_WINRT_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_GATT_DISCOVERER_WINRT_H_
+
+#include <windows.devices.bluetooth.genericattributeprofile.h>
+#include <windows.devices.bluetooth.h>
+#include <wrl/client.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_export.h"
+
+namespace device {
+
+// This class is responsible for discovering and enumerating GATT attributes on
+// the provided BluetoothLEDevice. Callers are expected to instantiate the class
+// and invoke StartGattDiscovery(). Once the discovery completes, the passed-in
+// GattDiscoveryCallback is invoked, and discovered GATT attributes can be
+// obtained by invoking the appropriate getters.
+class DEVICE_BLUETOOTH_EXPORT BluetoothGattDiscovererWinrt {
+ public:
+ using GattDiscoveryCallback = base::OnceCallback<void(bool)>;
+ using GattServiceList = std::vector<
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
+ GenericAttributeProfile::IGattDeviceService>>;
+
+ BluetoothGattDiscovererWinrt(
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice> ble_device);
+ ~BluetoothGattDiscovererWinrt();
+
+ void StartGattDiscovery(GattDiscoveryCallback callback);
+ const GattServiceList& GetGattServices() const;
+
+ private:
+ void OnGetGattServices(
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
+ IGattDeviceServicesResult> services_result);
+
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice>
+ ble_device_;
+ GattDiscoveryCallback callback_;
+ GattServiceList gatt_services_;
+ THREAD_CHECKER(thread_checker_);
+
+ // Note: This should remain the last member so it'll be destroyed and
+ // invalidate its weak pointers before any other members are destroyed.
+ base::WeakPtrFactory<BluetoothGattDiscovererWinrt> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothGattDiscovererWinrt);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_GATT_DISCOVERER_WINRT_H_
diff --git a/chromium/device/bluetooth/bluetooth_local_gatt_characteristic_unittest.cc b/chromium/device/bluetooth/bluetooth_local_gatt_characteristic_unittest.cc
index 6259fb3f184..7ba058e2fb8 100644
--- a/chromium/device/bluetooth/bluetooth_local_gatt_characteristic_unittest.cc
+++ b/chromium/device/bluetooth/bluetooth_local_gatt_characteristic_unittest.cc
@@ -88,6 +88,36 @@ TEST_F(BluetoothLocalGattCharacteristicTest,
}
#if defined(OS_CHROMEOS) || defined(OS_LINUX)
+#define MAYBE_PrepareWriteLocalCharacteristicValue \
+ PrepareWriteLocalCharacteristicValue
+#else
+#define MAYBE_PrepareWriteLocalCharacteristicValue \
+ DISABLED_PrepareWriteLocalCharacteristicValue
+#endif
+TEST_F(BluetoothLocalGattCharacteristicTest,
+ MAYBE_PrepareWriteLocalCharacteristicValue) {
+ const uint64_t kValueToWrite = 0x7331ul;
+ // Clear existing value.
+ SimulateLocalGattCharacteristicValueWriteRequest(
+ device_, write_characteristic_.get(), GetValue(0),
+ GetCallback(Call::EXPECTED), GetCallback(Call::NOT_EXPECTED));
+
+ // Reliable write session is going on.
+ SimulateLocalGattCharacteristicValuePrepareWriteRequest(
+ device_, write_characteristic_.get(), GetValue(402289342ul), 0, true,
+ GetCallback(Call::EXPECTED), GetCallback(Call::NOT_EXPECTED));
+ EXPECT_EQ(0ul, delegate_->last_written_value_);
+ EXPECT_EQ(device_->GetIdentifier(), delegate_->last_seen_device_);
+
+ // Reliable write session ends.
+ SimulateLocalGattCharacteristicValuePrepareWriteRequest(
+ device_, write_characteristic_.get(), GetValue(kValueToWrite), 0, false,
+ GetCallback(Call::EXPECTED), GetCallback(Call::NOT_EXPECTED));
+ EXPECT_EQ(kValueToWrite, delegate_->last_written_value_);
+ EXPECT_EQ(device_->GetIdentifier(), delegate_->last_seen_device_);
+}
+
+#if defined(OS_CHROMEOS) || defined(OS_LINUX)
#define MAYBE_ReadLocalCharacteristicValueFail ReadLocalCharacteristicValueFail
#else
#define MAYBE_ReadLocalCharacteristicValueFail \
diff --git a/chromium/device/bluetooth/bluetooth_local_gatt_service.h b/chromium/device/bluetooth/bluetooth_local_gatt_service.h
index dc8b61a9b5c..68791c09882 100644
--- a/chromium/device/bluetooth/bluetooth_local_gatt_service.h
+++ b/chromium/device/bluetooth/bluetooth_local_gatt_service.h
@@ -14,6 +14,7 @@
#include "base/memory/weak_ptr.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_export.h"
+#include "device/bluetooth/bluetooth_gatt_characteristic.h"
#include "device/bluetooth/bluetooth_gatt_service.h"
#include "device/bluetooth/bluetooth_uuid.h"
@@ -86,6 +87,31 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothLocalGattService
const base::Closure& callback,
const ErrorCallback& error_callback) = 0;
+ // Called when a remote device |device| requests to prepare write the value
+ // of the characteristic |characteristic| starting at offset |offset|.
+ // This method is only called if the characteristic was specified as
+ // reliable writable and any authentication and authorization challenges
+ // were satisfied by the remote device.
+ //
+ // |has_subsequent_request| is true when the reliable write session is still
+ // ongoing, false otherwise. When |has_subsequent_request| is false,
+ // delegates MUST tear down the current reliable write session with |device|
+ // and commit all the prepare writes in that session in order.
+ //
+ // To respond to the request with success the delegate must invoke
+ // |callback|. To respond to the request with failure delegates must invoke
+ // |error_callback|. If neither callback parameter is invoked, the request
+ // will time out and result in an error. Therefore, delegates MUST invoke
+ // either |callback| or |error_callback|.
+ virtual void OnCharacteristicPrepareWriteRequest(
+ const BluetoothDevice* device,
+ const BluetoothLocalGattCharacteristic* characteristic,
+ const std::vector<uint8_t>& value,
+ int offset,
+ bool has_subsequent_request,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) = 0;
+
// Called when a remote device |device| requests to read the value of the
// descriptor |descriptor| starting at offset |offset|.
// This method is only called if the descriptor was specified as
@@ -126,10 +152,12 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothLocalGattService
const ErrorCallback& error_callback) = 0;
// Called when a remote device |device| requests notifications to start for
- // |characteristic|. This is only called if the characteristic has
- // specified the notify or indicate property.
+ // |characteristic|. |notification_type| is either notify or indicate,
+ // depending on the request from |device|. This is only called if the
+ // characteristic has specified the notify or indicate property.
virtual void OnNotificationsStart(
const BluetoothDevice* device,
+ device::BluetoothGattCharacteristic::NotificationType notification_type,
const BluetoothLocalGattCharacteristic* characteristic) = 0;
// Called when a remote device |device| requests notifications to stop for
diff --git a/chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.h b/chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.h
new file mode 100644
index 00000000000..4c46820ed64
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.h
@@ -0,0 +1,62 @@
+// 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_LOW_ENERGY_ADVERTISEMENT_MANAGER_MAC_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_LOW_ENERGY_ADVERTISEMENT_MANAGER_MAC_H_
+
+#include "base/mac/sdk_forward_declarations.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "device/bluetooth/bluetooth_advertisement_mac.h"
+
+namespace device {
+
+// Class used by BluetoothAdapterMac to manage LE advertisements.
+// Currently, only a single concurrent BLE advertisement is supported. Future
+// work will add support for multiple concurrent and rotating advertisements.
+class DEVICE_BLUETOOTH_EXPORT BluetoothLowEnergyAdvertisementManagerMac {
+ public:
+ BluetoothLowEnergyAdvertisementManagerMac();
+ ~BluetoothLowEnergyAdvertisementManagerMac();
+
+ // Initializes the advertisement manager.
+ void Init(scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
+ CBPeripheralManager* peripheral_manager);
+
+ // Registers a new BLE advertisement.
+ void RegisterAdvertisement(
+ std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data,
+ const BluetoothAdapter::CreateAdvertisementCallback& callback,
+ const BluetoothAdapter::AdvertisementErrorCallback& error_callback);
+
+ // Unregisters an existing BLE advertisement.
+ void UnregisterAdvertisement(
+ BluetoothAdvertisementMac* advertisement,
+ const BluetoothAdvertisementMac::SuccessCallback& success_callback,
+ const BluetoothAdvertisementMac::ErrorCallback& error_callback);
+
+ // Called when the peripheral manager state changes.
+ void OnPeripheralManagerStateChanged();
+
+ // Called when advertisement starts with success or failure.
+ void DidStartAdvertising(NSError* error);
+
+ private:
+ void StartAdvertising();
+
+ CBPeripheralManagerState GetPeripheralManagerState();
+
+ scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
+
+ CBPeripheralManager* peripheral_manager_;
+
+ scoped_refptr<BluetoothAdvertisementMac> active_advertisement_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothLowEnergyAdvertisementManagerMac);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_LOW_ENERGY_ADVERTISEMENT_MANAGER_MAC_H_
diff --git a/chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.mm b/chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.mm
new file mode 100644
index 00000000000..c8fe0db8e7b
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac.mm
@@ -0,0 +1,175 @@
+// 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_low_energy_advertisement_manager_mac.h"
+
+#include "base/bind.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/optional.h"
+#include "base/strings/sys_string_conversions.h"
+#include "device/bluetooth/bluetooth_advertisement.h"
+
+namespace device {
+
+BluetoothLowEnergyAdvertisementManagerMac::
+ BluetoothLowEnergyAdvertisementManagerMac() {}
+
+BluetoothLowEnergyAdvertisementManagerMac::
+ ~BluetoothLowEnergyAdvertisementManagerMac() {}
+
+void BluetoothLowEnergyAdvertisementManagerMac::Init(
+ scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
+ CBPeripheralManager* peripheral_manager) {
+ ui_task_runner_ = ui_task_runner;
+ peripheral_manager_ = peripheral_manager;
+}
+
+void BluetoothLowEnergyAdvertisementManagerMac::
+ OnPeripheralManagerStateChanged() {
+ // This function handles several cases:
+ // 1. Error out when registering an advertisement, but the adapter is not
+ // 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.
+
+ if (!active_advertisement_) {
+ return;
+ }
+
+ CBPeripheralManagerState adapter_state = GetPeripheralManagerState();
+ if (adapter_state == CBPeripheralManagerStateUnknown) {
+ // Wait for the adapter to initialize.
+ return;
+ }
+
+ if (active_advertisement_->is_waiting_for_adapter() &&
+ adapter_state < CBPeripheralManagerStatePoweredOn) {
+ DVLOG(1)
+ << "Registration failed. Adapter changed to invalid adapter_state.";
+ BluetoothAdvertisement::ErrorCode error_code =
+ adapter_state == CBPeripheralManagerStatePoweredOff
+ ? BluetoothAdvertisement::ERROR_ADAPTER_POWERED_OFF
+ : BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM;
+ active_advertisement_->OnAdvertisementError(ui_task_runner_.get(),
+ error_code);
+ active_advertisement_ = nullptr;
+ return;
+ }
+
+ if (active_advertisement_->is_advertising() &&
+ adapter_state == CBPeripheralManagerStateResetting) {
+ DVLOG(1) << "Adapter resetting. Invaldating advertisement.";
+ active_advertisement_->OnAdapterReset();
+ active_advertisement_ = nullptr;
+ return;
+ }
+
+ if (active_advertisement_->is_waiting_for_adapter()) {
+ StartAdvertising();
+ }
+}
+
+void BluetoothLowEnergyAdvertisementManagerMac::StartAdvertising() {
+ base::scoped_nsobject<NSMutableArray> service_uuid_array(
+ [[NSMutableArray alloc]
+ initWithCapacity:active_advertisement_->service_uuids().size()]);
+ for (const std::string& service_uuid :
+ active_advertisement_->service_uuids()) {
+ NSString* uuid_string = base::SysUTF8ToNSString(service_uuid);
+ [service_uuid_array addObject:[CBUUID UUIDWithString:uuid_string]];
+ }
+
+ active_advertisement_->OnAdvertisementPending();
+ [peripheral_manager_ startAdvertising:@{
+ CBAdvertisementDataServiceUUIDsKey : service_uuid_array
+ }];
+}
+
+void BluetoothLowEnergyAdvertisementManagerMac::RegisterAdvertisement(
+ std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data,
+ const BluetoothAdapter::CreateAdvertisementCallback& callback,
+ const BluetoothAdapter::AdvertisementErrorCallback& error_callback) {
+ base::Optional<BluetoothAdvertisement::ErrorCode> error_code;
+
+ std::unique_ptr<BluetoothAdvertisement::UUIDList> service_uuids =
+ advertisement_data->service_uuids();
+ if (!service_uuids || advertisement_data->manufacturer_data() ||
+ advertisement_data->solicit_uuids() ||
+ advertisement_data->service_data()) {
+ DVLOG(1) << "macOS only supports advertising service UUIDs.";
+ error_code = BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM;
+ }
+
+ if (active_advertisement_ && active_advertisement_->status() !=
+ BluetoothAdvertisementMac::UNREGISTERED) {
+ DVLOG(1) << "Only one active BLE advertisement is currently supported.";
+ error_code = BluetoothAdvertisement::ERROR_ADVERTISEMENT_ALREADY_EXISTS;
+ }
+
+ if (error_code) {
+ ui_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(std::move(error_callback), *error_code));
+ return;
+ }
+
+ active_advertisement_ = new BluetoothAdvertisementMac(
+ std::move(service_uuids), callback, error_callback, this);
+ OnPeripheralManagerStateChanged();
+}
+
+void BluetoothLowEnergyAdvertisementManagerMac::UnregisterAdvertisement(
+ BluetoothAdvertisementMac* advertisement,
+ const BluetoothAdvertisement::SuccessCallback& success_callback,
+ const BluetoothAdvertisement::ErrorCallback& error_callback) {
+ if (advertisement != active_advertisement_.get()) {
+ DVLOG(1) << "Cannot unregister none-active advertisement.";
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(error_callback),
+ BluetoothAdvertisement::ERROR_RESET_ADVERTISING));
+ return;
+ }
+
+ active_advertisement_ = nullptr;
+ [peripheral_manager_ stopAdvertising];
+ ui_task_runner_->PostTask(FROM_HERE,
+ base::BindOnce(std::move(success_callback)));
+}
+
+void BluetoothLowEnergyAdvertisementManagerMac::DidStartAdvertising(
+ NSError* error) {
+ DCHECK(active_advertisement_ &&
+ active_advertisement_->is_advertisement_pending());
+ if (!active_advertisement_ ||
+ !active_advertisement_->is_advertisement_pending()) {
+ return;
+ }
+
+ if (error != nil) {
+ DVLOG(1) << "Error advertising: "
+ << base::SysNSStringToUTF8(error.localizedDescription);
+ active_advertisement_->OnAdvertisementError(
+ ui_task_runner_.get(),
+ BluetoothAdvertisement::ERROR_STARTING_ADVERTISEMENT);
+ active_advertisement_ = nullptr;
+ return;
+ }
+
+ active_advertisement_->OnAdvertisementSuccess(ui_task_runner_.get());
+}
+
+CBPeripheralManagerState
+BluetoothLowEnergyAdvertisementManagerMac::GetPeripheralManagerState() {
+#if defined(MAC_OS_X_VERSION_10_13)
+ // The state enum of this API changed in SDK 10.13, so for backwards
+ // compatibility, we need to cast the state to a CBPeripheralManagerState.
+ return static_cast<CBPeripheralManagerState>([peripheral_manager_ state]);
+#else
+ return [peripheral_manager_ state];
+#endif
+}
+
+} // device
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
new file mode 100644
index 00000000000..68ac8dc27de
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_low_energy_advertisement_manager_mac_unittest.mm
@@ -0,0 +1,314 @@
+// 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_low_energy_advertisement_manager_mac.h"
+
+#import <CoreBluetooth/CoreBluetooth.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/ref_counted.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/test_simple_task_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/ocmock/OCMock/OCMock.h"
+
+namespace {
+const char kTestUUID[] = "00000000-1111-2222-3333-444444444444";
+} // namespace
+
+namespace device {
+
+class BluetoothLowEnergyAdvertisementManagerMacTest : public testing::Test {
+ public:
+ BluetoothLowEnergyAdvertisementManagerMacTest()
+ : ui_task_runner_(new base::TestSimpleTaskRunner()),
+ peripheral_manager_state_(CBPeripheralManagerStatePoweredOn),
+ unregister_success_(false) {}
+
+ void SetUp() override {
+ peripheral_manager_ = OCMClassMock([CBPeripheralManager class]);
+ peripheral_manager_mock_ = peripheral_manager_;
+ OCMStub([peripheral_manager_ state]).andDo(^(NSInvocation* invocation) {
+ [invocation setReturnValue:&peripheral_manager_state_];
+ });
+
+ advertisement_manager_.Init(ui_task_runner_, peripheral_manager_);
+ }
+
+ void OnAdvertisementRegistered(
+ scoped_refptr<BluetoothAdvertisement> advertisement) {
+ ASSERT_FALSE(advertisement_.get());
+ advertisement_ = advertisement;
+ }
+
+ void OnAdvertisementRegisterError(
+ BluetoothAdvertisement::ErrorCode error_code) {
+ ASSERT_FALSE(registration_error_.get());
+ registration_error_.reset(
+ new BluetoothAdvertisement::ErrorCode(error_code));
+ }
+
+ void OnUnregisterSuccess() {
+ ASSERT_FALSE(unregister_success_);
+ unregister_success_ = true;
+ }
+
+ void OnUnregisterError(BluetoothAdvertisement::ErrorCode error_code) {
+ ASSERT_FALSE(unregister_error_);
+ unregister_error_.reset(new BluetoothAdvertisement::ErrorCode(error_code));
+ }
+
+ std::unique_ptr<BluetoothAdvertisement::Data> CreateAdvertisementData() {
+ std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data =
+ std::make_unique<BluetoothAdvertisement::Data>(
+ BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST);
+
+ std::unique_ptr<BluetoothAdvertisement::UUIDList> uuid_list =
+ std::make_unique<BluetoothAdvertisement::UUIDList>();
+ uuid_list->push_back(kTestUUID);
+
+ advertisement_data->set_service_uuids(std::move(uuid_list));
+
+ return advertisement_data;
+ }
+
+ void RegisterAdvertisement(
+ std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data) {
+ advertisement_manager_.RegisterAdvertisement(
+ std::move(advertisement_data),
+ base::Bind(&BluetoothLowEnergyAdvertisementManagerMacTest::
+ OnAdvertisementRegistered,
+ base::Unretained(this)),
+ base::Bind(&BluetoothLowEnergyAdvertisementManagerMacTest::
+ OnAdvertisementRegisterError,
+ base::Unretained(this)));
+ }
+
+ void Unregister(scoped_refptr<BluetoothAdvertisement> advertisement) {
+ advertisement->Unregister(
+ base::Bind(
+ &BluetoothLowEnergyAdvertisementManagerMacTest::OnUnregisterSuccess,
+ base::Unretained(this)),
+ base::Bind(
+ &BluetoothLowEnergyAdvertisementManagerMacTest::OnUnregisterError,
+ base::Unretained(this)));
+ }
+
+ protected:
+ scoped_refptr<base::TestSimpleTaskRunner> ui_task_runner_;
+ BluetoothLowEnergyAdvertisementManagerMac advertisement_manager_;
+ CBPeripheralManager* peripheral_manager_;
+ id peripheral_manager_mock_;
+ CBPeripheralManagerState peripheral_manager_state_;
+
+ scoped_refptr<BluetoothAdvertisement> advertisement_;
+ std::unique_ptr<BluetoothAdvertisement::ErrorCode> registration_error_;
+
+ bool unregister_success_;
+ std::unique_ptr<BluetoothAdvertisement::ErrorCode> unregister_error_;
+};
+
+TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest,
+ Register_AdapterPoweredOff) {
+ // Simulate adapter being powered off.
+ peripheral_manager_state_ = CBPeripheralManagerStatePoweredOff;
+
+ RegisterAdvertisement(CreateAdvertisementData());
+ ui_task_runner_->RunPendingTasks();
+
+ EXPECT_FALSE(advertisement_);
+ ASSERT_TRUE(registration_error_);
+ EXPECT_EQ(BluetoothAdvertisement::ERROR_ADAPTER_POWERED_OFF,
+ *registration_error_);
+}
+
+TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest,
+ Register_AdapterInitializing_ThenUnsupported) {
+ // Simulate adapter state being unknown and is initializing.
+ peripheral_manager_state_ = CBPeripheralManagerStateUnknown;
+
+ // The advertisement will not start or fail until the adapter state is
+ // initialized.
+ RegisterAdvertisement(CreateAdvertisementData());
+ ui_task_runner_->RunPendingTasks();
+ EXPECT_FALSE(advertisement_);
+ EXPECT_FALSE(registration_error_);
+
+ // Change the adapter state to CBPeripheralManagerStateUnsupported, which
+ // causes the registration to fail.
+ peripheral_manager_state_ = CBPeripheralManagerStateUnsupported;
+ advertisement_manager_.OnPeripheralManagerStateChanged();
+ ui_task_runner_->RunPendingTasks();
+ EXPECT_FALSE(advertisement_);
+ ASSERT_TRUE(registration_error_);
+ EXPECT_EQ(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM,
+ *registration_error_);
+}
+
+TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest,
+ Register_AdapterInitializing_ThenPoweredOff) {
+ // Simulate adapter state being unknown and is initializing.
+ peripheral_manager_state_ = CBPeripheralManagerStateUnknown;
+
+ // The advertisement will not start or fail until the adapter state is
+ // initialized.
+ RegisterAdvertisement(CreateAdvertisementData());
+ ui_task_runner_->RunPendingTasks();
+ EXPECT_FALSE(advertisement_);
+ EXPECT_FALSE(registration_error_);
+
+ // Change the adapter state to CBPeripheralManagerStateUnsupported, which
+ // causes the registration to fail.
+ peripheral_manager_state_ = CBPeripheralManagerStatePoweredOff;
+ advertisement_manager_.OnPeripheralManagerStateChanged();
+ ui_task_runner_->RunPendingTasks();
+ EXPECT_FALSE(advertisement_);
+ ASSERT_TRUE(registration_error_);
+ EXPECT_EQ(BluetoothAdvertisement::ERROR_ADAPTER_POWERED_OFF,
+ *registration_error_);
+}
+
+TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest,
+ Register_ExistingAdvertisement) {
+ // Start an advertisement.
+ RegisterAdvertisement(CreateAdvertisementData());
+ advertisement_manager_.DidStartAdvertising(nil);
+ ui_task_runner_->RunPendingTasks();
+ ASSERT_TRUE(advertisement_);
+
+ // Only one advertisement is supported.
+ RegisterAdvertisement(CreateAdvertisementData());
+ ui_task_runner_->RunPendingTasks();
+ ASSERT_TRUE(registration_error_);
+ EXPECT_EQ(BluetoothAdvertisement::ERROR_ADVERTISEMENT_ALREADY_EXISTS,
+ *registration_error_);
+}
+
+TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest, Register_InvalidData) {
+ std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data =
+ CreateAdvertisementData();
+
+ // Advertising service data is not supported.
+ std::unique_ptr<BluetoothAdvertisement::ServiceData> service_data =
+ std::make_unique<BluetoothAdvertisement::ServiceData>();
+ (*service_data)[kTestUUID] = std::vector<uint8_t>(10);
+ advertisement_data->set_service_data(std::move(service_data));
+
+ RegisterAdvertisement(std::move(advertisement_data));
+ ui_task_runner_->RunPendingTasks();
+
+ EXPECT_FALSE(advertisement_);
+ ASSERT_TRUE(registration_error_);
+ EXPECT_EQ(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM,
+ *registration_error_);
+}
+
+TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest, Register_Success) {
+ OCMExpect([peripheral_manager_ startAdvertising:[OCMArg any]]);
+
+ RegisterAdvertisement(CreateAdvertisementData());
+ advertisement_manager_.DidStartAdvertising(nil);
+ ui_task_runner_->RunPendingTasks();
+
+ EXPECT_TRUE(advertisement_);
+ EXPECT_FALSE(registration_error_);
+
+ [peripheral_manager_mock_ verifyAtLocation:nil];
+}
+
+TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest, Unregister_Success) {
+ RegisterAdvertisement(CreateAdvertisementData());
+ advertisement_manager_.DidStartAdvertising(nil);
+ ui_task_runner_->RunPendingTasks();
+ ASSERT_TRUE(advertisement_);
+
+ OCMExpect([peripheral_manager_ stopAdvertising]);
+ Unregister(advertisement_);
+ ui_task_runner_->RunPendingTasks();
+
+ EXPECT_TRUE(unregister_success_);
+ EXPECT_FALSE(unregister_error_);
+ [peripheral_manager_mock_ verifyAtLocation:nil];
+}
+
+TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest,
+ Unregister_InvalidAdvertisement) {
+ scoped_refptr<BluetoothAdvertisementMac> invalid_advertisement =
+ new BluetoothAdvertisementMac(
+ std::make_unique<BluetoothAdvertisement::UUIDList>(),
+ base::DoNothing(), base::DoNothing(), &advertisement_manager_);
+
+ // Register advertisement.
+ RegisterAdvertisement(CreateAdvertisementData());
+ advertisement_manager_.DidStartAdvertising(nil);
+ ui_task_runner_->RunPendingTasks();
+ ASSERT_TRUE(advertisement_);
+
+ // Try to unregister the invalid advertisement.
+ Unregister(invalid_advertisement);
+ ui_task_runner_->RunPendingTasks();
+ EXPECT_FALSE(unregister_success_);
+ ASSERT_TRUE(unregister_error_);
+}
+
+TEST_F(BluetoothLowEnergyAdvertisementManagerMacTest, Register_Twice) {
+ // Register one advertisement.
+ RegisterAdvertisement(CreateAdvertisementData());
+ advertisement_manager_.DidStartAdvertising(nil);
+ ui_task_runner_->RunPendingTasks();
+ EXPECT_TRUE(advertisement_);
+
+ // Unregister the advertisement.
+ Unregister(advertisement_);
+ advertisement_ = nullptr;
+
+ // Register another advertisement.
+ RegisterAdvertisement(CreateAdvertisementData());
+ advertisement_manager_.DidStartAdvertising(nil);
+ ui_task_runner_->RunPendingTasks();
+ EXPECT_TRUE(advertisement_);
+}
+
+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());
+ advertisement_manager_.DidStartAdvertising(nil);
+ ui_task_runner_->RunPendingTasks();
+ EXPECT_TRUE(advertisement_);
+
+ // Reset the adapter (i.e. on system crash).
+ peripheral_manager_state_ = CBPeripheralManagerStateResetting;
+ advertisement_manager_.OnPeripheralManagerStateChanged();
+ advertisement_ = nullptr;
+ peripheral_manager_state_ = CBPeripheralManagerStatePoweredOn;
+
+ // Registering another advertisement should succeed.
+ OCMExpect([peripheral_manager_ startAdvertising:[OCMArg any]]);
+ RegisterAdvertisement(CreateAdvertisementData());
+ advertisement_manager_.DidStartAdvertising(nil);
+ ui_task_runner_->RunPendingTasks();
+ EXPECT_TRUE(advertisement_);
+ [peripheral_manager_mock_ verifyAtLocation:nil];
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_low_energy_peripheral_manager_delegate.h b/chromium/device/bluetooth/bluetooth_low_energy_peripheral_manager_delegate.h
new file mode 100644
index 00000000000..aa0a2a58b30
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_low_energy_peripheral_manager_delegate.h
@@ -0,0 +1,33 @@
+// 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_LOW_ENERGY_PERIPHERAL_MANAGER_DELEGATE_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_LOW_ENERGY_PERIPHERAL_MANAGER_DELEGATE_H_
+
+#include "base/mac/sdk_forward_declarations.h"
+#include "base/memory/ref_counted.h"
+#include "build/build_config.h"
+
+#if defined(OS_IOS)
+#import <CoreBluetooth/CoreBluetooth.h>
+#else
+#import <IOBluetooth/IOBluetooth.h>
+#endif
+
+namespace device {
+class BluetoothAdapterMac;
+class BluetoothLowEnergyAdvertisementManagerMac;
+} // namespace device
+
+@interface BluetoothLowEnergyPeripheralManagerDelegate
+ : NSObject<CBPeripheralManagerDelegate>
+
+- (instancetype)
+initWithAdvertisementManager:
+ (device::BluetoothLowEnergyAdvertisementManagerMac*)advertisementManager
+ andAdapter:(device::BluetoothAdapterMac*)adapter;
+
+@end
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_LOW_ENERGY_PERIPHERAL_MANAGER_DELEGATE_H_
diff --git a/chromium/device/bluetooth/bluetooth_low_energy_peripheral_manager_delegate.mm b/chromium/device/bluetooth/bluetooth_low_energy_peripheral_manager_delegate.mm
new file mode 100644
index 00000000000..e1fe94fcc44
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_low_energy_peripheral_manager_delegate.mm
@@ -0,0 +1,72 @@
+// 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_low_energy_peripheral_manager_delegate.h"
+
+#include "device/bluetooth/bluetooth_adapter_mac.h"
+
+namespace device {
+
+// This class exists to bridge between the Objective-C
+// CBPeripheralManagerDelegate class and our BluetoothAdapterMac classes.
+class BluetoothLowEnergyPeripheralManagerBridge {
+ public:
+ BluetoothLowEnergyPeripheralManagerBridge(
+ BluetoothLowEnergyAdvertisementManagerMac* advertisement_manager,
+ BluetoothAdapterMac* adapter)
+ : advertisement_manager_(advertisement_manager), adapter_(adapter) {}
+
+ ~BluetoothLowEnergyPeripheralManagerBridge() {}
+
+ void UpdatedState() {
+ advertisement_manager_->OnPeripheralManagerStateChanged();
+ }
+
+ void DidStartAdvertising(NSError* error) {
+ advertisement_manager_->DidStartAdvertising(error);
+ }
+
+ CBPeripheralManager* GetPeripheralManager() {
+ return adapter_->GetPeripheralManager();
+ }
+
+ private:
+ BluetoothLowEnergyAdvertisementManagerMac* advertisement_manager_;
+ BluetoothAdapterMac* adapter_;
+};
+
+} // namespace device
+
+// Delegate for CBPeripheralManager, which forwards CoreBluetooth callbacks to
+// their appropriate handler.
+@implementation BluetoothLowEnergyPeripheralManagerDelegate {
+ std::unique_ptr<device::BluetoothLowEnergyPeripheralManagerBridge> bridge_;
+}
+
+- (id)initWithAdvertisementManager:
+ (device::BluetoothLowEnergyAdvertisementManagerMac*)
+ advertisementManager
+ andAdapter:(device::BluetoothAdapterMac*)adapter {
+ if ((self = [super init])) {
+ bridge_.reset(new device::BluetoothLowEnergyPeripheralManagerBridge(
+ advertisementManager, adapter));
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [bridge_->GetPeripheralManager() setDelegate:nil];
+ [super dealloc];
+}
+
+- (void)peripheralManagerDidUpdateState:(CBPeripheralManager*)peripheral {
+ bridge_->UpdatedState();
+}
+
+- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager*)peripheral
+ error:(NSError*)error {
+ bridge_->DidStartAdvertising(error);
+}
+
+@end
diff --git a/chromium/device/bluetooth/bluetooth_low_energy_win.cc b/chromium/device/bluetooth/bluetooth_low_energy_win.cc
index 3fa1467dc97..c4d49773559 100644
--- a/chromium/device/bluetooth/bluetooth_low_energy_win.cc
+++ b/chromium/device/bluetooth/bluetooth_low_energy_win.cc
@@ -19,8 +19,6 @@
namespace {
-static device::win::BluetoothLowEnergyWrapper* g_instance_ = nullptr;
-
using device::win::DeviceRegistryPropertyValue;
using device::win::DevicePropertyValue;
using device::win::BluetoothLowEnergyDeviceInfo;
@@ -651,24 +649,6 @@ bool ExtractBluetoothAddressFromDeviceInstanceIdForTesting(
return ExtractBluetoothAddressFromDeviceInstanceId(instance_id, btha, error);
}
-BluetoothLowEnergyWrapper* BluetoothLowEnergyWrapper::GetInstance() {
- if (g_instance_ == nullptr) {
- g_instance_ = new BluetoothLowEnergyWrapper();
- }
- return g_instance_;
-}
-
-void BluetoothLowEnergyWrapper::DeleteInstance() {
- delete g_instance_;
- g_instance_ = nullptr;
-}
-
-void BluetoothLowEnergyWrapper::SetInstanceForTest(
- BluetoothLowEnergyWrapper* instance) {
- delete g_instance_;
- g_instance_ = instance;
-}
-
BluetoothLowEnergyWrapper::BluetoothLowEnergyWrapper() {}
BluetoothLowEnergyWrapper::~BluetoothLowEnergyWrapper() {}
diff --git a/chromium/device/bluetooth/bluetooth_low_energy_win.h b/chromium/device/bluetooth/bluetooth_low_energy_win.h
index da00758ebf1..e582d4cbaa8 100644
--- a/chromium/device/bluetooth/bluetooth_low_energy_win.h
+++ b/chromium/device/bluetooth/bluetooth_low_energy_win.h
@@ -121,9 +121,8 @@ ExtractBluetoothAddressFromDeviceInstanceIdForTesting(
// interface that can be replaced with fakes in tests.
class DEVICE_BLUETOOTH_EXPORT BluetoothLowEnergyWrapper {
public:
- static BluetoothLowEnergyWrapper* GetInstance();
- static void DeleteInstance();
- static void SetInstanceForTest(BluetoothLowEnergyWrapper* instance);
+ BluetoothLowEnergyWrapper();
+ virtual ~BluetoothLowEnergyWrapper();
// Returns true only on Windows platforms supporting Bluetooth Low Energy.
virtual bool IsBluetoothLowEnergySupported();
@@ -210,10 +209,6 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothLowEnergyWrapper {
virtual HRESULT WriteDescriptorValue(base::FilePath& service_path,
const PBTH_LE_GATT_DESCRIPTOR descriptor,
PBTH_LE_GATT_DESCRIPTOR_VALUE new_value);
-
- protected:
- BluetoothLowEnergyWrapper();
- virtual ~BluetoothLowEnergyWrapper();
};
} // namespace win
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic.cc
index d72c8f80bec..e84ab7f8204 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic.cc
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic.cc
@@ -4,6 +4,8 @@
#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
+#include <utility>
+
#include "base/bind.h"
#include "base/location.h"
#include "base/single_thread_task_runner.h"
@@ -23,14 +25,29 @@ BluetoothRemoteGattCharacteristic::~BluetoothRemoteGattCharacteristic() {
}
std::vector<BluetoothRemoteGattDescriptor*>
+BluetoothRemoteGattCharacteristic::GetDescriptors() const {
+ std::vector<BluetoothRemoteGattDescriptor*> descriptors;
+ descriptors.reserve(descriptors_.size());
+ for (const auto& pair : descriptors_)
+ descriptors.push_back(pair.second.get());
+ return descriptors;
+}
+
+BluetoothRemoteGattDescriptor* BluetoothRemoteGattCharacteristic::GetDescriptor(
+ const std::string& identifier) const {
+ auto iter = descriptors_.find(identifier);
+ return iter != descriptors_.end() ? iter->second.get() : nullptr;
+}
+
+std::vector<BluetoothRemoteGattDescriptor*>
BluetoothRemoteGattCharacteristic::GetDescriptorsByUUID(
const BluetoothUUID& uuid) const {
std::vector<BluetoothRemoteGattDescriptor*> descriptors;
- for (BluetoothRemoteGattDescriptor* descriptor : GetDescriptors()) {
- if (descriptor->GetUUID() == uuid) {
- descriptors.push_back(descriptor);
- }
+ for (const auto& pair : descriptors_) {
+ if (pair.second->GetUUID() == uuid)
+ descriptors.push_back(pair.second.get());
}
+
return descriptors;
}
@@ -71,9 +88,42 @@ void BluetoothRemoteGattCharacteristic::NotifySessionCommand::Cancel() {
void BluetoothRemoteGattCharacteristic::StartNotifySession(
const NotifySessionCallback& callback,
const ErrorCallback& error_callback) {
+ StartNotifySessionInternal(base::nullopt, callback, error_callback);
+}
+
+#if defined(OS_CHROMEOS)
+void BluetoothRemoteGattCharacteristic::StartNotifySession(
+ NotificationType notification_type,
+ const NotifySessionCallback& callback,
+ const ErrorCallback& error_callback) {
+ StartNotifySessionInternal(notification_type, callback, error_callback);
+}
+#endif
+
+bool BluetoothRemoteGattCharacteristic::WriteWithoutResponse(
+ base::span<const uint8_t> value) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool BluetoothRemoteGattCharacteristic::AddDescriptor(
+ std::unique_ptr<BluetoothRemoteGattDescriptor> descriptor) {
+ if (!descriptor)
+ return false;
+
+ auto* descriptor_raw = descriptor.get();
+ return descriptors_
+ .try_emplace(descriptor_raw->GetIdentifier(), std::move(descriptor))
+ .second;
+}
+
+void BluetoothRemoteGattCharacteristic::StartNotifySessionInternal(
+ const base::Optional<NotificationType>& notification_type,
+ const NotifySessionCallback& callback,
+ const ErrorCallback& error_callback) {
NotifySessionCommand* command = new NotifySessionCommand(
base::Bind(&BluetoothRemoteGattCharacteristic::ExecuteStartNotifySession,
- GetWeakPtr(), callback, error_callback),
+ GetWeakPtr(), notification_type, callback, error_callback),
base::Bind(&BluetoothRemoteGattCharacteristic::CancelStartNotifySession,
GetWeakPtr(),
base::Bind(error_callback,
@@ -85,13 +135,8 @@ void BluetoothRemoteGattCharacteristic::StartNotifySession(
}
}
-bool BluetoothRemoteGattCharacteristic::WriteWithoutResponse(
- base::span<const uint8_t> value) {
- NOTIMPLEMENTED();
- return false;
-}
-
void BluetoothRemoteGattCharacteristic::ExecuteStartNotifySession(
+ const base::Optional<NotificationType>& notification_type,
NotifySessionCallback callback,
ErrorCallback error_callback,
NotifySessionCommand::Type previous_command_type,
@@ -117,14 +162,12 @@ void BluetoothRemoteGattCharacteristic::ExecuteStartNotifySession(
}
}
- // Check that the characteristic supports either notifications or
- // indications.
- Properties properties = GetProperties();
- bool hasNotify = (properties & PROPERTY_NOTIFY) != 0;
- bool hasIndicate = (properties & PROPERTY_INDICATE) != 0;
-
- if (!hasNotify && !hasIndicate) {
- LOG(ERROR) << "Characteristic needs NOTIFY or INDICATE";
+ if (!IsNotificationTypeSupported(notification_type)) {
+ if (notification_type)
+ LOG(ERROR) << "Characteristic doesn't support specified "
+ << "notification_type";
+ else
+ LOG(ERROR) << "Characteristic needs NOTIFY or INDICATE";
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
@@ -170,6 +213,11 @@ void BluetoothRemoteGattCharacteristic::ExecuteStartNotifySession(
// do whatever else is needed to get the notifications flowing.
SubscribeToNotifications(
ccc_descriptor[0],
+#if defined(OS_CHROMEOS)
+ notification_type.value_or((GetProperties() & PROPERTY_NOTIFY)
+ ? NotificationType::kNotification
+ : NotificationType::kIndication),
+#endif
base::Bind(
&BluetoothRemoteGattCharacteristic::OnStartNotifySessionSuccess,
GetWeakPtr(), callback),
@@ -337,4 +385,19 @@ void BluetoothRemoteGattCharacteristic::OnStopNotifySessionError(
}
}
+bool BluetoothRemoteGattCharacteristic::IsNotificationTypeSupported(
+ const base::Optional<NotificationType>& notification_type) {
+ Properties properties = GetProperties();
+ bool hasNotify = (properties & PROPERTY_NOTIFY) != 0;
+ bool hasIndicate = (properties & PROPERTY_INDICATE) != 0;
+ if (!notification_type)
+ return hasNotify || hasIndicate;
+ switch (notification_type.value()) {
+ case NotificationType::kNotification:
+ return hasNotify;
+ case NotificationType::kIndication:
+ return hasIndicate;
+ }
+}
+
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic.h b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic.h
index 6191a4380d8..c475063f5eb 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic.h
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic.h
@@ -7,12 +7,14 @@
#include <stdint.h>
+#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/callback_forward.h"
+#include "base/containers/flat_map.h"
#include "base/containers/queue.h"
#include "base/containers/span.h"
#include "base/macros.h"
@@ -26,7 +28,6 @@ namespace device {
class BluetoothGattNotifySession;
class BluetoothRemoteGattDescriptor;
-class BluetoothRemoteGattService;
// BluetoothRemoteGattCharacteristic represents a remote GATT characteristic.
// This class is used to represent GATT characteristics that belong to a service
@@ -50,6 +51,8 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristic
typedef base::Callback<void(std::unique_ptr<BluetoothGattNotifySession>)>
NotifySessionCallback;
+ ~BluetoothRemoteGattCharacteristic() override;
+
// Returns the value of the characteristic. For remote characteristics, this
// is the most recently cached value. For local characteristics, this is the
// most recently updated value or the value retrieved from the delegate.
@@ -60,18 +63,17 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristic
// Returns the list of GATT characteristic descriptors that provide more
// information about this characteristic.
- virtual std::vector<BluetoothRemoteGattDescriptor*> GetDescriptors()
- const = 0;
+ virtual std::vector<BluetoothRemoteGattDescriptor*> GetDescriptors() const;
// Returns the GATT characteristic descriptor with identifier |identifier| if
// it belongs to this GATT characteristic.
virtual BluetoothRemoteGattDescriptor* GetDescriptor(
- const std::string& identifier) const = 0;
+ const std::string& identifier) const;
// Returns the GATT characteristic descriptors that match |uuid|. There may be
// multiple, as illustrated by Core Bluetooth Specification [V4.2 Vol 3 Part G
// 3.3.3.5 Characteristic Presentation Format].
- std::vector<BluetoothRemoteGattDescriptor*> GetDescriptorsByUUID(
+ virtual std::vector<BluetoothRemoteGattDescriptor*> GetDescriptorsByUUID(
const BluetoothUUID& uuid) const;
// Get a weak pointer to the characteristic.
@@ -116,6 +118,17 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristic
// BluetoothGattNotifySession object that you received in |callback|.
virtual void StartNotifySession(const NotifySessionCallback& callback,
const ErrorCallback& error_callback);
+#if defined(OS_CHROMEOS)
+ // TODO(https://crbug.com/849359): This method should also be implemented on
+ // Android and Windows.
+ // macOS does not support specifying a notification type. According to macOS
+ // documentation if the characteristic supports both notify and indicate, only
+ // notifications will be enabled.
+ // https://developer.apple.com/documentation/corebluetooth/cbperipheral/1518949-setnotifyvalue?language=objc#discussion
+ virtual void StartNotifySession(NotificationType notification_type,
+ const NotifySessionCallback& callback,
+ const ErrorCallback& error_callback);
+#endif
// Sends a read request to a remote characteristic to read its value.
// |callback| is called to return the read value on success and
@@ -133,6 +146,19 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristic
const base::Closure& callback,
const ErrorCallback& error_callback) = 0;
+#if defined(OS_CHROMEOS)
+ // Sends a prepare write request to a remote characteristic with the value
+ // |value|. |callback| is called to signal success and |error_callback| for
+ // failures. This method only applies to remote characteristics and will fail
+ // for those that are locally hosted.
+ // Callers should use BluetoothDevice::ExecuteWrite() to commit or
+ // BluetoothDevice::AbortWrite() to abort the change.
+ virtual void PrepareWriteRemoteCharacteristic(
+ const std::vector<uint8_t>& value,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) = 0;
+#endif
+
// Sends a write request to a remote characteristic with the value |value|
// without waiting for a response. This method returns false to signal
// failures. When attempting to write the remote characteristic true is
@@ -144,16 +170,27 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristic
protected:
BluetoothRemoteGattCharacteristic();
- ~BluetoothRemoteGattCharacteristic() override;
// Writes to the Client Characteristic Configuration descriptor to enable
// notifications/indications. This method is meant to be called from
// StartNotifySession and should contain only the code necessary to start
// listening to characteristic notifications on a particular platform.
+#if defined(OS_CHROMEOS)
+ // |notification_type| specifies the type of notifications that will be
+ // enabled: notifications or indications.
+ // TODO(https://crbug.com/849359): This method should also be implemented on
+ // Android and Windows.
virtual void SubscribeToNotifications(
BluetoothRemoteGattDescriptor* ccc_descriptor,
+ NotificationType notification_type,
const base::Closure& callback,
const ErrorCallback& error_callback) = 0;
+#else
+ virtual void SubscribeToNotifications(
+ BluetoothRemoteGattDescriptor* ccc_descriptor,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) = 0;
+#endif
// Writes to the Client Characteristic Configuration descriptor to disable
// notifications/indications. This method is meant to be called from
@@ -164,6 +201,14 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristic
const base::Closure& callback,
const ErrorCallback& error_callback) = 0;
+ // Utility function to add a |descriptor| to the map of |descriptors_|.
+ bool AddDescriptor(std::unique_ptr<BluetoothRemoteGattDescriptor> descriptor);
+
+ // Descriptors owned by the chracteristic. The descriptors' identifiers serve
+ // as keys.
+ base::flat_map<std::string, std::unique_ptr<BluetoothRemoteGattDescriptor>>
+ descriptors_;
+
private:
friend class BluetoothGattNotifySession;
@@ -207,7 +252,12 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristic
void Cancel();
};
+ void StartNotifySessionInternal(
+ const base::Optional<NotificationType>& notification_type,
+ const NotifySessionCallback& callback,
+ const ErrorCallback& error_callback);
void ExecuteStartNotifySession(
+ const base::Optional<NotificationType>& notification_type,
NotifySessionCallback callback,
ErrorCallback error_callback,
NotifySessionCommand::Type previous_command_type,
@@ -232,6 +282,8 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristic
BluetoothGattNotifySession* session,
base::Closure callback,
BluetoothRemoteGattService::GattErrorCode error);
+ bool IsNotificationTypeSupported(
+ const base::Optional<NotificationType>& notification_type);
// Pending StartNotifySession / StopNotifySession calls.
base::queue<std::unique_ptr<NotifySessionCommand>> pending_notify_commands_;
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc
index 787307a8bfb..175a79e5ca9 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc
@@ -106,20 +106,21 @@ BluetoothRemoteGattCharacteristicAndroid::GetPermissions() const {
std::vector<BluetoothRemoteGattDescriptor*>
BluetoothRemoteGattCharacteristicAndroid::GetDescriptors() const {
EnsureDescriptorsCreated();
- std::vector<BluetoothRemoteGattDescriptor*> descriptors;
- for (const auto& map_iter : descriptors_)
- descriptors.push_back(map_iter.second.get());
- return descriptors;
+ return BluetoothRemoteGattCharacteristic::GetDescriptors();
}
BluetoothRemoteGattDescriptor*
BluetoothRemoteGattCharacteristicAndroid::GetDescriptor(
const std::string& identifier) const {
EnsureDescriptorsCreated();
- const auto& iter = descriptors_.find(identifier);
- if (iter == descriptors_.end())
- return nullptr;
- return iter->second.get();
+ return BluetoothRemoteGattCharacteristic::GetDescriptor(identifier);
+}
+
+std::vector<BluetoothRemoteGattDescriptor*>
+BluetoothRemoteGattCharacteristicAndroid::GetDescriptorsByUUID(
+ const BluetoothUUID& uuid) const {
+ EnsureDescriptorsCreated();
+ return BluetoothRemoteGattCharacteristic::GetDescriptorsByUUID(uuid);
}
void BluetoothRemoteGattCharacteristicAndroid::ReadRemoteCharacteristic(
@@ -236,10 +237,9 @@ void BluetoothRemoteGattCharacteristicAndroid::CreateGattRemoteDescriptor(
base::android::ConvertJavaStringToUTF8(env, instanceId);
DCHECK(!base::ContainsKey(descriptors_, instanceIdString));
-
- descriptors_[instanceIdString] = BluetoothRemoteGattDescriptorAndroid::Create(
+ AddDescriptor(BluetoothRemoteGattDescriptorAndroid::Create(
instanceIdString, bluetooth_gatt_descriptor_wrapper,
- chrome_bluetooth_device);
+ chrome_bluetooth_device));
}
void BluetoothRemoteGattCharacteristicAndroid::SubscribeToNotifications(
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_android.h b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_android.h
index a51ffd65934..4ee9306544f 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_android.h
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_android.h
@@ -8,7 +8,8 @@
#include <stdint.h>
#include <memory>
-#include <unordered_map>
+#include <string>
+#include <vector>
#include "base/android/jni_android.h"
#include "base/android/scoped_java_ref.h"
@@ -19,7 +20,6 @@
namespace device {
class BluetoothAdapterAndroid;
-class BluetoothRemoteGattDescriptorAndroid;
class BluetoothRemoteGattServiceAndroid;
// BluetoothRemoteGattCharacteristicAndroid along with its owned Java class
@@ -61,6 +61,8 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristicAndroid
std::vector<BluetoothRemoteGattDescriptor*> GetDescriptors() const override;
BluetoothRemoteGattDescriptor* GetDescriptor(
const std::string& identifier) const override;
+ std::vector<BluetoothRemoteGattDescriptor*> GetDescriptorsByUUID(
+ const BluetoothUUID& uuid) const override;
void ReadRemoteCharacteristic(const ValueCallback& callback,
const ErrorCallback& error_callback) override;
void WriteRemoteCharacteristic(const std::vector<uint8_t>& value,
@@ -138,11 +140,6 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristicAndroid
std::vector<uint8_t> value_;
- // Map of descriptors, keyed by descriptor identifier.
- std::unordered_map<std::string,
- std::unique_ptr<BluetoothRemoteGattDescriptorAndroid>>
- descriptors_;
-
DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattCharacteristicAndroid);
};
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h
index 744e8aa9ea1..6a53225639f 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h
@@ -8,7 +8,9 @@
#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
#import <CoreBluetooth/CoreBluetooth.h>
-#include <unordered_map>
+#include <string>
+#include <utility>
+#include <vector>
#include "base/mac/scoped_nsobject.h"
#include "base/memory/weak_ptr.h"
@@ -39,9 +41,6 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristicMac
const std::vector<uint8_t>& GetValue() const override;
BluetoothRemoteGattService* GetService() const override;
bool IsNotifying() const override;
- std::vector<BluetoothRemoteGattDescriptor*> GetDescriptors() const override;
- BluetoothRemoteGattDescriptor* GetDescriptor(
- const std::string& identifier) const override;
void ReadRemoteCharacteristic(const ValueCallback& callback,
const ErrorCallback& error_callback) override;
void WriteRemoteCharacteristic(const std::vector<uint8_t>& value,
@@ -49,8 +48,6 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristicMac
const ErrorCallback& error_callback) override;
bool WriteWithoutResponse(base::span<const uint8_t> value) override;
- DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattCharacteristicMac);
-
protected:
void SubscribeToNotifications(BluetoothRemoteGattDescriptor* ccc_descriptor,
const base::Closure& callback,
@@ -104,10 +101,10 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristicMac
CBDescriptor* cb_descriptor) const;
bool HasPendingRead() const {
return !read_characteristic_value_callbacks_.first.is_null();
- };
+ }
bool HasPendingWrite() const {
return !write_characteristic_value_callbacks_.first.is_null();
- };
+ }
// Is true if the characteristic has been discovered with all its descriptors
// and discovery_pending_count_ is 0.
bool is_discovery_complete_;
@@ -135,12 +132,10 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristicMac
PendingNotifyCallbacks subscribe_to_notification_callbacks_;
// Stores UnsubscribeFromNotifications request callbacks.
PendingNotifyCallbacks unsubscribe_from_notification_callbacks_;
- // Map of descriptors, keyed by descriptor identifier.
- std::unordered_map<std::string,
- std::unique_ptr<BluetoothRemoteGattDescriptorMac>>
- gatt_descriptor_macs_;
base::WeakPtrFactory<BluetoothRemoteGattCharacteristicMac> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattCharacteristicMac);
};
// Stream operator for logging.
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm
index c2c2ee00d2d..2e91b58f8c6 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm
@@ -129,28 +129,6 @@ bool BluetoothRemoteGattCharacteristicMac::IsNotifying() const {
return [cb_characteristic_ isNotifying] == YES;
}
-std::vector<BluetoothRemoteGattDescriptor*>
-BluetoothRemoteGattCharacteristicMac::GetDescriptors() const {
- std::vector<BluetoothRemoteGattDescriptor*> gatt_descriptors;
- for (const auto& iter : gatt_descriptor_macs_) {
- BluetoothRemoteGattDescriptor* gatt_descriptor =
- static_cast<BluetoothRemoteGattDescriptor*>(iter.second.get());
- gatt_descriptors.push_back(gatt_descriptor);
- }
- return gatt_descriptors;
-}
-
-BluetoothRemoteGattDescriptor*
-BluetoothRemoteGattCharacteristicMac::GetDescriptor(
- const std::string& identifier) const {
- auto searched_pair = gatt_descriptor_macs_.find(identifier);
- if (searched_pair == gatt_descriptor_macs_.end()) {
- return nullptr;
- }
- return static_cast<BluetoothRemoteGattDescriptor*>(
- searched_pair->second.get());
-}
-
void BluetoothRemoteGattCharacteristicMac::ReadRemoteCharacteristic(
const ValueCallback& callback,
const ErrorCallback& error_callback) {
@@ -389,7 +367,7 @@ void BluetoothRemoteGattCharacteristicMac::DidDiscoverDescriptors() {
VLOG(1) << *this << ": Did discover descriptors.";
--discovery_pending_count_;
std::unordered_set<std::string> descriptor_identifier_to_remove;
- for (const auto& iter : gatt_descriptor_macs_) {
+ for (const auto& iter : descriptors_) {
descriptor_identifier_to_remove.insert(iter.first);
}
@@ -404,21 +382,19 @@ void BluetoothRemoteGattCharacteristicMac::DidDiscoverDescriptors() {
}
gatt_descriptor_mac =
new BluetoothRemoteGattDescriptorMac(this, cb_descriptor);
- const std::string& identifier = gatt_descriptor_mac->GetIdentifier();
- auto result_iter = gatt_descriptor_macs_.insert(
- {identifier, base::WrapUnique(gatt_descriptor_mac)});
- DCHECK(result_iter.second);
+ bool result = AddDescriptor(base::WrapUnique(gatt_descriptor_mac));
+ DCHECK(result);
GetMacAdapter()->NotifyGattDescriptorAdded(gatt_descriptor_mac);
VLOG(1) << *gatt_descriptor_mac << ": New descriptor.";
}
for (const std::string& identifier : descriptor_identifier_to_remove) {
- auto pair_to_remove = gatt_descriptor_macs_.find(identifier);
- std::unique_ptr<BluetoothRemoteGattDescriptorMac> descriptor_to_remove;
- VLOG(1) << *descriptor_to_remove << ": Removed descriptor.";
- pair_to_remove->second.swap(descriptor_to_remove);
- gatt_descriptor_macs_.erase(pair_to_remove);
- GetMacAdapter()->NotifyGattDescriptorRemoved(descriptor_to_remove.get());
+ auto iter = descriptors_.find(identifier);
+ auto pair = std::move(*iter);
+ VLOG(1) << static_cast<BluetoothRemoteGattDescriptorMac&>(*pair.second)
+ << ": Removed descriptor.";
+ descriptors_.erase(iter);
+ GetMacAdapter()->NotifyGattDescriptorRemoved(pair.second.get());
}
is_discovery_complete_ = discovery_pending_count_ == 0;
}
@@ -471,19 +447,14 @@ bool BluetoothRemoteGattCharacteristicMac::IsDiscoveryComplete() const {
BluetoothRemoteGattDescriptorMac*
BluetoothRemoteGattCharacteristicMac::GetBluetoothRemoteGattDescriptorMac(
CBDescriptor* cb_descriptor) const {
- auto found = std::find_if(
- gatt_descriptor_macs_.begin(), gatt_descriptor_macs_.end(),
- [cb_descriptor](
- const std::pair<const std::string,
- std::unique_ptr<BluetoothRemoteGattDescriptorMac>>&
- pair) {
- return pair.second->GetCBDescriptor() == cb_descriptor;
- });
- if (found == gatt_descriptor_macs_.end()) {
- return nullptr;
- } else {
- return found->second.get();
+ for (const auto& pair : descriptors_) {
+ auto* descriptor_mac =
+ static_cast<BluetoothRemoteGattDescriptorMac*>(pair.second.get());
+ if (descriptor_mac->GetCBDescriptor() == cb_descriptor)
+ return descriptor_mac;
}
+
+ return nullptr;
}
DEVICE_BLUETOOTH_EXPORT std::ostream& operator<<(
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_win.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_win.cc
index bca5dd4efad..53945e43eab 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_win.cc
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_win.cc
@@ -20,10 +20,10 @@ namespace device {
BluetoothRemoteGattCharacteristicWin::BluetoothRemoteGattCharacteristicWin(
BluetoothRemoteGattServiceWin* parent_service,
BTH_LE_GATT_CHARACTERISTIC* characteristic_info,
- scoped_refptr<base::SequencedTaskRunner>& ui_task_runner)
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner)
: parent_service_(parent_service),
characteristic_info_(characteristic_info),
- ui_task_runner_(ui_task_runner),
+ ui_task_runner_(std::move(ui_task_runner)),
characteristic_added_notified_(false),
characteristic_value_read_or_write_in_progress_(false),
gatt_event_handle_(nullptr),
@@ -135,23 +135,6 @@ bool BluetoothRemoteGattCharacteristicWin::IsNotifying() const {
return gatt_event_handle_ != nullptr;
}
-std::vector<BluetoothRemoteGattDescriptor*>
-BluetoothRemoteGattCharacteristicWin::GetDescriptors() const {
- std::vector<BluetoothRemoteGattDescriptor*> descriptors;
- for (const auto& descriptor : included_descriptors_)
- descriptors.push_back(descriptor.second.get());
- return descriptors;
-}
-
-BluetoothRemoteGattDescriptor*
-BluetoothRemoteGattCharacteristicWin::GetDescriptor(
- const std::string& identifier) const {
- GattDescriptorMap::const_iterator it = included_descriptors_.find(identifier);
- if (it != included_descriptors_.end())
- return it->second.get();
- return nullptr;
-}
-
void BluetoothRemoteGattCharacteristicWin::ReadRemoteCharacteristic(
const ValueCallback& callback,
const ErrorCallback& error_callback) {
@@ -264,23 +247,26 @@ void BluetoothRemoteGattCharacteristicWin::UpdateIncludedDescriptors(
PBTH_LE_GATT_DESCRIPTOR descriptors,
uint16_t num) {
if (num == 0) {
- included_descriptors_.clear();
+ descriptors_.clear();
return;
}
// First, remove descriptors that no longer exist.
std::vector<std::string> to_be_removed;
- for (const auto& d : included_descriptors_) {
- if (!DoesDescriptorExist(descriptors, num, d.second.get()))
+ for (const auto& d : descriptors_) {
+ if (!DoesDescriptorExist(
+ descriptors, num,
+ static_cast<BluetoothRemoteGattDescriptorWin*>(d.second.get())))
to_be_removed.push_back(d.second->GetIdentifier());
}
- for (auto id : to_be_removed) {
- included_descriptors_[id].reset();
- included_descriptors_.erase(id);
+ for (const auto& id : to_be_removed) {
+ auto iter = descriptors_.find(id);
+ auto pair = std::move(*iter);
+ descriptors_.erase(iter);
}
// Return if no new descriptors have been added.
- if (included_descriptors_.size() == num)
+ if (descriptors_.size() == num)
return;
// Add new descriptors.
@@ -293,20 +279,21 @@ void BluetoothRemoteGattCharacteristicWin::UpdateIncludedDescriptors(
BluetoothRemoteGattDescriptorWin* descriptor =
new BluetoothRemoteGattDescriptorWin(this, win_descriptor_info,
ui_task_runner_);
- included_descriptors_[descriptor->GetIdentifier()] =
- base::WrapUnique(descriptor);
+ AddDescriptor(base::WrapUnique(descriptor));
}
}
}
bool BluetoothRemoteGattCharacteristicWin::IsDescriptorDiscovered(
- BTH_LE_UUID& uuid,
+ const BTH_LE_UUID& uuid,
uint16_t attribute_handle) {
BluetoothUUID bt_uuid =
BluetoothTaskManagerWin::BluetoothLowEnergyUuidToBluetoothUuid(uuid);
- for (const auto& d : included_descriptors_) {
+ for (const auto& d : descriptors_) {
if (bt_uuid == d.second->GetUUID() &&
- attribute_handle == d.second->GetAttributeHandle()) {
+ attribute_handle ==
+ static_cast<BluetoothRemoteGattDescriptorWin*>(d.second.get())
+ ->GetAttributeHandle()) {
return true;
}
}
@@ -409,9 +396,7 @@ void BluetoothRemoteGattCharacteristicWin::GattEventRegistrationCallback(
void BluetoothRemoteGattCharacteristicWin::ClearIncludedDescriptors() {
// Explicitly reset to null to ensure that calling GetDescriptor() on the
// removed descriptor in GattDescriptorRemoved() returns null.
- for (auto& entry : included_descriptors_)
- entry.second.reset();
- included_descriptors_.clear();
+ std::exchange(descriptors_, {});
}
} // namespace device.
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_win.h b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_win.h
index 89361195db6..54d10ae5d80 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_win.h
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_win.h
@@ -6,7 +6,9 @@
#define DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_CHARACTERISTIC_WIN_H_
#include <memory>
-#include <unordered_map>
+#include <string>
+#include <utility>
+#include <vector>
#include "base/memory/weak_ptr.h"
#include "base/sequenced_task_runner.h"
@@ -29,7 +31,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristicWin
BluetoothRemoteGattCharacteristicWin(
BluetoothRemoteGattServiceWin* parent_service,
BTH_LE_GATT_CHARACTERISTIC* characteristic_info,
- scoped_refptr<base::SequencedTaskRunner>& ui_task_runner);
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner);
~BluetoothRemoteGattCharacteristicWin() override;
// Override BluetoothRemoteGattCharacteristic interfaces.
@@ -40,9 +42,6 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristicWin
Properties GetProperties() const override;
Permissions GetPermissions() const override;
bool IsNotifying() const override;
- std::vector<BluetoothRemoteGattDescriptor*> GetDescriptors() const override;
- BluetoothRemoteGattDescriptor* GetDescriptor(
- const std::string& identifier) const override;
void ReadRemoteCharacteristic(const ValueCallback& callback,
const ErrorCallback& error_callback) override;
void WriteRemoteCharacteristic(const std::vector<uint8_t>& value,
@@ -73,7 +72,8 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristicWin
// Checks if the descriptor with |uuid| and |attribute_handle| has already
// been discovered as included descriptor.
- bool IsDescriptorDiscovered(BTH_LE_UUID& uuid, uint16_t attribute_handle);
+ bool IsDescriptorDiscovered(const BTH_LE_UUID& uuid,
+ uint16_t attribute_handle);
// Checks if |descriptor| still exists in this characteristic according to
// newly discovered |num| of |descriptors|.
@@ -104,13 +104,6 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristicWin
std::vector<uint8_t> characteristic_value_;
std::string characteristic_identifier_;
- // The key of GattDescriptorMap is the identitfier of
- // BluetoothRemoteGattDescriptorWin instance.
- typedef std::unordered_map<std::string,
- std::unique_ptr<BluetoothRemoteGattDescriptorWin>>
- GattDescriptorMap;
- GattDescriptorMap included_descriptors_;
-
// Flag indicates if characteristic added notification of this characteristic
// has been sent out to avoid duplicate notification.
bool characteristic_added_notified_;
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.cc
new file mode 100644
index 00000000000..93166b8b1fa
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.cc
@@ -0,0 +1,78 @@
+// 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_characteristic_winrt.h"
+
+#include "base/logging.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+
+namespace device {
+
+BluetoothRemoteGattCharacteristicWinrt::
+ BluetoothRemoteGattCharacteristicWinrt() = default;
+
+BluetoothRemoteGattCharacteristicWinrt::
+ ~BluetoothRemoteGattCharacteristicWinrt() = default;
+
+std::string BluetoothRemoteGattCharacteristicWinrt::GetIdentifier() const {
+ NOTIMPLEMENTED();
+ return std::string();
+}
+
+BluetoothUUID BluetoothRemoteGattCharacteristicWinrt::GetUUID() const {
+ NOTIMPLEMENTED();
+ return BluetoothUUID();
+}
+
+BluetoothGattCharacteristic::Properties
+BluetoothRemoteGattCharacteristicWinrt::GetProperties() const {
+ NOTIMPLEMENTED();
+ return Properties();
+}
+
+BluetoothGattCharacteristic::Permissions
+BluetoothRemoteGattCharacteristicWinrt::GetPermissions() const {
+ NOTIMPLEMENTED();
+ return Permissions();
+}
+
+const std::vector<uint8_t>& BluetoothRemoteGattCharacteristicWinrt::GetValue()
+ const {
+ return value_;
+}
+
+BluetoothRemoteGattService* BluetoothRemoteGattCharacteristicWinrt::GetService()
+ const {
+ NOTIMPLEMENTED();
+ return nullptr;
+}
+
+void BluetoothRemoteGattCharacteristicWinrt::ReadRemoteCharacteristic(
+ const ValueCallback& callback,
+ const ErrorCallback& error_callback) {
+ NOTIMPLEMENTED();
+}
+
+void BluetoothRemoteGattCharacteristicWinrt::WriteRemoteCharacteristic(
+ const std::vector<uint8_t>& value,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ NOTIMPLEMENTED();
+}
+
+void BluetoothRemoteGattCharacteristicWinrt::SubscribeToNotifications(
+ BluetoothRemoteGattDescriptor* ccc_descriptor,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ NOTIMPLEMENTED();
+}
+
+void BluetoothRemoteGattCharacteristicWinrt::UnsubscribeFromNotifications(
+ BluetoothRemoteGattDescriptor* ccc_descriptor,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ NOTIMPLEMENTED();
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.h b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.h
new file mode 100644
index 00000000000..ca1b3c0255e
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_winrt.h
@@ -0,0 +1,63 @@
+// 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_CHARACTERISTIC_WINRT_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_CHARACTERISTIC_WINRT_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "device/bluetooth/bluetooth_export.h"
+#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
+
+namespace device {
+
+class BluetoothRemoteGattDescriptor;
+class BluetoothRemoteGattService;
+class BluetoothUUID;
+
+class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristicWinrt
+ : public BluetoothRemoteGattCharacteristic {
+ public:
+ BluetoothRemoteGattCharacteristicWinrt();
+ ~BluetoothRemoteGattCharacteristicWinrt() override;
+
+ // BluetoothGattCharacteristic:
+ std::string GetIdentifier() const override;
+ BluetoothUUID GetUUID() const override;
+ Properties GetProperties() const override;
+ Permissions GetPermissions() const override;
+
+ // BluetoothRemoteGattCharacteristic:
+ const std::vector<uint8_t>& GetValue() const override;
+ BluetoothRemoteGattService* GetService() const override;
+ void ReadRemoteCharacteristic(const ValueCallback& callback,
+ const ErrorCallback& error_callback) override;
+ void WriteRemoteCharacteristic(const std::vector<uint8_t>& value,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) override;
+
+ protected:
+ // BluetoothRemoteGattCharacteristic:
+ void SubscribeToNotifications(BluetoothRemoteGattDescriptor* ccc_descriptor,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) override;
+ void UnsubscribeFromNotifications(
+ BluetoothRemoteGattDescriptor* ccc_descriptor,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) override;
+
+ private:
+ std::vector<uint8_t> value_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattCharacteristicWinrt);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_CHARACTERISTIC_WINRT_H_
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor.h b/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor.h
index 34a1928a148..816d140dc2c 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor.h
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor.h
@@ -29,6 +29,8 @@ class BluetoothRemoteGattCharacteristic;
class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattDescriptor
: public virtual BluetoothGattDescriptor {
public:
+ ~BluetoothRemoteGattDescriptor() override;
+
// The ValueCallback is used to return the value of a remote characteristic
// descriptor upon a read request.
typedef base::Callback<void(const std::vector<uint8_t>&)> ValueCallback;
@@ -60,7 +62,6 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattDescriptor
protected:
BluetoothRemoteGattDescriptor();
- ~BluetoothRemoteGattDescriptor() override;
private:
DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattDescriptor);
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_service.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_service.cc
index 1df683b80d1..1441f58508c 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_service.cc
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_service.cc
@@ -4,6 +4,8 @@
#include "device/bluetooth/bluetooth_remote_gatt_service.h"
+#include <utility>
+
#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
#include "device/bluetooth/bluetooth_uuid.h"
@@ -14,16 +16,30 @@ BluetoothRemoteGattService::BluetoothRemoteGattService() = default;
BluetoothRemoteGattService::~BluetoothRemoteGattService() = default;
std::vector<BluetoothRemoteGattCharacteristic*>
+BluetoothRemoteGattService::GetCharacteristics() const {
+ std::vector<BluetoothRemoteGattCharacteristic*> characteristics;
+ characteristics.reserve(characteristics_.size());
+ for (const auto& characteristic : characteristics_)
+ characteristics.push_back(characteristic.second.get());
+ return characteristics;
+}
+
+BluetoothRemoteGattCharacteristic*
+BluetoothRemoteGattService::GetCharacteristic(
+ const std::string& identifier) const {
+ auto iter = characteristics_.find(identifier);
+ return iter != characteristics_.end() ? iter->second.get() : nullptr;
+}
+
+std::vector<BluetoothRemoteGattCharacteristic*>
BluetoothRemoteGattService::GetCharacteristicsByUUID(
- const BluetoothUUID& characteristic_uuid) {
+ const BluetoothUUID& characteristic_uuid) const {
std::vector<BluetoothRemoteGattCharacteristic*> result;
- std::vector<BluetoothRemoteGattCharacteristic*> characteristics =
- GetCharacteristics();
- for (auto* characteristic : characteristics) {
- if (characteristic->GetUUID() == characteristic_uuid) {
- result.push_back(characteristic);
- }
+ for (const auto& characteristic : characteristics_) {
+ if (characteristic.second->GetUUID() == characteristic_uuid)
+ result.push_back(characteristic.second.get());
}
+
return result;
}
@@ -35,4 +51,16 @@ void BluetoothRemoteGattService::SetDiscoveryComplete(bool complete) {
discovery_complete_ = complete;
}
+bool BluetoothRemoteGattService::AddCharacteristic(
+ std::unique_ptr<BluetoothRemoteGattCharacteristic> characteristic) {
+ if (!characteristic)
+ return false;
+
+ const auto& characteristic_raw = *characteristic;
+ return characteristics_
+ .try_emplace(characteristic_raw.GetIdentifier(),
+ std::move(characteristic))
+ .second;
+}
+
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_service.h b/chromium/device/bluetooth/bluetooth_remote_gatt_service.h
index d21fe2fb902..97c00b11447 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_service.h
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_service.h
@@ -5,9 +5,11 @@
#ifndef DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_SERVICE_H_
#define DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_SERVICE_H_
+#include <memory>
#include <string>
#include <vector>
+#include "base/containers/flat_map.h"
#include "base/macros.h"
#include "device/bluetooth/bluetooth_export.h"
#include "device/bluetooth/bluetooth_gatt_service.h"
@@ -40,7 +42,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattService
// List of characteristics that belong to this service.
virtual std::vector<BluetoothRemoteGattCharacteristic*> GetCharacteristics()
- const = 0;
+ const;
// List of GATT services that are included by this service.
virtual std::vector<BluetoothRemoteGattService*> GetIncludedServices()
@@ -49,7 +51,12 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattService
// Returns the GATT characteristic with identifier |identifier| if it belongs
// to this GATT service.
virtual BluetoothRemoteGattCharacteristic* GetCharacteristic(
- const std::string& identifier) const = 0;
+ const std::string& identifier) const;
+
+ // List of characteristics that belong to this service and have a UUID equal
+ // to |characteristic_uuid|.
+ virtual std::vector<BluetoothRemoteGattCharacteristic*>
+ GetCharacteristicsByUUID(const BluetoothUUID& characteristic_uuid) const;
// Returns true if all the characteristics have been discovered.
virtual bool IsDiscoveryComplete() const;
@@ -57,12 +64,18 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattService
// Sets characteristic discovery as complete or incomplete.
virtual void SetDiscoveryComplete(bool complete);
- std::vector<BluetoothRemoteGattCharacteristic*> GetCharacteristicsByUUID(
- const BluetoothUUID& characteristic_uuid);
-
protected:
+ using CharacteristicMap =
+ base::flat_map<std::string,
+ std::unique_ptr<BluetoothRemoteGattCharacteristic>>;
+
BluetoothRemoteGattService();
+ bool AddCharacteristic(
+ std::unique_ptr<BluetoothRemoteGattCharacteristic> characteristic);
+
+ CharacteristicMap characteristics_;
+
private:
// Is true if all the characteristics have been discovered.
bool discovery_complete_ = false;
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_service_android.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_service_android.cc
index 80258f41fd9..a8e3957d455 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_service_android.cc
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_service_android.cc
@@ -128,10 +128,7 @@ device::BluetoothDevice* BluetoothRemoteGattServiceAndroid::GetDevice() const {
std::vector<device::BluetoothRemoteGattCharacteristic*>
BluetoothRemoteGattServiceAndroid::GetCharacteristics() const {
EnsureCharacteristicsCreated();
- std::vector<device::BluetoothRemoteGattCharacteristic*> characteristics;
- for (const auto& map_iter : characteristics_)
- characteristics.push_back(map_iter.second.get());
- return characteristics;
+ return BluetoothRemoteGattService::GetCharacteristics();
}
std::vector<device::BluetoothRemoteGattService*>
@@ -144,10 +141,15 @@ device::BluetoothRemoteGattCharacteristic*
BluetoothRemoteGattServiceAndroid::GetCharacteristic(
const std::string& identifier) const {
EnsureCharacteristicsCreated();
- const auto& iter = characteristics_.find(identifier);
- if (iter == characteristics_.end())
- return nullptr;
- return iter->second.get();
+ return BluetoothRemoteGattService::GetCharacteristic(identifier);
+}
+
+std::vector<BluetoothRemoteGattCharacteristic*>
+BluetoothRemoteGattServiceAndroid::GetCharacteristicsByUUID(
+ const BluetoothUUID& characteristic_uuid) const {
+ EnsureCharacteristicsCreated();
+ return BluetoothRemoteGattService::GetCharacteristicsByUUID(
+ characteristic_uuid);
}
bool BluetoothRemoteGattServiceAndroid::IsDiscoveryComplete() const {
@@ -175,11 +177,9 @@ void BluetoothRemoteGattServiceAndroid::CreateGattRemoteCharacteristic(
base::android::ConvertJavaStringToUTF8(env, instance_id);
DCHECK(!base::ContainsKey(characteristics_, instance_id_string));
-
- characteristics_[instance_id_string] =
- BluetoothRemoteGattCharacteristicAndroid::Create(
- adapter_, this, instance_id_string,
- bluetooth_gatt_characteristic_wrapper, chrome_bluetooth_device);
+ AddCharacteristic(BluetoothRemoteGattCharacteristicAndroid::Create(
+ adapter_, this, instance_id_string, bluetooth_gatt_characteristic_wrapper,
+ chrome_bluetooth_device));
}
BluetoothRemoteGattServiceAndroid::BluetoothRemoteGattServiceAndroid(
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_service_android.h b/chromium/device/bluetooth/bluetooth_remote_gatt_service_android.h
index be602a8d3ba..5f767585414 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_service_android.h
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_service_android.h
@@ -8,7 +8,6 @@
#include <map>
#include <memory>
#include <string>
-#include <unordered_map>
#include <vector>
#include "base/android/jni_android.h"
@@ -21,7 +20,6 @@ namespace device {
class BluetoothAdapterAndroid;
class BluetoothDeviceAndroid;
-class BluetoothRemoteGattCharacteristicAndroid;
// BluetoothRemoteGattServiceAndroid along with its owned Java class
// org.chromium.device.bluetooth.ChromeBluetoothRemoteGattService implement
@@ -70,6 +68,8 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattServiceAndroid
const override;
device::BluetoothRemoteGattCharacteristic* GetCharacteristic(
const std::string& identifier) const override;
+ std::vector<BluetoothRemoteGattCharacteristic*> GetCharacteristicsByUUID(
+ const BluetoothUUID& characteristic_uuid) const override;
bool IsDiscoveryComplete() const override;
void SetDiscoveryComplete(bool complete) override;
@@ -107,11 +107,6 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattServiceAndroid
// Adapter unique instance ID.
std::string instance_id_;
- // Map of characteristics, keyed by characteristic identifier.
- std::unordered_map<std::string,
- std::unique_ptr<BluetoothRemoteGattCharacteristicAndroid>>
- characteristics_;
-
DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattServiceAndroid);
};
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_service_mac.h b/chromium/device/bluetooth/bluetooth_remote_gatt_service_mac.h
index e2d6cd2adaa..20bfd7326fd 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_service_mac.h
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_service_mac.h
@@ -7,7 +7,7 @@
#include <stdint.h>
-#include <unordered_map>
+#include <string>
#include <vector>
#include "base/mac/scoped_nsobject.h"
@@ -40,11 +40,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattServiceMac
BluetoothUUID GetUUID() const override;
bool IsPrimary() const override;
BluetoothDevice* GetDevice() const override;
- std::vector<BluetoothRemoteGattCharacteristic*> GetCharacteristics()
- const override;
std::vector<BluetoothRemoteGattService*> GetIncludedServices() const override;
- BluetoothRemoteGattCharacteristic* GetCharacteristic(
- const std::string& identifier) const override;
private:
friend class BluetoothLowEnergyDeviceMac;
@@ -80,10 +76,6 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattServiceMac
BluetoothLowEnergyDeviceMac* bluetooth_device_mac_;
// A service from CBPeripheral.services.
base::scoped_nsobject<CBService> service_;
- // Map of characteristics, keyed by characteristic identifier.
- std::unordered_map<std::string,
- std::unique_ptr<BluetoothRemoteGattCharacteristicMac>>
- gatt_characteristic_macs_;
bool is_primary_;
// Service identifier.
std::string identifier_;
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_service_mac.mm b/chromium/device/bluetooth/bluetooth_remote_gatt_service_mac.mm
index 58c4ecca7b3..a9db0c038e8 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_service_mac.mm
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_service_mac.mm
@@ -49,34 +49,12 @@ BluetoothDevice* BluetoothRemoteGattServiceMac::GetDevice() const {
return bluetooth_device_mac_;
}
-std::vector<BluetoothRemoteGattCharacteristic*>
-BluetoothRemoteGattServiceMac::GetCharacteristics() const {
- std::vector<BluetoothRemoteGattCharacteristic*> gatt_characteristics;
- for (const auto& iter : gatt_characteristic_macs_) {
- BluetoothRemoteGattCharacteristic* gatt_characteristic =
- static_cast<BluetoothRemoteGattCharacteristic*>(iter.second.get());
- gatt_characteristics.push_back(gatt_characteristic);
- }
- return gatt_characteristics;
-}
-
std::vector<BluetoothRemoteGattService*>
BluetoothRemoteGattServiceMac::GetIncludedServices() const {
NOTIMPLEMENTED();
return std::vector<BluetoothRemoteGattService*>();
}
-BluetoothRemoteGattCharacteristic*
-BluetoothRemoteGattServiceMac::GetCharacteristic(
- const std::string& identifier) const {
- auto searched_pair = gatt_characteristic_macs_.find(identifier);
- if (searched_pair == gatt_characteristic_macs_.end()) {
- return nullptr;
- }
- return static_cast<BluetoothRemoteGattCharacteristic*>(
- searched_pair->second.get());
-}
-
void BluetoothRemoteGattServiceMac::DiscoverCharacteristics() {
VLOG(1) << *this << ": DiscoverCharacteristics.";
SetDiscoveryComplete(false);
@@ -96,7 +74,7 @@ void BluetoothRemoteGattServiceMac::DidDiscoverCharacteristics() {
VLOG(1) << *this << ": DidDiscoverCharacteristics.";
--discovery_pending_count_;
std::unordered_set<std::string> characteristic_identifier_to_remove;
- for (const auto& iter : gatt_characteristic_macs_) {
+ for (const auto& iter : characteristics_) {
characteristic_identifier_to_remove.insert(iter.first);
}
@@ -114,10 +92,8 @@ void BluetoothRemoteGattServiceMac::DidDiscoverCharacteristics() {
}
gatt_characteristic_mac =
new BluetoothRemoteGattCharacteristicMac(this, cb_characteristic);
- const std::string& identifier = gatt_characteristic_mac->GetIdentifier();
- auto result_iter = gatt_characteristic_macs_.insert(
- {identifier, base::WrapUnique(gatt_characteristic_mac)});
- DCHECK(result_iter.second);
+ bool result = AddCharacteristic(base::WrapUnique(gatt_characteristic_mac));
+ DCHECK(result);
VLOG(1) << *gatt_characteristic_mac << ": New characteristic, properties "
<< gatt_characteristic_mac->GetProperties();
if (discovery_pending_count_ == 0) {
@@ -127,12 +103,12 @@ void BluetoothRemoteGattServiceMac::DidDiscoverCharacteristics() {
}
for (const std::string& identifier : characteristic_identifier_to_remove) {
- auto pair_to_remove = gatt_characteristic_macs_.find(identifier);
- std::unique_ptr<BluetoothRemoteGattCharacteristicMac>
- characteristic_to_remove;
- pair_to_remove->second.swap(characteristic_to_remove);
- VLOG(1) << *characteristic_to_remove << ": Removed characteristic.";
- gatt_characteristic_macs_.erase(pair_to_remove);
+ auto pair_to_remove = characteristics_.find(identifier);
+ auto characteristic_to_remove = std::move(pair_to_remove->second);
+ VLOG(1) << static_cast<BluetoothRemoteGattCharacteristicMac&>(
+ *characteristic_to_remove)
+ << ": Removed characteristic.";
+ characteristics_.erase(pair_to_remove);
GetMacAdapter()->NotifyGattCharacteristicRemoved(
characteristic_to_remove.get());
}
@@ -160,13 +136,12 @@ void BluetoothRemoteGattServiceMac::SendNotificationIfComplete() {
// Notify when all characteristics have been fully discovered.
SetDiscoveryComplete(
discovery_pending_count_ == 0 &&
- std::find_if_not(
- gatt_characteristic_macs_.begin(), gatt_characteristic_macs_.end(),
- [](const std::pair<
- const std::string,
- std::unique_ptr<BluetoothRemoteGattCharacteristicMac>>& pair) {
- return pair.second->IsDiscoveryComplete();
- }) == gatt_characteristic_macs_.end());
+ std::all_of(characteristics_.begin(), characteristics_.end(),
+ [](const auto& pair) {
+ return static_cast<BluetoothRemoteGattCharacteristicMac*>(
+ pair.second.get())
+ ->IsDiscoveryComplete();
+ }));
if (IsDiscoveryComplete()) {
VLOG(1) << *this << ": Discovery complete.";
GetMacAdapter()->NotifyGattServiceChanged(this);
@@ -188,19 +163,14 @@ CBService* BluetoothRemoteGattServiceMac::GetService() const {
BluetoothRemoteGattCharacteristicMac*
BluetoothRemoteGattServiceMac::GetBluetoothRemoteGattCharacteristicMac(
CBCharacteristic* cb_characteristic) const {
- auto found = std::find_if(
- gatt_characteristic_macs_.begin(), gatt_characteristic_macs_.end(),
- [cb_characteristic](
- const std::pair<
- const std::string,
- std::unique_ptr<BluetoothRemoteGattCharacteristicMac>>& pair) {
- return pair.second->GetCBCharacteristic() == cb_characteristic;
- });
- if (found == gatt_characteristic_macs_.end()) {
- return nullptr;
- } else {
- return found->second.get();
+ for (const auto& pair : characteristics_) {
+ auto* characteristic_mac =
+ static_cast<BluetoothRemoteGattCharacteristicMac*>(pair.second.get());
+ if (characteristic_mac->GetCBCharacteristic() == cb_characteristic)
+ return characteristic_mac;
}
+
+ return nullptr;
}
BluetoothRemoteGattDescriptorMac*
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_service_win.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_service_win.cc
index 7278bf54fc2..24ab750d5b4 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_service_win.cc
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_service_win.cc
@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
#include "device/bluetooth/bluetooth_adapter_win.h"
#include "device/bluetooth/bluetooth_device_win.h"
#include "device/bluetooth/bluetooth_remote_gatt_characteristic_win.h"
@@ -73,14 +74,6 @@ BluetoothDevice* BluetoothRemoteGattServiceWin::GetDevice() const {
return device_;
}
-std::vector<BluetoothRemoteGattCharacteristic*>
-BluetoothRemoteGattServiceWin::GetCharacteristics() const {
- std::vector<BluetoothRemoteGattCharacteristic*> has_characteristics;
- for (const auto& c : included_characteristics_)
- has_characteristics.push_back(c.second.get());
- return has_characteristics;
-}
-
std::vector<BluetoothRemoteGattService*>
BluetoothRemoteGattServiceWin::GetIncludedServices() const {
NOTIMPLEMENTED();
@@ -88,25 +81,14 @@ BluetoothRemoteGattServiceWin::GetIncludedServices() const {
return std::vector<BluetoothRemoteGattService*>();
}
-BluetoothRemoteGattCharacteristic*
-BluetoothRemoteGattServiceWin::GetCharacteristic(
- const std::string& identifier) const {
- GattCharacteristicsMap::const_iterator it =
- included_characteristics_.find(identifier);
- if (it != included_characteristics_.end())
- return it->second.get();
- return nullptr;
-}
-
void BluetoothRemoteGattServiceWin::GattCharacteristicDiscoveryComplete(
BluetoothRemoteGattCharacteristicWin* characteristic) {
DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
- DCHECK(included_characteristics_.find(characteristic->GetIdentifier()) !=
- included_characteristics_.end());
+ DCHECK(base::ContainsKey(characteristics_, characteristic->GetIdentifier()));
discovery_completed_included_characteristics_.insert(
characteristic->GetIdentifier());
- SetDiscoveryComplete(included_characteristics_.size() ==
+ SetDiscoveryComplete(characteristics_.size() ==
discovery_completed_included_characteristics_.size());
adapter_->NotifyGattCharacteristicAdded(characteristic);
NotifyGattServiceDiscoveryCompleteIfNecessary();
@@ -132,7 +114,7 @@ void BluetoothRemoteGattServiceWin::OnGetIncludedCharacteristics(
return;
UpdateIncludedCharacteristics(characteristics.get(), num);
- SetDiscoveryComplete(included_characteristics_.size() ==
+ SetDiscoveryComplete(characteristics_.size() ==
discovery_completed_included_characteristics_.size());
// In case there are new included characterisitics that haven't been
@@ -152,7 +134,7 @@ void BluetoothRemoteGattServiceWin::UpdateIncludedCharacteristics(
PBTH_LE_GATT_CHARACTERISTIC characteristics,
uint16_t num) {
if (num == 0) {
- if (included_characteristics_.size() > 0) {
+ if (!characteristics_.empty()) {
ClearIncludedCharacteristics();
adapter_->NotifyGattServiceChanged(this);
}
@@ -161,20 +143,26 @@ void BluetoothRemoteGattServiceWin::UpdateIncludedCharacteristics(
// First, remove characteristics that no longer exist.
std::vector<std::string> to_be_removed;
- for (const auto& c : included_characteristics_) {
- if (!DoesCharacteristicExist(characteristics, num, c.second.get()))
+ for (const auto& c : characteristics_) {
+ if (!DoesCharacteristicExist(
+ characteristics, num,
+ static_cast<BluetoothRemoteGattCharacteristicWin*>(
+ c.second.get()))) {
to_be_removed.push_back(c.second->GetIdentifier());
+ }
}
for (const auto& id : to_be_removed) {
RemoveIncludedCharacteristic(id);
}
// Update previously known characteristics.
- for (auto& c : included_characteristics_)
- c.second->Update();
+ for (auto& c : characteristics_) {
+ static_cast<BluetoothRemoteGattCharacteristicWin*>(c.second.get())
+ ->Update();
+ }
// Return if no new characteristics have been added.
- if (included_characteristics_.size() == num)
+ if (characteristics_.size() == num)
return;
// Add new characteristics.
@@ -183,11 +171,8 @@ void BluetoothRemoteGattServiceWin::UpdateIncludedCharacteristics(
characteristics[i].AttributeHandle)) {
PBTH_LE_GATT_CHARACTERISTIC info = new BTH_LE_GATT_CHARACTERISTIC();
*info = characteristics[i];
- auto characteristic_object =
- std::make_unique<BluetoothRemoteGattCharacteristicWin>(
- this, info, ui_task_runner_);
- included_characteristics_.emplace(characteristic_object->GetIdentifier(),
- std::move(characteristic_object));
+ AddCharacteristic(std::make_unique<BluetoothRemoteGattCharacteristicWin>(
+ this, info, ui_task_runner_));
}
}
}
@@ -205,9 +190,11 @@ bool BluetoothRemoteGattServiceWin::IsCharacteristicDiscovered(
uint16_t attribute_handle) {
BluetoothUUID bt_uuid =
BluetoothTaskManagerWin::BluetoothLowEnergyUuidToBluetoothUuid(uuid);
- for (const auto& c : included_characteristics_) {
+ for (const auto& c : characteristics_) {
if (bt_uuid == c.second->GetUUID() &&
- attribute_handle == c.second->GetAttributeHandle()) {
+ attribute_handle ==
+ static_cast<BluetoothRemoteGattCharacteristicWin*>(c.second.get())
+ ->GetAttributeHandle()) {
return true;
}
}
@@ -234,17 +221,24 @@ bool BluetoothRemoteGattServiceWin::DoesCharacteristicExist(
void BluetoothRemoteGattServiceWin::RemoveIncludedCharacteristic(
std::string identifier) {
discovery_completed_included_characteristics_.erase(identifier);
- included_characteristics_[identifier].reset();
- included_characteristics_.erase(identifier);
+
+ // Explicitly moving the to be deleted characteristic into a local variable,
+ // so that we can erase the entry from |characteristics_| before calling the
+ // characteristic's destructor. This will ensure that any call to
+ // GetCharacteristics() won't contain an entry corresponding to |identifier|.
+ // Note: `characteristics_.erase(identifier);` would not guarantee this.
+ DCHECK(base::ContainsKey(characteristics_, identifier));
+ auto iter = characteristics_.find(identifier);
+ auto pair = std::move(*iter);
+ characteristics_.erase(iter);
}
void BluetoothRemoteGattServiceWin::ClearIncludedCharacteristics() {
discovery_completed_included_characteristics_.clear();
- // Explicitly reset to null to ensure that calling GetCharacteristic() on the
- // removed characteristic in GattDescriptorRemoved() returns null.
- for (auto& entry : included_characteristics_)
- entry.second.reset();
- included_characteristics_.clear();
+ // Explicitly reset to null to ensure that calling GetCharacteristics() in
+ // GattCharacteristicRemoved() will return an empty collection.
+ // Note: `characteristics_.clear();` would not guarantee this.
+ std::exchange(characteristics_, {});
}
} // namespace device.
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_service_win.h b/chromium/device/bluetooth/bluetooth_remote_gatt_service_win.h
index e44b077f0f3..9ef078b2c52 100644
--- a/chromium/device/bluetooth/bluetooth_remote_gatt_service_win.h
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_service_win.h
@@ -8,7 +8,6 @@
#include <memory>
#include <set>
#include <string>
-#include <unordered_map>
#include <vector>
#include "base/files/file.h"
@@ -44,11 +43,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattServiceWin
BluetoothUUID GetUUID() const override;
bool IsPrimary() const override;
BluetoothDevice* GetDevice() const override;
- std::vector<BluetoothRemoteGattCharacteristic*> GetCharacteristics()
- const override;
std::vector<BluetoothRemoteGattService*> GetIncludedServices() const override;
- BluetoothRemoteGattCharacteristic* GetCharacteristic(
- const std::string& identifier) const override;
// Notify |characteristic| discovery complete, |characteristic| is the
// included characteritic of this service.
@@ -102,14 +97,6 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattServiceWin
// dependent operations.
scoped_refptr<BluetoothTaskManagerWin> task_manager_;
- // The key of GattCharacteristicsMap is the identifier of
- // BluetoothRemoteGattCharacteristicWin instance.
- typedef std::unordered_map<
- std::string,
- std::unique_ptr<BluetoothRemoteGattCharacteristicWin>>
- GattCharacteristicsMap;
- GattCharacteristicsMap included_characteristics_;
-
// The element of the set is the identifier of
// BluetoothRemoteGattCharacteristicWin instance.
std::set<std::string> discovery_completed_included_characteristics_;
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_service_winrt.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_service_winrt.cc
new file mode 100644
index 00000000000..b2de385570f
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_service_winrt.cc
@@ -0,0 +1,95 @@
+// 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_service_winrt.h"
+
+#include <windows.foundation.collections.h>
+
+#include <utility>
+
+#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"
+
+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
+
+// static
+std::unique_ptr<BluetoothRemoteGattServiceWinrt>
+BluetoothRemoteGattServiceWinrt::Create(
+ BluetoothDevice* device,
+ ComPtr<IGattDeviceService> gatt_service) {
+ DCHECK(gatt_service);
+ GUID guid;
+ HRESULT hr = gatt_service->get_Uuid(&guid);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting UUID failed: " << logging::SystemErrorCodeToString(hr);
+ return nullptr;
+ }
+
+ uint16_t attribute_handle;
+ hr = gatt_service->get_AttributeHandle(&attribute_handle);
+ if (FAILED(hr)) {
+ VLOG(2) << "Getting AttributeHandle failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return nullptr;
+ }
+
+ return base::WrapUnique(new BluetoothRemoteGattServiceWinrt(
+ device, std::move(gatt_service), BluetoothUUID(guid), attribute_handle));
+}
+
+BluetoothRemoteGattServiceWinrt::~BluetoothRemoteGattServiceWinrt() = default;
+
+std::string BluetoothRemoteGattServiceWinrt::GetIdentifier() const {
+ return identifier_;
+}
+
+BluetoothUUID BluetoothRemoteGattServiceWinrt::GetUUID() const {
+ return uuid_;
+}
+
+bool BluetoothRemoteGattServiceWinrt::IsPrimary() const {
+ return true;
+}
+
+BluetoothDevice* BluetoothRemoteGattServiceWinrt::GetDevice() const {
+ return device_;
+}
+
+std::vector<BluetoothRemoteGattService*>
+BluetoothRemoteGattServiceWinrt::GetIncludedServices() const {
+ NOTIMPLEMENTED();
+ return {};
+}
+
+BluetoothRemoteGattServiceWinrt::BluetoothRemoteGattServiceWinrt(
+ BluetoothDevice* device,
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
+ GenericAttributeProfile::IGattDeviceService>
+ gatt_service,
+ BluetoothUUID uuid,
+ uint16_t attribute_handle)
+ : device_(device),
+ gatt_service_(std::move(gatt_service)),
+ uuid_(std::move(uuid)),
+ attribute_handle_(attribute_handle),
+ identifier_(base::StringPrintf("%s/%s_%04x",
+ device_->GetIdentifier().c_str(),
+ uuid_.value().c_str(),
+ 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
new file mode 100644
index 00000000000..c04a73afef0
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_service_winrt.h
@@ -0,0 +1,63 @@
+// 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_SERVICE_WINRT_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_SERVICE_WINRT_H_
+
+#include <windows.devices.bluetooth.genericattributeprofile.h>
+#include <wrl/client.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "device/bluetooth/bluetooth_export.h"
+#include "device/bluetooth/bluetooth_remote_gatt_service.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+
+namespace device {
+
+class BluetoothDevice;
+
+class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattServiceWinrt
+ : public BluetoothRemoteGattService {
+ public:
+ static std::unique_ptr<BluetoothRemoteGattServiceWinrt> Create(
+ BluetoothDevice* device,
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
+ GenericAttributeProfile::IGattDeviceService>
+ gatt_service);
+ ~BluetoothRemoteGattServiceWinrt() override;
+
+ // BluetoothRemoteGattService:
+ std::string GetIdentifier() const override;
+ BluetoothUUID GetUUID() const override;
+ bool IsPrimary() const override;
+ BluetoothDevice* GetDevice() const override;
+ std::vector<BluetoothRemoteGattService*> GetIncludedServices() const override;
+
+ private:
+ BluetoothRemoteGattServiceWinrt(
+ BluetoothDevice* device,
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
+ GenericAttributeProfile::IGattDeviceService>
+ gatt_service,
+ BluetoothUUID uuid,
+ uint16_t attribute_handle);
+
+ BluetoothDevice* device_;
+ Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::
+ GenericAttributeProfile::IGattDeviceService>
+ gatt_service_;
+ BluetoothUUID uuid_;
+ uint16_t attribute_handle_;
+ std::string identifier_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattServiceWinrt);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_SERVICE_WINRT_H_
diff --git a/chromium/device/bluetooth/bluetooth_task_manager_win.cc b/chromium/device/bluetooth/bluetooth_task_manager_win.cc
index 8b4fd5cd77d..2f9f7aeba3a 100644
--- a/chromium/device/bluetooth/bluetooth_task_manager_win.cc
+++ b/chromium/device/bluetooth/bluetooth_task_manager_win.cc
@@ -90,22 +90,18 @@ bool BluetoothUUIDToWinBLEUUID(const device::BluetoothUUID& uuid,
return true;
}
-// Populates bluetooth adapter state using adapter_handle.
-void GetAdapterState(HANDLE adapter_handle,
+// Populates bluetooth adapter state from the currently open adapter.
+void GetAdapterState(device::win::BluetoothClassicWrapper* classic_wrapper,
device::BluetoothTaskManagerWin::AdapterState* state) {
std::string name;
std::string address;
bool powered = false;
BLUETOOTH_RADIO_INFO adapter_info = {sizeof(BLUETOOTH_RADIO_INFO)};
- if (adapter_handle &&
- ERROR_SUCCESS ==
- device::win::BluetoothClassicWrapper::GetInstance()->GetRadioInfo(
- adapter_handle, &adapter_info)) {
+ if (classic_wrapper->HasHandle() &&
+ ERROR_SUCCESS == classic_wrapper->GetRadioInfo(&adapter_info)) {
name = base::SysWideToUTF8(adapter_info.szName);
address = BluetoothAddressToCanonicalString(adapter_info.address);
- powered =
- !!device::win::BluetoothClassicWrapper::GetInstance()->IsConnectable(
- adapter_handle);
+ powered = !!classic_wrapper->IsConnectable();
}
state->name = name;
state->address = address;
@@ -211,16 +207,32 @@ BluetoothTaskManagerWin::DeviceState::~DeviceState() {
BluetoothTaskManagerWin::BluetoothTaskManagerWin(
scoped_refptr<base::SequencedTaskRunner> ui_task_runner)
- : ui_task_runner_(ui_task_runner),
- adapter_handle_(NULL),
- discovering_(false),
- current_logging_batch_count_(0) {}
-
-BluetoothTaskManagerWin::~BluetoothTaskManagerWin() {
- win::BluetoothLowEnergyWrapper::DeleteInstance();
- win::BluetoothClassicWrapper::DeleteInstance();
+ : ui_task_runner_(std::move(ui_task_runner)),
+ classic_wrapper_(std::make_unique<win::BluetoothClassicWrapper>()),
+ le_wrapper_(std::make_unique<win::BluetoothLowEnergyWrapper>()) {}
+
+BluetoothTaskManagerWin::BluetoothTaskManagerWin(
+ std::unique_ptr<win::BluetoothClassicWrapper> classic_wrapper,
+ std::unique_ptr<win::BluetoothLowEnergyWrapper> le_wrapper,
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner)
+ : ui_task_runner_(std::move(ui_task_runner)),
+ classic_wrapper_(std::move(classic_wrapper)),
+ le_wrapper_(std::move(le_wrapper)) {}
+
+BluetoothTaskManagerWin::~BluetoothTaskManagerWin() = default;
+
+// static
+scoped_refptr<BluetoothTaskManagerWin>
+BluetoothTaskManagerWin::CreateForTesting(
+ std::unique_ptr<win::BluetoothClassicWrapper> classic_wrapper,
+ std::unique_ptr<win::BluetoothLowEnergyWrapper> le_wrapper,
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
+ return new BluetoothTaskManagerWin(std::move(classic_wrapper),
+ std::move(le_wrapper),
+ std::move(ui_task_runner));
}
+// static
BluetoothUUID BluetoothTaskManagerWin::BluetoothLowEnergyUuidToBluetoothUuid(
const BTH_LE_UUID& bth_le_uuid) {
if (bth_le_uuid.IsShortUuid) {
@@ -380,15 +392,12 @@ void BluetoothTaskManagerWin::PollAdapter() {
if (!discovering_) {
const BLUETOOTH_FIND_RADIO_PARAMS adapter_param =
{ sizeof(BLUETOOTH_FIND_RADIO_PARAMS) };
- HANDLE temp_adapter_handle;
HBLUETOOTH_RADIO_FIND handle =
- win::BluetoothClassicWrapper::GetInstance()->FindFirstRadio(
- &adapter_param, &temp_adapter_handle);
+ classic_wrapper_->FindFirstRadio(&adapter_param);
if (handle) {
- adapter_handle_ = temp_adapter_handle;
GetKnownDevices();
- win::BluetoothClassicWrapper::GetInstance()->FindRadioClose(handle);
+ classic_wrapper_->FindRadioClose(handle);
}
PostAdapterStateToUi();
@@ -405,7 +414,7 @@ void BluetoothTaskManagerWin::PollAdapter() {
void BluetoothTaskManagerWin::PostAdapterStateToUi() {
DCHECK(bluetooth_task_runner_->RunsTasksInCurrentSequence());
AdapterState* state = new AdapterState();
- GetAdapterState(adapter_handle_, state);
+ GetAdapterState(classic_wrapper_.get(), state);
ui_task_runner_->PostTask(
FROM_HERE,
base::Bind(&BluetoothTaskManagerWin::OnAdapterStateChanged,
@@ -419,13 +428,11 @@ void BluetoothTaskManagerWin::SetPowered(
const BluetoothAdapter::ErrorCallback& error_callback) {
DCHECK(bluetooth_task_runner_->RunsTasksInCurrentSequence());
bool success = false;
- if (adapter_handle_) {
- if (!powered) {
- win::BluetoothClassicWrapper::GetInstance()->EnableDiscovery(
- adapter_handle_, false);
- }
- success = !!win::BluetoothClassicWrapper::GetInstance()
- ->EnableIncomingConnections(adapter_handle_, powered);
+ if (classic_wrapper_->HasHandle()) {
+ if (!powered)
+ classic_wrapper_->EnableDiscovery(false);
+
+ success = !!classic_wrapper_->EnableIncomingConnections(powered);
}
if (success) {
@@ -438,10 +445,11 @@ void BluetoothTaskManagerWin::SetPowered(
void BluetoothTaskManagerWin::StartDiscovery() {
DCHECK(bluetooth_task_runner_->RunsTasksInCurrentSequence());
+ bool adapter_opened = classic_wrapper_->HasHandle();
ui_task_runner_->PostTask(
FROM_HERE, base::Bind(&BluetoothTaskManagerWin::OnDiscoveryStarted, this,
- !!adapter_handle_));
- if (!adapter_handle_)
+ adapter_opened));
+ if (!adapter_opened)
return;
discovering_ = true;
@@ -458,7 +466,7 @@ void BluetoothTaskManagerWin::StopDiscovery() {
void BluetoothTaskManagerWin::DiscoverDevices(int timeout_multiplier) {
DCHECK(bluetooth_task_runner_->RunsTasksInCurrentSequence());
- if (!discovering_ || !adapter_handle_) {
+ if (!discovering_ || !classic_wrapper_->HasHandle()) {
ui_task_runner_->PostTask(
FROM_HERE,
base::Bind(&BluetoothTaskManagerWin::OnDiscoveryStopped, this));
@@ -518,10 +526,9 @@ bool BluetoothTaskManagerWin::SearchClassicDevices(
ZeroMemory(&device_info, sizeof(device_info));
device_info.dwSize = sizeof(BLUETOOTH_DEVICE_INFO);
HBLUETOOTH_DEVICE_FIND handle =
- win::BluetoothClassicWrapper::GetInstance()->FindFirstDevice(
- &device_search_params, &device_info);
+ classic_wrapper_->FindFirstDevice(&device_search_params, &device_info);
if (!handle) {
- int last_error = win::BluetoothClassicWrapper::GetInstance()->LastError();
+ int last_error = classic_wrapper_->LastError();
if (last_error == ERROR_NO_MORE_ITEMS) {
return true; // No devices is not an error.
}
@@ -537,21 +544,20 @@ bool BluetoothTaskManagerWin::SearchClassicDevices(
// Reset device info before next call (as a safety precaution).
ZeroMemory(&device_info, sizeof(device_info));
device_info.dwSize = sizeof(BLUETOOTH_DEVICE_INFO);
- if (!win::BluetoothClassicWrapper::GetInstance()->FindNextDevice(
- handle, &device_info)) {
- int last_error = win::BluetoothClassicWrapper::GetInstance()->LastError();
+ if (!classic_wrapper_->FindNextDevice(handle, &device_info)) {
+ int last_error = classic_wrapper_->LastError();
if (last_error == ERROR_NO_MORE_ITEMS) {
break; // No more items is expected error when done enumerating.
}
LogPollingError("Error calling BluetoothFindNextDevice", last_error);
- win::BluetoothClassicWrapper::GetInstance()->FindDeviceClose(handle);
+ classic_wrapper_->FindDeviceClose(handle);
return false;
}
}
- if (!win::BluetoothClassicWrapper::GetInstance()->FindDeviceClose(handle)) {
+ if (!classic_wrapper_->FindDeviceClose(handle)) {
LogPollingError("Error calling BluetoothFindDeviceClose",
- win::BluetoothClassicWrapper::GetInstance()->LastError());
+ classic_wrapper_->LastError());
return false;
}
return true;
@@ -559,16 +565,14 @@ bool BluetoothTaskManagerWin::SearchClassicDevices(
bool BluetoothTaskManagerWin::SearchLowEnergyDevices(
std::vector<std::unique_ptr<DeviceState>>* device_list) {
- if (!win::BluetoothLowEnergyWrapper::GetInstance()
- ->IsBluetoothLowEnergySupported()) {
+ if (!le_wrapper_->IsBluetoothLowEnergySupported()) {
return true; // Bluetooth LE not supported is not an error.
}
std::vector<std::unique_ptr<win::BluetoothLowEnergyDeviceInfo>> btle_devices;
std::string error;
- bool success =
- win::BluetoothLowEnergyWrapper::GetInstance()
- ->EnumerateKnownBluetoothLowEnergyDevices(&btle_devices, &error);
+ bool success = le_wrapper_->EnumerateKnownBluetoothLowEnergyDevices(
+ &btle_devices, &error);
if (!success) {
error.insert(0, "Error calling EnumerateKnownBluetoothLowEnergyDevices: ");
LogPollingError(error.c_str(), 0);
@@ -717,16 +721,14 @@ int BluetoothTaskManagerWin::DiscoverClassicDeviceServicesWorker(
bool BluetoothTaskManagerWin::DiscoverLowEnergyDeviceServices(
const base::FilePath& device_path,
std::vector<std::unique_ptr<ServiceRecordState>>* service_record_states) {
- if (!win::BluetoothLowEnergyWrapper::GetInstance()
- ->IsBluetoothLowEnergySupported()) {
+ if (!le_wrapper_->IsBluetoothLowEnergySupported()) {
return true; // Bluetooth LE not supported is not an error.
}
std::string error;
std::vector<std::unique_ptr<win::BluetoothLowEnergyServiceInfo>> services;
- bool success = win::BluetoothLowEnergyWrapper::GetInstance()
- ->EnumerateKnownBluetoothLowEnergyServices(
- device_path, &services, &error);
+ bool success = le_wrapper_->EnumerateKnownBluetoothLowEnergyServices(
+ device_path, &services, &error);
if (!success) {
error.insert(0, "Error calling EnumerateKnownBluetoothLowEnergyServices: ");
LogPollingError(error.c_str(), 0);
@@ -756,9 +758,9 @@ bool BluetoothTaskManagerWin::SearchForGattServiceDevicePaths(
// List all known GATT service devices on the machine.
std::vector<std::unique_ptr<win::BluetoothLowEnergyDeviceInfo>>
gatt_service_devices;
- bool success = win::BluetoothLowEnergyWrapper::GetInstance()
- ->EnumerateKnownBluetoothLowEnergyGattServiceDevices(
- &gatt_service_devices, &error);
+ bool success =
+ le_wrapper_->EnumerateKnownBluetoothLowEnergyGattServiceDevices(
+ &gatt_service_devices, &error);
if (!success) {
error.insert(
0,
@@ -777,9 +779,8 @@ bool BluetoothTaskManagerWin::SearchForGattServiceDevicePaths(
// Discover this service device's contained services.
std::vector<std::unique_ptr<win::BluetoothLowEnergyServiceInfo>>
gatt_services;
- if (!win::BluetoothLowEnergyWrapper::GetInstance()
- ->EnumerateKnownBluetoothLowEnergyServices(
- gatt_service_device->path, &gatt_services, &error)) {
+ if (!le_wrapper_->EnumerateKnownBluetoothLowEnergyServices(
+ gatt_service_device->path, &gatt_services, &error)) {
error.insert(0,
"Error calling EnumerateKnownBluetoothLowEnergyServices: ");
LogPollingError(error.c_str(), 0);
@@ -828,10 +829,9 @@ void BluetoothTaskManagerWin::GetGattIncludedCharacteristics(
BTH_LE_GATT_SERVICE win_service;
if (BluetoothUUIDToWinBLEUUID(uuid, &(win_service.ServiceUuid))) {
win_service.AttributeHandle = attribute_handle;
- hr = win::BluetoothLowEnergyWrapper::GetInstance()
- ->ReadCharacteristicsOfAService(service_path, &win_service,
- &win_characteristics_info,
- &number_of_charateristics);
+ hr = le_wrapper_->ReadCharacteristicsOfAService(service_path, &win_service,
+ &win_characteristics_info,
+ &number_of_charateristics);
} else {
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
}
@@ -848,11 +848,9 @@ void BluetoothTaskManagerWin::GetGattIncludedDescriptors(
std::unique_ptr<BTH_LE_GATT_DESCRIPTOR> win_descriptors_info;
uint16_t number_of_descriptors = 0;
- HRESULT hr =
- win::BluetoothLowEnergyWrapper::GetInstance()
- ->ReadDescriptorsOfACharacteristic(
- service_path, (PBTH_LE_GATT_CHARACTERISTIC)(&characteristic),
- &win_descriptors_info, &number_of_descriptors);
+ HRESULT hr = le_wrapper_->ReadDescriptorsOfACharacteristic(
+ service_path, (PBTH_LE_GATT_CHARACTERISTIC)(&characteristic),
+ &win_descriptors_info, &number_of_descriptors);
ui_task_runner_->PostTask(
FROM_HERE, base::BindOnce(callback, std::move(win_descriptors_info),
@@ -864,10 +862,9 @@ void BluetoothTaskManagerWin::ReadGattCharacteristicValue(
BTH_LE_GATT_CHARACTERISTIC characteristic,
const ReadGattCharacteristicValueCallback& callback) {
std::unique_ptr<BTH_LE_GATT_CHARACTERISTIC_VALUE> win_characteristic_value;
- HRESULT hr =
- win::BluetoothLowEnergyWrapper::GetInstance()->ReadCharacteristicValue(
- service_path, (PBTH_LE_GATT_CHARACTERISTIC)(&characteristic),
- &win_characteristic_value);
+ HRESULT hr = le_wrapper_->ReadCharacteristicValue(
+ service_path, (PBTH_LE_GATT_CHARACTERISTIC)(&characteristic),
+ &win_characteristic_value);
ui_task_runner_->PostTask(
FROM_HERE,
@@ -887,10 +884,9 @@ void BluetoothTaskManagerWin::WriteGattCharacteristicValue(
for (ULONG i = 0; i < new_value.size(); i++)
win_new_value->Data[i] = new_value[i];
- HRESULT hr =
- win::BluetoothLowEnergyWrapper::GetInstance()->WriteCharacteristicValue(
- service_path, (PBTH_LE_GATT_CHARACTERISTIC)(&characteristic),
- win_new_value);
+ HRESULT hr = le_wrapper_->WriteCharacteristicValue(
+ service_path, (PBTH_LE_GATT_CHARACTERISTIC)(&characteristic),
+ win_new_value);
ui_task_runner_->PostTask(FROM_HERE, base::Bind(callback, hr));
}
@@ -909,10 +905,9 @@ void BluetoothTaskManagerWin::RegisterGattCharacteristicValueChangedEvent(
sizeof(BTH_LE_GATT_CHARACTERISTIC));
win_event_parameter.NumCharacteristics = 1;
PVOID user_event_handle = (PVOID)&registered_callback;
- HRESULT hr =
- win::BluetoothLowEnergyWrapper::GetInstance()->RegisterGattEvents(
- service_path, CharacteristicValueChangedEvent, &win_event_parameter,
- &OnGetGattEventWin, user_event_handle, &win_event_handle);
+ HRESULT hr = le_wrapper_->RegisterGattEvents(
+ service_path, CharacteristicValueChangedEvent, &win_event_parameter,
+ &OnGetGattEventWin, user_event_handle, &win_event_handle);
// Sets the Client Characteristic Configuration descriptor.
if (SUCCEEDED(hr)) {
@@ -927,7 +922,7 @@ void BluetoothTaskManagerWin::RegisterGattCharacteristicValueChangedEvent(
TRUE;
}
- hr = win::BluetoothLowEnergyWrapper::GetInstance()->WriteDescriptorValue(
+ hr = le_wrapper_->WriteDescriptorValue(
service_path, (PBTH_LE_GATT_DESCRIPTOR)(&ccc_descriptor),
&new_cccd_value);
}
@@ -955,8 +950,7 @@ void BluetoothTaskManagerWin::UnregisterGattCharacteristicValueChangedEvent(
CharacteristicValueChangedRegistrationMap::const_iterator it =
g_characteristic_value_changed_registrations.find(event_handle);
if (it != g_characteristic_value_changed_registrations.end()) {
- win::BluetoothLowEnergyWrapper::GetInstance()->UnregisterGattEvent(
- it->second->win_event_handle);
+ le_wrapper_->UnregisterGattEvent(it->second->win_event_handle);
g_characteristic_value_changed_registrations.erase(event_handle);
}
}
diff --git a/chromium/device/bluetooth/bluetooth_task_manager_win.h b/chromium/device/bluetooth/bluetooth_task_manager_win.h
index 3fd6318779f..8a5e82f64d3 100644
--- a/chromium/device/bluetooth/bluetooth_task_manager_win.h
+++ b/chromium/device/bluetooth/bluetooth_task_manager_win.h
@@ -29,6 +29,11 @@ class SequencedTaskRunner;
namespace device {
+namespace win {
+class BluetoothClassicWrapper;
+class BluetoothLowEnergyWrapper;
+} // namespace win
+
// Manages the blocking Bluetooth tasks using |SequencedWorkerPool|. It runs
// bluetooth tasks using |SequencedWorkerPool| and informs its observers of
// bluetooth adapter state changes and any other bluetooth device inquiry
@@ -111,6 +116,11 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothTaskManagerWin
explicit BluetoothTaskManagerWin(
scoped_refptr<base::SequencedTaskRunner> ui_task_runner);
+ static scoped_refptr<BluetoothTaskManagerWin> CreateForTesting(
+ std::unique_ptr<win::BluetoothClassicWrapper> classic_wrapper,
+ std::unique_ptr<win::BluetoothLowEnergyWrapper> le_wrapper,
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner);
+
static BluetoothUUID BluetoothLowEnergyUuidToBluetoothUuid(
const BTH_LE_UUID& bth_le_uuid);
@@ -199,6 +209,10 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothTaskManagerWin
static const int kPollIntervalMs;
+ BluetoothTaskManagerWin(
+ std::unique_ptr<win::BluetoothClassicWrapper> classic_wrapper,
+ std::unique_ptr<win::BluetoothLowEnergyWrapper> le_wrapper,
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner);
virtual ~BluetoothTaskManagerWin();
// Logs Win32 errors occurring during polling on the worker thread. The method
@@ -321,16 +335,16 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothTaskManagerWin
// List of observers interested in event notifications.
base::ObserverList<Observer> observers_;
- // Weak reference of the adapter handle, let BluetoothClassicWrapper handle
- // the close of |adapter_handle_|.
- HANDLE adapter_handle_;
-
// indicates whether the adapter is in discovery mode or not.
- bool discovering_;
+ bool discovering_ = false;
// Use for discarding too many log messages.
base::TimeTicks current_logging_batch_ticks_;
- int current_logging_batch_count_;
+ int current_logging_batch_count_ = 0;
+
+ // Wrapper around the Windows Bluetooth APIs. Owns the radio handle.
+ std::unique_ptr<win::BluetoothClassicWrapper> classic_wrapper_;
+ std::unique_ptr<win::BluetoothLowEnergyWrapper> le_wrapper_;
DISALLOW_COPY_AND_ASSIGN(BluetoothTaskManagerWin);
};
diff --git a/chromium/device/bluetooth/bluetooth_task_manager_win_unittest.cc b/chromium/device/bluetooth/bluetooth_task_manager_win_unittest.cc
index 9ffd07ec693..076500dbc65 100644
--- a/chromium/device/bluetooth/bluetooth_task_manager_win_unittest.cc
+++ b/chromium/device/bluetooth/bluetooth_task_manager_win_unittest.cc
@@ -8,6 +8,7 @@
#include "base/memory/ref_counted.h"
#include "base/test/test_pending_task.h"
#include "base/test/test_simple_task_runner.h"
+#include "device/bluetooth/bluetooth_classic_win.h"
#include "device/bluetooth/bluetooth_init_win.h"
#include "device/bluetooth/bluetooth_task_manager_win.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/device/bluetooth/bluetooth_uuid.cc b/chromium/device/bluetooth/bluetooth_uuid.cc
index 193402c4e4d..88e2736339d 100644
--- a/chromium/device/bluetooth/bluetooth_uuid.cc
+++ b/chromium/device/bluetooth/bluetooth_uuid.cc
@@ -7,7 +7,15 @@
#include <stddef.h>
#include "base/logging.h"
+#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+
+#if defined(OS_WIN)
+#include <objbase.h>
+
+#include "base/strings/string16.h"
+#endif // defined(OS_WIN)
namespace device {
@@ -69,11 +77,38 @@ BluetoothUUID::BluetoothUUID(const std::string& uuid) {
GetCanonicalUuid(uuid, &value_, &canonical_value_, &format_);
}
+#if defined(OS_WIN)
+BluetoothUUID::BluetoothUUID(GUID uuid) {
+ // 36 chars for UUID + 2 chars for braces + 1 char for null-terminator.
+ constexpr int kBufferSize = 39;
+ wchar_t buffer[kBufferSize];
+ int result = ::StringFromGUID2(uuid, buffer, kBufferSize);
+ DCHECK_EQ(kBufferSize, result);
+ DCHECK_EQ('{', buffer[0]);
+ DCHECK_EQ('}', buffer[37]);
+
+ GetCanonicalUuid(base::WideToUTF8(base::WStringPiece(buffer).substr(1, 36)),
+ &value_, &canonical_value_, &format_);
+ DCHECK_EQ(kFormat128Bit, format_);
+}
+#endif // defined(OS_WIN)
+
BluetoothUUID::BluetoothUUID() : format_(kFormatInvalid) {
}
BluetoothUUID::~BluetoothUUID() = default;
+#if defined(OS_WIN)
+// static
+GUID BluetoothUUID::GetCanonicalValueAsGUID(base::StringPiece uuid) {
+ DCHECK_EQ(36u, uuid.size());
+ base::string16 braced_uuid = L'{' + base::UTF8ToWide(uuid) + L'}';
+ GUID guid;
+ CHECK_EQ(NOERROR, ::CLSIDFromString(braced_uuid.data(), &guid));
+ return guid;
+}
+#endif // defined(OS_WIN)
+
bool BluetoothUUID::IsValid() const {
return format_ != kFormatInvalid;
}
diff --git a/chromium/device/bluetooth/bluetooth_uuid.h b/chromium/device/bluetooth/bluetooth_uuid.h
index 8487f6a2da5..c2624148357 100644
--- a/chromium/device/bluetooth/bluetooth_uuid.h
+++ b/chromium/device/bluetooth/bluetooth_uuid.h
@@ -7,8 +7,15 @@
#include <string>
+#include "build/build_config.h"
#include "device/bluetooth/bluetooth_export.h"
+#if defined(OS_WIN)
+#include <rpc.h>
+
+#include "base/strings/string_piece_forward.h"
+#endif // defined(OS_WIN)
+
namespace device {
// Opaque wrapper around a Bluetooth UUID. Instances of UUID represent the
@@ -42,6 +49,12 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothUUID {
// after construction.
explicit BluetoothUUID(const std::string& uuid);
+#if defined(OS_WIN)
+ // Windows exclusive constructor converting a GUID structure to a
+ // BluetoothUUID. This will always result in a 128 bit Format.
+ explicit BluetoothUUID(GUID uuid);
+#endif // defined(OS_WIN)
+
// Default constructor does nothing. Since BluetoothUUID is copyable, this
// constructor is useful for initializing member variables and assigning a
// value to them later. The default constructor will initialize an invalid
@@ -49,6 +62,11 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothUUID {
BluetoothUUID();
virtual ~BluetoothUUID();
+#if defined(OS_WIN)
+ // The canonical UUID string format is device::BluetoothUUID.value().
+ static GUID GetCanonicalValueAsGUID(base::StringPiece uuid);
+#endif // defined(OS_WIN)
+
// Returns true, if the UUID is in a valid canonical format.
bool IsValid() const;
diff --git a/chromium/device/bluetooth/bluetooth_uuid_unittest.cc b/chromium/device/bluetooth/bluetooth_uuid_unittest.cc
index 86ffe20ed73..04755198c66 100644
--- a/chromium/device/bluetooth/bluetooth_uuid_unittest.cc
+++ b/chromium/device/bluetooth/bluetooth_uuid_unittest.cc
@@ -2,10 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "device/bluetooth/bluetooth_uuid.h"
+
#include <stddef.h>
#include "base/macros.h"
-#include "device/bluetooth/bluetooth_uuid.h"
+#include "base/strings/string_piece.h"
+#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace device {
@@ -76,6 +79,47 @@ TEST(BluetoothUUIDTest, BluetoothUUID) {
EXPECT_EQ(uuid1, uuid6);
}
+#if defined(OS_WIN)
+TEST(BluetoothUUIDTest, BluetoothUUID_GUID) {
+ const char kValid128Bit0[] = "12345678-1234-5678-9abc-def123456789";
+ GUID guid;
+ guid.Data1 = 0x12345678;
+ guid.Data2 = 0x1234;
+ guid.Data3 = 0x5678;
+ guid.Data4[0] = 0x9a;
+ guid.Data4[1] = 0xbc;
+ guid.Data4[2] = 0xde;
+ guid.Data4[3] = 0xf1;
+ guid.Data4[4] = 0x23;
+ guid.Data4[5] = 0x45;
+ guid.Data4[6] = 0x67;
+ guid.Data4[7] = 0x89;
+
+ BluetoothUUID uuid(guid);
+ EXPECT_TRUE(uuid.IsValid());
+ EXPECT_EQ(BluetoothUUID::kFormat128Bit, uuid.format());
+ EXPECT_EQ(kValid128Bit0, uuid.value());
+ EXPECT_EQ(kValid128Bit0, uuid.canonical_value());
+}
+
+TEST(BluetoothUUIDTest, GetCanonicalValueAsGUID) {
+ const char kValid128Bit0[] = "12345678-1234-5678-9abc-def123456789";
+ GUID guid = BluetoothUUID::GetCanonicalValueAsGUID(kValid128Bit0);
+
+ EXPECT_EQ(0x12345678, guid.Data1);
+ EXPECT_EQ(0x1234, guid.Data2);
+ EXPECT_EQ(0x5678, guid.Data3);
+ EXPECT_EQ(0x9a, guid.Data4[0]);
+ EXPECT_EQ(0xbc, guid.Data4[1]);
+ EXPECT_EQ(0xde, guid.Data4[2]);
+ EXPECT_EQ(0xf1, guid.Data4[3]);
+ EXPECT_EQ(0x23, guid.Data4[4]);
+ EXPECT_EQ(0x45, guid.Data4[5]);
+ EXPECT_EQ(0x67, guid.Data4[6]);
+ EXPECT_EQ(0x89, guid.Data4[7]);
+}
+#endif // defined(OS_WIN)
+
// Verify that UUIDs are parsed case-insensitively
TEST(BluetoothUUIDTest, BluetoothUUID_CaseInsensitive) {
const char k16Bit[] = "1abc";
diff --git a/chromium/device/bluetooth/bluez/bluetooth_device_bluez.cc b/chromium/device/bluetooth/bluez/bluetooth_device_bluez.cc
index 6da4644c0c5..ff1f83816f7 100644
--- a/chromium/device/bluetooth/bluez/bluetooth_device_bluez.cc
+++ b/chromium/device/bluetooth/bluez/bluetooth_device_bluez.cc
@@ -666,6 +666,26 @@ void BluetoothDeviceBlueZ::GetServiceRecords(
weak_ptr_factory_.GetWeakPtr(), error_callback));
}
+#if defined(OS_CHROMEOS)
+void BluetoothDeviceBlueZ::ExecuteWrite(
+ const base::Closure& callback,
+ const ExecuteWriteErrorCallback& error_callback) {
+ bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->ExecuteWrite(
+ object_path_, callback,
+ base::Bind(&BluetoothDeviceBlueZ::OnExecuteWriteError,
+ weak_ptr_factory_.GetWeakPtr(), error_callback));
+}
+
+void BluetoothDeviceBlueZ::AbortWrite(
+ const base::Closure& callback,
+ const AbortWriteErrorCallback& error_callback) {
+ bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->AbortWrite(
+ object_path_, callback,
+ base::Bind(&BluetoothDeviceBlueZ::OnAbortWriteError,
+ weak_ptr_factory_.GetWeakPtr(), error_callback));
+}
+#endif
+
void BluetoothDeviceBlueZ::UpdateServiceData() {
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
@@ -863,6 +883,30 @@ void BluetoothDeviceBlueZ::OnGetServiceRecordsError(
error_callback.Run(code);
}
+#if defined(OS_CHROMEOS)
+void BluetoothDeviceBlueZ::OnExecuteWriteError(
+ const ExecuteWriteErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ BLUETOOTH_LOG(EVENT) << object_path_.value()
+ << ": Failed to execute write: " << error_name << ": "
+ << error_message;
+ error_callback.Run(
+ BluetoothGattServiceBlueZ::DBusErrorToServiceError(error_name));
+}
+
+void BluetoothDeviceBlueZ::OnAbortWriteError(
+ const AbortWriteErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ BLUETOOTH_LOG(EVENT) << object_path_.value()
+ << ": Failed to abort write: " << error_name << ": "
+ << error_message;
+ error_callback.Run(
+ BluetoothGattServiceBlueZ::DBusErrorToServiceError(error_name));
+}
+#endif
+
void BluetoothDeviceBlueZ::ConnectInternal(
bool after_pairing,
const base::Closure& callback,
diff --git a/chromium/device/bluetooth/bluez/bluetooth_device_bluez.h b/chromium/device/bluetooth/bluez/bluetooth_device_bluez.h
index 9978b5198d0..3cb5464b85f 100644
--- a/chromium/device/bluetooth/bluez/bluetooth_device_bluez.h
+++ b/chromium/device/bluetooth/bluez/bluetooth_device_bluez.h
@@ -104,6 +104,12 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceBlueZ
void Pair(device::BluetoothDevice::PairingDelegate* pairing_delegate,
const base::Closure& callback,
const ConnectErrorCallback& error_callback) override;
+#if defined(OS_CHROMEOS)
+ void ExecuteWrite(const base::Closure& callback,
+ const ExecuteWriteErrorCallback& error_callback) override;
+ void AbortWrite(const base::Closure& callback,
+ const AbortWriteErrorCallback& error_callback) override;
+#endif
// Returns the complete list of service records discovered for on this
// device via SDP. If called before discovery is complete, it may return
@@ -203,6 +209,16 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceBlueZ
const std::string& error_name,
const std::string& error_message);
+#if defined(OS_CHROMEOS)
+ void OnExecuteWriteError(const ExecuteWriteErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ void OnAbortWriteError(const AbortWriteErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message);
+#endif
+
// Internal method to initiate a connection to this device, and methods called
// by dbus:: on completion of the D-Bus method call.
void ConnectInternal(bool after_pairing,
diff --git a/chromium/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc b/chromium/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc
index d2566e9aaad..ab60b46eeb9 100644
--- a/chromium/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc
+++ b/chromium/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc
@@ -18,12 +18,14 @@
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_discovery_filter.h"
+#include "device/bluetooth/bluetooth_gatt_characteristic.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_descriptor.h"
#include "device/bluetooth/bluetooth_remote_gatt_service.h"
#include "device/bluetooth/bluetooth_uuid.h"
+#include "device/bluetooth/bluez/bluetooth_device_bluez.h"
#include "device/bluetooth/dbus/bluez_dbus_manager.h"
#include "device/bluetooth/dbus/fake_bluetooth_adapter_client.h"
#include "device/bluetooth/dbus/fake_bluetooth_agent_manager_client.h"
@@ -1434,8 +1436,10 @@ TEST_F(BluetoothGattBlueZTest, GattCharacteristicProperties) {
characteristic = service->GetCharacteristic(
fake_bluetooth_gatt_characteristic_client_->GetHeartRateMeasurementPath()
.value());
- EXPECT_EQ(BluetoothRemoteGattCharacteristic::PROPERTY_NOTIFY,
- characteristic->GetProperties());
+ EXPECT_EQ(BluetoothRemoteGattCharacteristic::PROPERTY_NOTIFY |
+ BluetoothRemoteGattCharacteristic::PROPERTY_INDICATE,
+ static_cast<BluetoothRemoteGattCharacteristic::Property>(
+ characteristic->GetProperties()));
}
TEST_F(BluetoothGattBlueZTest, GattDescriptorValue) {
@@ -1848,4 +1852,151 @@ TEST_F(BluetoothGattBlueZTest, NotifySessionsMadeInactive) {
EXPECT_TRUE(update_sessions_[0]->IsActive());
}
+#if defined(OS_CHROMEOS)
+TEST_F(BluetoothGattBlueZTest, ReliableWrite) {
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDeviceBlueZ* device = static_cast<BluetoothDeviceBlueZ*>(
+ adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress));
+ ASSERT_TRUE(device);
+
+ TestBluetoothAdapterObserver observer(adapter_);
+
+ // Expose the fake Heart Rate service. This will asynchronously expose
+ // characteristics.
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
+ ASSERT_EQ(1, observer.gatt_service_added_count());
+
+ BluetoothRemoteGattService* service =
+ device->GetGattService(observer.last_gatt_service_id());
+
+ // Run the message loop so that the characteristics appear.
+ base::RunLoop().Run();
+
+ // Request to start notifications.
+ service
+ ->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
+ ->GetHeartRateMeasurementPath()
+ .value())
+ ->StartNotifySession(
+ base::Bind(&BluetoothGattBlueZTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ base::RunLoop().Run();
+
+ // Obtain writable Heart Rate Control Point characteristic.
+ BluetoothRemoteGattCharacteristic* characteristic =
+ service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
+ ->GetHeartRateControlPointPath()
+ .value());
+ std::vector<uint8_t> write_value = {0x01};
+
+ // Prepare 1000 writes.
+ success_callback_count_ = 0;
+ error_callback_count_ = 0;
+ observer.Reset();
+ for (int i = 0; i < 1000; ++i) {
+ characteristic->PrepareWriteRemoteCharacteristic(
+ write_value,
+ base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ }
+ EXPECT_EQ(1000, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
+
+ // Abort.
+ device->AbortWrite(base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1001, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
+
+ // Prepare another 1000 writes.
+ success_callback_count_ = 0;
+ error_callback_count_ = 0;
+ observer.Reset();
+ for (int i = 0; i < 1000; ++i) {
+ characteristic->PrepareWriteRemoteCharacteristic(
+ write_value,
+ base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ }
+ EXPECT_EQ(1000, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
+
+ // Execute.
+ device->ExecuteWrite(base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1001, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1000, observer.gatt_characteristic_value_changed_count());
+}
+
+TEST_F(BluetoothGattBlueZTest, NotificationType) {
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDeviceBlueZ* device = static_cast<BluetoothDeviceBlueZ*>(
+ adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress));
+ ASSERT_TRUE(device);
+
+ TestBluetoothAdapterObserver observer(adapter_);
+
+ // Expose the fake Heart Rate service. This will asynchronously expose
+ // characteristics.
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
+ ASSERT_EQ(1, observer.gatt_service_added_count());
+
+ BluetoothRemoteGattService* service =
+ device->GetGattService(observer.last_gatt_service_id());
+
+ // Run the message loop so that the characteristics appear.
+ base::RunLoop().Run();
+ BluetoothRemoteGattCharacteristic* characteristic =
+ service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
+ ->GetHeartRateMeasurementPath()
+ .value());
+
+ // Request to start notifications.
+ characteristic->StartNotifySession(
+ device::BluetoothGattCharacteristic::NotificationType::kNotification,
+ base::Bind(&BluetoothGattBlueZTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ base::RunLoop().Run();
+ EXPECT_EQ(1, observer.gatt_characteristic_value_changed_count());
+
+ // Request to start indications.
+ fake_bluetooth_gatt_characteristic_client_->StopNotify(
+ fake_bluetooth_gatt_characteristic_client_->GetHeartRateMeasurementPath(),
+ base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattBlueZTest::DBusErrorCallback,
+ base::Unretained(this)));
+ characteristic->StartNotifySession(
+ device::BluetoothGattCharacteristic::NotificationType::kIndication,
+ base::Bind(&BluetoothGattBlueZTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
+ base::Unretained(this)));
+ base::RunLoop().Run();
+ EXPECT_EQ(2, observer.gatt_characteristic_value_changed_count());
+}
+#endif
+
} // namespace bluez
diff --git a/chromium/device/bluetooth/bluez/bluetooth_local_gatt_characteristic_bluez.cc b/chromium/device/bluetooth/bluez/bluetooth_local_gatt_characteristic_bluez.cc
index 2a88ebbb2d2..1fb2e33117a 100644
--- a/chromium/device/bluetooth/bluez/bluetooth_local_gatt_characteristic_bluez.cc
+++ b/chromium/device/bluetooth/bluez/bluetooth_local_gatt_characteristic_bluez.cc
@@ -26,10 +26,9 @@ BluetoothLocalGattCharacteristic::Create(
DCHECK(service);
bluez::BluetoothLocalGattServiceBlueZ* service_bluez =
static_cast<bluez::BluetoothLocalGattServiceBlueZ*>(service);
- // TODO(rkc): Handle permisisons once BlueZ supports getting them from DBus.
bluez::BluetoothLocalGattCharacteristicBlueZ* characteristic =
- new bluez::BluetoothLocalGattCharacteristicBlueZ(uuid, properties,
- service_bluez);
+ new bluez::BluetoothLocalGattCharacteristicBlueZ(
+ uuid, properties, permissions, service_bluez);
return characteristic->weak_ptr_factory_.GetWeakPtr();
}
@@ -40,12 +39,14 @@ namespace bluez {
BluetoothLocalGattCharacteristicBlueZ::BluetoothLocalGattCharacteristicBlueZ(
const device::BluetoothUUID& uuid,
Properties properties,
+ Permissions permissions,
BluetoothLocalGattServiceBlueZ* service)
: BluetoothGattCharacteristicBlueZ(
BluetoothLocalGattServiceBlueZ::AddGuidToObjectPath(
service->object_path().value() + "/characteristic")),
uuid_(uuid),
properties_(properties),
+ permissions_(permissions),
service_(service),
weak_ptr_factory_(this) {
VLOG(1) << "Creating local GATT characteristic with identifier: "
@@ -67,8 +68,7 @@ BluetoothLocalGattCharacteristicBlueZ::GetProperties() const {
device::BluetoothGattCharacteristic::Permissions
BluetoothLocalGattCharacteristicBlueZ::GetPermissions() const {
- NOTIMPLEMENTED();
- return Permissions();
+ return permissions_;
}
device::BluetoothLocalGattCharacteristic::NotificationStatus
diff --git a/chromium/device/bluetooth/bluez/bluetooth_local_gatt_characteristic_bluez.h b/chromium/device/bluetooth/bluez/bluetooth_local_gatt_characteristic_bluez.h
index 12ee68d8ecc..5566c807ec0 100644
--- a/chromium/device/bluetooth/bluez/bluetooth_local_gatt_characteristic_bluez.h
+++ b/chromium/device/bluetooth/bluez/bluetooth_local_gatt_characteristic_bluez.h
@@ -23,13 +23,14 @@ class BluetoothLocalGattServiceBlueZ;
// The BluetoothLocalGattCharacteristicBlueZ class implements
// BluetoothLocalGattCharacteristic for local GATT characteristics for
// platforms that use BlueZ.
-class BluetoothLocalGattCharacteristicBlueZ
+class DEVICE_BLUETOOTH_EXPORT BluetoothLocalGattCharacteristicBlueZ
: public BluetoothGattCharacteristicBlueZ,
public device::BluetoothLocalGattCharacteristic {
public:
BluetoothLocalGattCharacteristicBlueZ(
const device::BluetoothUUID& uuid,
Properties properties,
+ Permissions permissions,
BluetoothLocalGattServiceBlueZ* service);
~BluetoothLocalGattCharacteristicBlueZ() override;
@@ -62,6 +63,9 @@ class BluetoothLocalGattCharacteristicBlueZ
// Properties of this characteristic.
Properties properties_;
+ // Permissions of this characteristic.
+ Permissions permissions_;
+
// Service that contains this characteristic.
BluetoothLocalGattServiceBlueZ* service_;
diff --git a/chromium/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.cc b/chromium/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.cc
index 5f7862107e4..1bbc2b7fdc5 100644
--- a/chromium/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.cc
+++ b/chromium/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.cc
@@ -155,21 +155,6 @@ bool BluetoothRemoteGattCharacteristicBlueZ::IsNotifying() const {
return has_notify_session_ && properties->notifying.value();
}
-std::vector<device::BluetoothRemoteGattDescriptor*>
-BluetoothRemoteGattCharacteristicBlueZ::GetDescriptors() const {
- std::vector<device::BluetoothRemoteGattDescriptor*> descriptors;
- for (const auto& descriptor : descriptors_)
- descriptors.push_back(descriptor.second.get());
- return descriptors;
-}
-
-device::BluetoothRemoteGattDescriptor*
-BluetoothRemoteGattCharacteristicBlueZ::GetDescriptor(
- const std::string& identifier) const {
- auto iter = descriptors_.find(dbus::ObjectPath(identifier));
- return iter != descriptors_.end() ? iter->second.get() : nullptr;
-}
-
void BluetoothRemoteGattCharacteristicBlueZ::ReadRemoteCharacteristic(
const ValueCallback& callback,
const ErrorCallback& error_callback) {
@@ -204,14 +189,39 @@ void BluetoothRemoteGattCharacteristicBlueZ::WriteRemoteCharacteristic(
weak_ptr_factory_.GetWeakPtr(), error_callback));
}
+#if defined(OS_CHROMEOS)
+void BluetoothRemoteGattCharacteristicBlueZ::PrepareWriteRemoteCharacteristic(
+ const std::vector<uint8_t>& value,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ VLOG(1) << "Sending GATT characteristic prepare write request to "
+ << "characteristic: " << GetIdentifier()
+ << ", UUID: " << GetUUID().canonical_value()
+ << ", with value: " << value << ".";
+
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothGattCharacteristicClient()
+ ->PrepareWriteValue(
+ object_path(), value, callback,
+ base::Bind(&BluetoothRemoteGattCharacteristicBlueZ::OnWriteError,
+ weak_ptr_factory_.GetWeakPtr(), error_callback));
+}
+#endif
+
void BluetoothRemoteGattCharacteristicBlueZ::SubscribeToNotifications(
device::BluetoothRemoteGattDescriptor* ccc_descriptor,
+#if defined(OS_CHROMEOS)
+ NotificationType notification_type,
+#endif
const base::Closure& callback,
const ErrorCallback& error_callback) {
bluez::BluezDBusManager::Get()
->GetBluetoothGattCharacteristicClient()
->StartNotify(
object_path(),
+#if defined(OS_CHROMEOS)
+ notification_type,
+#endif
base::Bind(
&BluetoothRemoteGattCharacteristicBlueZ::OnStartNotifySuccess,
weak_ptr_factory_.GetWeakPtr(), callback),
@@ -237,7 +247,7 @@ void BluetoothRemoteGattCharacteristicBlueZ::UnsubscribeFromNotifications(
void BluetoothRemoteGattCharacteristicBlueZ::GattDescriptorAdded(
const dbus::ObjectPath& object_path) {
- if (descriptors_.find(object_path) != descriptors_.end()) {
+ if (descriptors_.find(object_path.value()) != descriptors_.end()) {
VLOG(1) << "Remote GATT characteristic descriptor already exists: "
<< object_path.value();
return;
@@ -259,7 +269,7 @@ void BluetoothRemoteGattCharacteristicBlueZ::GattDescriptorAdded(
// NOTE: Can't use std::make_unique due to private constructor.
BluetoothRemoteGattDescriptorBlueZ* descriptor =
new BluetoothRemoteGattDescriptorBlueZ(this, object_path);
- descriptors_.emplace(object_path, base::WrapUnique(descriptor));
+ AddDescriptor(base::WrapUnique(descriptor));
DCHECK(descriptor->GetIdentifier() == object_path.value());
DCHECK(descriptor->GetUUID().IsValid());
DCHECK(service_);
@@ -270,7 +280,7 @@ void BluetoothRemoteGattCharacteristicBlueZ::GattDescriptorAdded(
void BluetoothRemoteGattCharacteristicBlueZ::GattDescriptorRemoved(
const dbus::ObjectPath& object_path) {
- DescriptorMap::iterator iter = descriptors_.find(object_path);
+ auto iter = descriptors_.find(object_path.value());
if (iter == descriptors_.end()) {
VLOG(2) << "Unknown descriptor removed: " << object_path.value();
return;
@@ -280,19 +290,21 @@ void BluetoothRemoteGattCharacteristicBlueZ::GattDescriptorRemoved(
<< GetIdentifier() << ", UUID: " << GetUUID().canonical_value();
auto descriptor = std::move(iter->second);
- DCHECK(descriptor->object_path() == object_path);
+ auto* descriptor_bluez =
+ static_cast<BluetoothRemoteGattDescriptorBlueZ*>(descriptor.get());
+ DCHECK(descriptor_bluez->object_path() == object_path);
descriptors_.erase(iter);
DCHECK(service_);
static_cast<BluetoothRemoteGattServiceBlueZ*>(service_)
- ->NotifyDescriptorAddedOrRemoved(this, descriptor.get(),
+ ->NotifyDescriptorAddedOrRemoved(this, descriptor_bluez,
false /* added */);
}
void BluetoothRemoteGattCharacteristicBlueZ::GattDescriptorPropertyChanged(
const dbus::ObjectPath& object_path,
const std::string& property_name) {
- DescriptorMap::iterator iter = descriptors_.find(object_path);
+ auto iter = descriptors_.find(object_path.value());
if (iter == descriptors_.end()) {
VLOG(2) << "Unknown descriptor removed: " << object_path.value();
return;
@@ -310,8 +322,10 @@ void BluetoothRemoteGattCharacteristicBlueZ::GattDescriptorPropertyChanged(
DCHECK(service_);
static_cast<BluetoothRemoteGattServiceBlueZ*>(service_)
- ->NotifyDescriptorValueChanged(this, iter->second.get(),
- properties->value.value());
+ ->NotifyDescriptorValueChanged(
+ this,
+ static_cast<BluetoothRemoteGattDescriptorBlueZ*>(iter->second.get()),
+ properties->value.value());
}
void BluetoothRemoteGattCharacteristicBlueZ::OnStartNotifySuccess(
diff --git a/chromium/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h b/chromium/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h
index f40d317abb1..4d252cfbb24 100644
--- a/chromium/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h
+++ b/chromium/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h
@@ -33,7 +33,6 @@ class BluetoothRemoteGattService;
namespace bluez {
-class BluetoothRemoteGattDescriptorBlueZ;
class BluetoothRemoteGattServiceBlueZ;
// The BluetoothRemoteGattCharacteristicBlueZ class implements
@@ -55,21 +54,31 @@ class BluetoothRemoteGattCharacteristicBlueZ
const std::vector<uint8_t>& GetValue() const override;
device::BluetoothRemoteGattService* GetService() const override;
bool IsNotifying() const override;
- std::vector<device::BluetoothRemoteGattDescriptor*> GetDescriptors()
- const override;
- device::BluetoothRemoteGattDescriptor* GetDescriptor(
- const std::string& identifier) const override;
void ReadRemoteCharacteristic(const ValueCallback& callback,
const ErrorCallback& error_callback) override;
void WriteRemoteCharacteristic(const std::vector<uint8_t>& value,
const base::Closure& callback,
const ErrorCallback& error_callback) override;
+#if defined(OS_CHROMEOS)
+ void PrepareWriteRemoteCharacteristic(
+ const std::vector<uint8_t>& value,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) override;
+#endif
protected:
+#if defined(OS_CHROMEOS)
+ void SubscribeToNotifications(
+ device::BluetoothRemoteGattDescriptor* ccc_descriptor,
+ NotificationType notification_type,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) override;
+#else
void SubscribeToNotifications(
device::BluetoothRemoteGattDescriptor* ccc_descriptor,
const base::Closure& callback,
const ErrorCallback& error_callback) override;
+#endif
void UnsubscribeFromNotifications(
device::BluetoothRemoteGattDescriptor* ccc_descriptor,
const base::Closure& callback,
@@ -123,16 +132,6 @@ class BluetoothRemoteGattCharacteristicBlueZ
// True, if there exists a Bluez notify session.
bool has_notify_session_;
- using DescriptorMap =
- std::map<dbus::ObjectPath,
- std::unique_ptr<BluetoothRemoteGattDescriptorBlueZ>>;
-
- // Mapping from GATT descriptor object paths to descriptor objects owned by
- // this characteristic. Since the BlueZ implementation uses object paths
- // as unique identifiers, we also use this mapping to return descriptors by
- // identifier.
- DescriptorMap descriptors_;
-
// The GATT service this GATT characteristic belongs to.
BluetoothRemoteGattServiceBlueZ* service_;
diff --git a/chromium/device/bluetooth/bluez/bluetooth_remote_gatt_service_bluez.cc b/chromium/device/bluetooth/bluez/bluetooth_remote_gatt_service_bluez.cc
index 158e0adcbdc..fbc94a2e7eb 100644
--- a/chromium/device/bluetooth/bluez/bluetooth_remote_gatt_service_bluez.cc
+++ b/chromium/device/bluetooth/bluez/bluetooth_remote_gatt_service_bluez.cc
@@ -87,27 +87,12 @@ device::BluetoothDevice* BluetoothRemoteGattServiceBlueZ::GetDevice() const {
return device_;
}
-std::vector<device::BluetoothRemoteGattCharacteristic*>
-BluetoothRemoteGattServiceBlueZ::GetCharacteristics() const {
- std::vector<device::BluetoothRemoteGattCharacteristic*> characteristics;
- for (const auto& characteristic : characteristics_)
- characteristics.push_back(characteristic.second.get());
- return characteristics;
-}
-
std::vector<device::BluetoothRemoteGattService*>
BluetoothRemoteGattServiceBlueZ::GetIncludedServices() const {
// TODO(armansito): Return the actual included services here.
return std::vector<device::BluetoothRemoteGattService*>();
}
-device::BluetoothRemoteGattCharacteristic*
-BluetoothRemoteGattServiceBlueZ::GetCharacteristic(
- const std::string& identifier) const {
- auto iter = characteristics_.find(dbus::ObjectPath(identifier));
- return iter != characteristics_.end() ? iter->second.get() : nullptr;
-}
-
void BluetoothRemoteGattServiceBlueZ::NotifyServiceChanged() {
// Don't send service changed unless we know that all characteristics have
// already been discovered. This is to prevent spammy events before sending
@@ -164,7 +149,7 @@ void BluetoothRemoteGattServiceBlueZ::GattServicePropertyChanged(
void BluetoothRemoteGattServiceBlueZ::GattCharacteristicAdded(
const dbus::ObjectPath& object_path) {
- if (characteristics_.find(object_path) != characteristics_.end()) {
+ if (characteristics_.find(object_path.value()) != characteristics_.end()) {
VLOG(1) << "Remote GATT characteristic already exists: "
<< object_path.value();
return;
@@ -186,7 +171,7 @@ void BluetoothRemoteGattServiceBlueZ::GattCharacteristicAdded(
// NOTE: Can't use std::make_unique due to private constructor.
BluetoothRemoteGattCharacteristicBlueZ* characteristic =
new BluetoothRemoteGattCharacteristicBlueZ(this, object_path);
- characteristics_.emplace(object_path, base::WrapUnique(characteristic));
+ AddCharacteristic(base::WrapUnique(characteristic));
DCHECK(characteristic->GetIdentifier() == object_path.value());
DCHECK(characteristic->GetUUID().IsValid());
@@ -196,7 +181,7 @@ void BluetoothRemoteGattServiceBlueZ::GattCharacteristicAdded(
void BluetoothRemoteGattServiceBlueZ::GattCharacteristicRemoved(
const dbus::ObjectPath& object_path) {
- CharacteristicMap::iterator iter = characteristics_.find(object_path);
+ CharacteristicMap::iterator iter = characteristics_.find(object_path.value());
if (iter == characteristics_.end()) {
VLOG(2) << "Unknown GATT characteristic removed: " << object_path.value();
return;
@@ -206,7 +191,9 @@ void BluetoothRemoteGattServiceBlueZ::GattCharacteristicRemoved(
<< GetIdentifier() << ", UUID: " << GetUUID().canonical_value();
auto characteristic = std::move(iter->second);
- DCHECK(characteristic->object_path() == object_path);
+ DCHECK(
+ static_cast<BluetoothRemoteGattCharacteristicBlueZ*>(characteristic.get())
+ ->object_path() == object_path);
characteristics_.erase(iter);
DCHECK(GetAdapter());
@@ -216,8 +203,11 @@ void BluetoothRemoteGattServiceBlueZ::GattCharacteristicRemoved(
void BluetoothRemoteGattServiceBlueZ::GattCharacteristicPropertyChanged(
const dbus::ObjectPath& object_path,
const std::string& property_name) {
- CharacteristicMap::iterator iter = characteristics_.find(object_path);
- if (iter == characteristics_.end()) {
+ auto* characteristic_bluez =
+ static_cast<BluetoothRemoteGattCharacteristicBlueZ*>(
+ GetCharacteristic(object_path.value()));
+
+ if (!characteristic_bluez) {
VLOG(3) << "Properties of unknown characteristic changed";
return;
}
@@ -238,12 +228,14 @@ void BluetoothRemoteGattServiceBlueZ::GattCharacteristicPropertyChanged(
if (property_name == properties->flags.name()) {
NotifyServiceChanged();
} else if (property_name == properties->value.name()) {
- DCHECK_GE(iter->second->num_of_characteristic_value_read_in_progress_, 0);
- if (iter->second->num_of_characteristic_value_read_in_progress_ > 0) {
- --iter->second->num_of_characteristic_value_read_in_progress_;
+ DCHECK_GE(
+ characteristic_bluez->num_of_characteristic_value_read_in_progress_, 0);
+ if (characteristic_bluez->num_of_characteristic_value_read_in_progress_ >
+ 0) {
+ --characteristic_bluez->num_of_characteristic_value_read_in_progress_;
} else {
GetAdapter()->NotifyGattCharacteristicValueChanged(
- iter->second.get(), properties->value.value());
+ characteristic_bluez, properties->value.value());
}
}
}
diff --git a/chromium/device/bluetooth/bluez/bluetooth_remote_gatt_service_bluez.h b/chromium/device/bluetooth/bluez/bluetooth_remote_gatt_service_bluez.h
index 116f2db7669..b192eadc5b8 100644
--- a/chromium/device/bluetooth/bluez/bluetooth_remote_gatt_service_bluez.h
+++ b/chromium/device/bluetooth/bluez/bluetooth_remote_gatt_service_bluez.h
@@ -7,7 +7,6 @@
#include <stdint.h>
-#include <map>
#include <memory>
#include <string>
#include <vector>
@@ -24,7 +23,6 @@
namespace device {
class BluetoothDevice;
-class BluetoothRemoteGattCharacteristic;
} // namespace device
@@ -49,12 +47,8 @@ class BluetoothRemoteGattServiceBlueZ
device::BluetoothUUID GetUUID() const override;
device::BluetoothDevice* GetDevice() const override;
bool IsPrimary() const override;
- std::vector<device::BluetoothRemoteGattCharacteristic*> GetCharacteristics()
- const override;
std::vector<device::BluetoothRemoteGattService*> GetIncludedServices()
const override;
- device::BluetoothRemoteGattCharacteristic* GetCharacteristic(
- const std::string& identifier) const override;
// Notifies its observers that the GATT service has changed. This is mainly
// used by BluetoothRemoteGattCharacteristicBlueZ instances to notify
@@ -102,16 +96,6 @@ class BluetoothRemoteGattServiceBlueZ
// here since |device_| owns this instance.
BluetoothDeviceBlueZ* device_;
- using CharacteristicMap =
- std::map<dbus::ObjectPath,
- std::unique_ptr<BluetoothRemoteGattCharacteristicBlueZ>>;
-
- // Mapping from GATT characteristic object paths to characteristic objects.
- // owned by this service. Since the BlueZ implementation uses object
- // paths as unique identifiers, we also use this mapping to return
- // characteristics by identifier.
- CharacteristicMap characteristics_;
-
// Note: This should remain the last member so it'll be destroyed and
// invalidate its weak pointers before any other members are destroyed.
base::WeakPtrFactory<BluetoothRemoteGattServiceBlueZ> weak_ptr_factory_;
diff --git a/chromium/device/bluetooth/cast/bluetooth_adapter_cast.cc b/chromium/device/bluetooth/cast/bluetooth_adapter_cast.cc
index af5df4cbd04..c95719c7a84 100644
--- a/chromium/device/bluetooth/cast/bluetooth_adapter_cast.cc
+++ b/chromium/device/bluetooth/cast/bluetooth_adapter_cast.cc
@@ -213,9 +213,8 @@ void BluetoothAdapterCast::AddDiscoverySession(
// There is no active discovery session, and no pending requests. Enable
// scanning.
- le_scan_manager_->SetScanEnable(
- true, base::BindOnce(&BluetoothAdapterCast::OnScanEnabled,
- weak_factory_.GetWeakPtr()));
+ le_scan_manager_->RequestScan(base::BindOnce(
+ &BluetoothAdapterCast::OnScanEnabled, weak_factory_.GetWeakPtr()));
}
void BluetoothAdapterCast::RemoveDiscoverySession(
@@ -230,8 +229,7 @@ void BluetoothAdapterCast::RemoveDiscoverySession(
(void)discovery_filter;
// If there are pending requests, run the error call immediately.
- if (pending_discovery_requests_.size() > 0u ||
- pending_disable_discovery_request_) {
+ if (pending_discovery_requests_.size() > 0u) {
std::move(error_callback)
.Run(UMABluetoothDiscoverySessionOutcome::REMOVE_WITH_PENDING_REQUEST);
return;
@@ -241,14 +239,14 @@ void BluetoothAdapterCast::RemoveDiscoverySession(
if (num_discovery_sessions_ > 1) {
num_discovery_sessions_--;
callback.Run();
+ return;
}
// This was the last active discovery session. Disable scanning.
- pending_disable_discovery_request_.emplace(discovery_filter, callback,
- std::move(error_callback));
- le_scan_manager_->SetScanEnable(
- false, base::BindOnce(&BluetoothAdapterCast::OnScanDisabled,
- weak_factory_.GetWeakPtr()));
+ num_discovery_sessions_--;
+ DCHECK(scan_handle_);
+ scan_handle_.reset();
+ callback.Run();
}
void BluetoothAdapterCast::SetDiscoveryFilter(
@@ -394,10 +392,11 @@ void BluetoothAdapterCast::AddDevice(
observer.DeviceAdded(this, device);
}
-void BluetoothAdapterCast::OnScanEnabled(bool enabled) {
+void BluetoothAdapterCast::OnScanEnabled(
+ std::unique_ptr<chromecast::bluetooth::LeScanManager::ScanHandle>
+ scan_handle) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
- if (!enabled) {
+ if (!scan_handle) {
LOG(WARNING) << "Failed to start scan.";
// Run the error callback.
@@ -408,13 +407,14 @@ void BluetoothAdapterCast::OnScanEnabled(bool enabled) {
// If there is another pending request, try again.
if (pending_discovery_requests_.size() > 0u) {
- le_scan_manager_->SetScanEnable(
- true, base::BindOnce(&BluetoothAdapterCast::OnScanEnabled,
- weak_factory_.GetWeakPtr()));
+ le_scan_manager_->RequestScan(base::BindOnce(
+ &BluetoothAdapterCast::OnScanEnabled, weak_factory_.GetWeakPtr()));
}
return;
}
+ scan_handle_ = std::move(scan_handle);
+
// The scan has been successfully enabled. Request the initial scan results
// from the scan manager.
le_scan_manager_->GetScanResults(base::BindOnce(
@@ -428,26 +428,6 @@ void BluetoothAdapterCast::OnScanEnabled(bool enabled) {
}
}
-void BluetoothAdapterCast::OnScanDisabled(bool success) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(pending_disable_discovery_request_);
-
- if (!success) {
- LOG(WARNING) << "Failed to stop scan.";
-
- // Run the error callback.
- std::move(pending_disable_discovery_request_->error_callback)
- .Run(UMABluetoothDiscoverySessionOutcome::FAILED);
- pending_disable_discovery_request_ = base::nullopt;
- return;
- }
-
- // The scan has been successfully disabled.
- num_discovery_sessions_--;
- pending_disable_discovery_request_->success_callback.Run();
- pending_disable_discovery_request_ = base::nullopt;
-}
-
void BluetoothAdapterCast::OnGetDevice(
scoped_refptr<chromecast::bluetooth::RemoteDevice> remote_device) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/chromium/device/bluetooth/cast/bluetooth_adapter_cast.h b/chromium/device/bluetooth/cast/bluetooth_adapter_cast.h
index 7dca46652a5..6aa65f5262f 100644
--- a/chromium/device/bluetooth/cast/bluetooth_adapter_cast.h
+++ b/chromium/device/bluetooth/cast/bluetooth_adapter_cast.h
@@ -159,8 +159,9 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterCast
scoped_refptr<chromecast::bluetooth::RemoteDevice> remote_device);
// Called when the scanner has enabled scanning.
- void OnScanEnabled(bool success);
- void OnScanDisabled(bool success);
+ void OnScanEnabled(
+ std::unique_ptr<chromecast::bluetooth::LeScanManager::ScanHandle>
+ scan_handle);
void OnGetDevice(scoped_refptr<chromecast::bluetooth::RemoteDevice> device);
void OnGetScanResults(
std::vector<chromecast::bluetooth::LeScanResult> results);
@@ -178,7 +179,6 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterCast
};
std::queue<DiscoveryParams> pending_discovery_requests_;
- base::Optional<DiscoveryParams> pending_disable_discovery_request_;
int num_discovery_sessions_ = 0;
@@ -189,6 +189,9 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterCast
chromecast::bluetooth::GattClientManager* const gatt_client_manager_;
chromecast::bluetooth::LeScanManager* const le_scan_manager_;
+ std::unique_ptr<chromecast::bluetooth::LeScanManager::ScanHandle>
+ scan_handle_;
+
bool powered_ = true;
bool initialized_ = false;
diff --git a/chromium/device/bluetooth/cast/bluetooth_device_cast.cc b/chromium/device/bluetooth/cast/bluetooth_device_cast.cc
index 32ccb2a546c..4fd0dd01949 100644
--- a/chromium/device/bluetooth/cast/bluetooth_device_cast.cc
+++ b/chromium/device/bluetooth/cast/bluetooth_device_cast.cc
@@ -352,12 +352,7 @@ void BluetoothDeviceCast::CreateGattConnectionImpl() {
}
void BluetoothDeviceCast::DisconnectGatt() {
- DVLOG(2) << __func__ << " pending:" << pending_disconnect_;
- if (pending_disconnect_)
- return;
- pending_disconnect_ = true;
- remote_device_->Disconnect(base::BindOnce(&BluetoothDeviceCast::OnDisconnect,
- weak_factory_.GetWeakPtr()));
+ // The device is intentionally not disconnected.
}
void BluetoothDeviceCast::OnConnect(bool success) {
@@ -367,11 +362,4 @@ void BluetoothDeviceCast::OnConnect(bool success) {
DidFailToConnectGatt(ERROR_FAILED);
}
-void BluetoothDeviceCast::OnDisconnect(bool success) {
- DVLOG(2) << __func__ << " success:" << success;
- pending_disconnect_ = false;
- if (!success)
- LOG(ERROR) << "Request to DisconnectGatt() failed!";
-}
-
} // namespace device
diff --git a/chromium/device/bluetooth/cast/bluetooth_device_cast.h b/chromium/device/bluetooth/cast/bluetooth_device_cast.h
index 0f212b1f357..5adb91449a8 100644
--- a/chromium/device/bluetooth/cast/bluetooth_device_cast.h
+++ b/chromium/device/bluetooth/cast/bluetooth_device_cast.h
@@ -122,9 +122,6 @@ class BluetoothDeviceCast : public BluetoothDevice {
// Called back from connect requests generated from CreateGattConnectionImpl.
void OnConnect(bool success);
- // Called back from disconnect requests.
- void OnDisconnect(bool success);
-
// Called in response to GetServices
void OnGetServices(
std::vector<scoped_refptr<chromecast::bluetooth::RemoteService>>
@@ -132,7 +129,6 @@ class BluetoothDeviceCast : public BluetoothDevice {
bool connected_;
bool pending_connect_ = false;
- bool pending_disconnect_ = false;
const scoped_refptr<chromecast::bluetooth::RemoteDevice> remote_device_;
const std::string address_;
diff --git a/chromium/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.cc b/chromium/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.cc
index 3876ee9d587..9718c43a794 100644
--- a/chromium/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.cc
+++ b/chromium/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.cc
@@ -4,6 +4,8 @@
#include "device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.h"
+#include <memory>
+
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_forward.h"
@@ -94,7 +96,7 @@ BluetoothRemoteGattCharacteristicCast::BluetoothRemoteGattCharacteristicCast(
auto descriptors = remote_characteristic_->GetDescriptors();
descriptors_.reserve(descriptors.size());
for (const auto& descriptor : descriptors) {
- descriptors_.push_back(
+ AddDescriptor(
std::make_unique<BluetoothRemoteGattDescriptorCast>(this, descriptor));
}
}
@@ -130,27 +132,6 @@ BluetoothRemoteGattService* BluetoothRemoteGattCharacteristicCast::GetService()
return service_;
}
-std::vector<BluetoothRemoteGattDescriptor*>
-BluetoothRemoteGattCharacteristicCast::GetDescriptors() const {
- std::vector<BluetoothRemoteGattDescriptor*> descriptors;
- descriptors.reserve(descriptors_.size());
- for (auto& descriptor : descriptors_) {
- descriptors.push_back(descriptor.get());
- }
- return descriptors;
-}
-
-BluetoothRemoteGattDescriptor*
-BluetoothRemoteGattCharacteristicCast::GetDescriptor(
- const std::string& identifier) const {
- for (auto& descriptor : descriptors_) {
- if (descriptor->GetIdentifier() == identifier) {
- return descriptor.get();
- }
- }
- return nullptr;
-}
-
void BluetoothRemoteGattCharacteristicCast::ReadRemoteCharacteristic(
const ValueCallback& callback,
const ErrorCallback& error_callback) {
diff --git a/chromium/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.h b/chromium/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.h
index 8c5a2e332e8..0f001b66574 100644
--- a/chromium/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.h
+++ b/chromium/device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.h
@@ -9,6 +9,7 @@
#include <set>
#include <string>
+#include <utility>
#include <vector>
#include "base/callback.h"
@@ -24,7 +25,6 @@ class RemoteCharacteristic;
namespace device {
-class BluetoothRemoteGattDescriptorCast;
class BluetoothRemoteGattServiceCast;
class BluetoothRemoteGattCharacteristicCast
@@ -45,9 +45,6 @@ class BluetoothRemoteGattCharacteristicCast
// BluetoothRemoteGattCharacteristic implementation:
const std::vector<uint8_t>& GetValue() const override;
BluetoothRemoteGattService* GetService() const override;
- std::vector<BluetoothRemoteGattDescriptor*> GetDescriptors() const override;
- BluetoothRemoteGattDescriptor* GetDescriptor(
- const std::string& identifier) const override;
void ReadRemoteCharacteristic(const ValueCallback& callback,
const ErrorCallback& error_callback) override;
void WriteRemoteCharacteristic(const std::vector<uint8_t>& value,
@@ -92,7 +89,6 @@ class BluetoothRemoteGattCharacteristicCast
remote_characteristic_;
std::vector<uint8_t> value_;
- std::vector<std::unique_ptr<BluetoothRemoteGattDescriptorCast>> descriptors_;
base::WeakPtrFactory<BluetoothRemoteGattCharacteristicCast> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattCharacteristicCast);
@@ -100,4 +96,4 @@ class BluetoothRemoteGattCharacteristicCast
} // namespace device
-#endif // DEVICE_BLUETOOTH_CAST_BLUETOOTH_REMOTE_GATT_CHARACTERISTIC_CAST_H_ \ No newline at end of file
+#endif // DEVICE_BLUETOOTH_CAST_BLUETOOTH_REMOTE_GATT_CHARACTERISTIC_CAST_H_
diff --git a/chromium/device/bluetooth/cast/bluetooth_remote_gatt_service_cast.cc b/chromium/device/bluetooth/cast/bluetooth_remote_gatt_service_cast.cc
index 96f43d9e46c..73d22dab80e 100644
--- a/chromium/device/bluetooth/cast/bluetooth_remote_gatt_service_cast.cc
+++ b/chromium/device/bluetooth/cast/bluetooth_remote_gatt_service_cast.cc
@@ -23,10 +23,9 @@ BluetoothRemoteGattServiceCast::BluetoothRemoteGattServiceCast(
std::vector<scoped_refptr<chromecast::bluetooth::RemoteCharacteristic>>
characteristics = remote_service_->GetCharacteristics();
characteristics_.reserve(characteristics.size());
- for (auto& characteristic : characteristics) {
- characteristics_.push_back(
- std::make_unique<BluetoothRemoteGattCharacteristicCast>(
- this, characteristic));
+ for (const auto& characteristic : characteristics) {
+ AddCharacteristic(std::make_unique<BluetoothRemoteGattCharacteristicCast>(
+ this, characteristic));
}
SetDiscoveryComplete(true);
}
@@ -43,35 +42,16 @@ BluetoothUUID BluetoothRemoteGattServiceCast::GetUUID() const {
bool BluetoothRemoteGattServiceCast::IsPrimary() const {
return remote_service_->primary();
-};
+}
BluetoothDevice* BluetoothRemoteGattServiceCast::GetDevice() const {
return device_;
}
-std::vector<BluetoothRemoteGattCharacteristic*>
-BluetoothRemoteGattServiceCast::GetCharacteristics() const {
- std::vector<BluetoothRemoteGattCharacteristic*> results;
- results.reserve(characteristics_.size());
- for (auto& characteristic : characteristics_)
- results.push_back(characteristic.get());
- return results;
-}
-
std::vector<BluetoothRemoteGattService*>
BluetoothRemoteGattServiceCast::GetIncludedServices() const {
NOTIMPLEMENTED();
return std::vector<BluetoothRemoteGattService*>();
}
-BluetoothRemoteGattCharacteristic*
-BluetoothRemoteGattServiceCast::GetCharacteristic(
- const std::string& identifier) const {
- for (auto& characteristic : characteristics_) {
- if (characteristic->GetIdentifier() == identifier)
- return characteristic.get();
- }
- return nullptr;
-}
-
} // namespace device
diff --git a/chromium/device/bluetooth/cast/bluetooth_remote_gatt_service_cast.h b/chromium/device/bluetooth/cast/bluetooth_remote_gatt_service_cast.h
index 223a5eff4ce..8280cac043c 100644
--- a/chromium/device/bluetooth/cast/bluetooth_remote_gatt_service_cast.h
+++ b/chromium/device/bluetooth/cast/bluetooth_remote_gatt_service_cast.h
@@ -23,7 +23,6 @@ class RemoteService;
namespace device {
class BluetoothDeviceCast;
-class BluetoothRemoteGattCharacteristicCast;
class BluetoothRemoteGattServiceCast : public BluetoothRemoteGattService {
public:
@@ -39,19 +38,12 @@ class BluetoothRemoteGattServiceCast : public BluetoothRemoteGattService {
// BluetoothRemoteGattService implementation:
BluetoothDevice* GetDevice() const override;
- std::vector<BluetoothRemoteGattCharacteristic*> GetCharacteristics()
- const override;
std::vector<BluetoothRemoteGattService*> GetIncludedServices() const override;
- BluetoothRemoteGattCharacteristic* GetCharacteristic(
- const std::string& identifier) const override;
private:
BluetoothDeviceCast* const device_;
scoped_refptr<chromecast::bluetooth::RemoteService> remote_service_;
- std::vector<std::unique_ptr<BluetoothRemoteGattCharacteristicCast>>
- characteristics_;
-
DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattServiceCast);
};
diff --git a/chromium/device/bluetooth/chromeos/bluetooth_utils.cc b/chromium/device/bluetooth/chromeos/bluetooth_utils.cc
index 71f45bf0644..fcc64506be9 100644
--- a/chromium/device/bluetooth/chromeos/bluetooth_utils.cc
+++ b/chromium/device/bluetooth/chromeos/bluetooth_utils.cc
@@ -4,6 +4,9 @@
#include "device/bluetooth/chromeos/bluetooth_utils.h"
+#include "base/feature_list.h"
+#include "device/base/features.h"
+
namespace device {
namespace {
@@ -46,6 +49,9 @@ BluetoothAdapter::DeviceList GetLimitedNumDevices(
// Filter out unknown devices from the list.
BluetoothAdapter::DeviceList FilterUnknownDevices(
const BluetoothAdapter::DeviceList& devices) {
+ if (base::FeatureList::IsEnabled(device::kUnfilteredBluetoothDevices))
+ return devices;
+
BluetoothAdapter::DeviceList result;
for (BluetoothDevice* device : devices) {
switch (device->GetType()) {
diff --git a/chromium/device/bluetooth/dbus/bluetooth_device_client.cc b/chromium/device/bluetooth/dbus/bluetooth_device_client.cc
index 9a69b3ef415..4ca96ede4dc 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_device_client.cc
+++ b/chromium/device/bluetooth/dbus/bluetooth_device_client.cc
@@ -503,6 +503,52 @@ class BluetoothDeviceClientImpl : public BluetoothDeviceClient,
weak_ptr_factory_.GetWeakPtr(), error_callback));
}
+ void ExecuteWrite(const dbus::ObjectPath& object_path,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) override {
+ dbus::MethodCall method_call(bluetooth_device::kBluetoothDeviceInterface,
+ bluetooth_device::kExecuteWrite);
+
+ dbus::ObjectProxy* object_proxy =
+ object_manager_->GetObjectProxy(object_path);
+ if (!object_proxy) {
+ error_callback.Run(kUnknownDeviceError, "");
+ return;
+ }
+
+ dbus::MessageWriter writer(&method_call);
+ writer.AppendBool(true);
+ object_proxy->CallMethodWithErrorCallback(
+ &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+ base::BindOnce(&BluetoothDeviceClientImpl::OnSuccess,
+ weak_ptr_factory_.GetWeakPtr(), callback),
+ base::BindOnce(&BluetoothDeviceClientImpl::OnError,
+ weak_ptr_factory_.GetWeakPtr(), error_callback));
+ }
+
+ void AbortWrite(const dbus::ObjectPath& object_path,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) override {
+ dbus::MethodCall method_call(bluetooth_device::kBluetoothDeviceInterface,
+ bluetooth_device::kExecuteWrite);
+
+ dbus::ObjectProxy* object_proxy =
+ object_manager_->GetObjectProxy(object_path);
+ if (!object_proxy) {
+ error_callback.Run(kUnknownDeviceError, "");
+ return;
+ }
+
+ dbus::MessageWriter writer(&method_call);
+ writer.AppendBool(false);
+ object_proxy->CallMethodWithErrorCallback(
+ &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+ base::BindOnce(&BluetoothDeviceClientImpl::OnSuccess,
+ weak_ptr_factory_.GetWeakPtr(), callback),
+ base::BindOnce(&BluetoothDeviceClientImpl::OnError,
+ weak_ptr_factory_.GetWeakPtr(), error_callback));
+ }
+
protected:
void Init(dbus::Bus* bus,
const std::string& bluetooth_service_name) override {
diff --git a/chromium/device/bluetooth/dbus/bluetooth_device_client.h b/chromium/device/bluetooth/dbus/bluetooth_device_client.h
index d670b360f67..a59f4242617 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_device_client.h
+++ b/chromium/device/bluetooth/dbus/bluetooth_device_client.h
@@ -242,6 +242,16 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceClient : public BluezDBusClient {
const ServiceRecordsCallback& callback,
const ErrorCallback& error_callback) = 0;
+ // Executes all the privous prepare writes in a reliable write session.
+ virtual void ExecuteWrite(const dbus::ObjectPath& object_path,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) = 0;
+
+ // Aborts all the privous prepare writes in a reliable write session.
+ virtual void AbortWrite(const dbus::ObjectPath& object_path,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) = 0;
+
// Creates the instance.
static BluetoothDeviceClient* Create();
diff --git a/chromium/device/bluetooth/dbus/bluetooth_gatt_application_service_provider.cc b/chromium/device/bluetooth/dbus/bluetooth_gatt_application_service_provider.cc
index 600b8a6f133..dc344cd8fdf 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_gatt_application_service_provider.cc
+++ b/chromium/device/bluetooth/dbus/bluetooth_gatt_application_service_provider.cc
@@ -22,8 +22,9 @@ namespace bluez {
namespace {
-const std::vector<std::string> FlagsFromProperties(
- device::BluetoothGattCharacteristic::Properties properties) {
+const std::vector<std::string> FlagsFromPropertiesAndPermissions(
+ device::BluetoothGattCharacteristic::Properties properties,
+ device::BluetoothGattCharacteristic::Permissions permissions) {
static_assert(
device::BluetoothGattCharacteristic::NUM_PROPERTY == 1 << 14,
"Update required if the number of characteristic properties changes.");
@@ -66,6 +67,24 @@ const std::vector<std::string> FlagsFromProperties(
PROPERTY_WRITE_ENCRYPTED_AUTHENTICATED)
flags.push_back(
bluetooth_gatt_characteristic::kFlagEncryptAuthenticatedWrite);
+ if (permissions & device::BluetoothGattCharacteristic::PERMISSION_READ)
+ flags.push_back(bluetooth_gatt_characteristic::kFlagPermissionRead);
+ if (permissions & device::BluetoothGattCharacteristic::PERMISSION_WRITE)
+ flags.push_back(bluetooth_gatt_characteristic::kFlagPermissionWrite);
+ if (permissions &
+ device::BluetoothGattCharacteristic::PERMISSION_READ_ENCRYPTED)
+ flags.push_back(bluetooth_gatt_characteristic::kFlagPermissionEncryptRead);
+ if (permissions &
+ device::BluetoothGattCharacteristic::PERMISSION_WRITE_ENCRYPTED)
+ flags.push_back(bluetooth_gatt_characteristic::kFlagPermissionEncryptWrite);
+ if (permissions & device::BluetoothGattCharacteristic::
+ PERMISSION_READ_ENCRYPTED_AUTHENTICATED)
+ flags.push_back(
+ bluetooth_gatt_characteristic::kFlagPermissionAuthenticatedRead);
+ if (permissions & device::BluetoothGattCharacteristic::
+ PERMISSION_WRITE_ENCRYPTED_AUTHENTICATED)
+ flags.push_back(
+ bluetooth_gatt_characteristic::kFlagPermissionAuthenticatedWrite);
return flags;
}
@@ -119,7 +138,9 @@ void BluetoothGattApplicationServiceProvider::CreateAttributeServiceProviders(
std::make_unique<BluetoothGattCharacteristicDelegateWrapper>(
service.second, characteristic.second.get()),
characteristic.second->GetUUID().value(),
- FlagsFromProperties(characteristic.second->GetProperties()),
+ FlagsFromPropertiesAndPermissions(
+ characteristic.second->GetProperties(),
+ characteristic.second->GetPermissions()),
service.second->object_path())));
for (const auto& descriptor : characteristic.second->GetDescriptors()) {
descriptor_providers_.push_back(
diff --git a/chromium/device/bluetooth/dbus/bluetooth_gatt_attribute_helpers.cc b/chromium/device/bluetooth/dbus/bluetooth_gatt_attribute_helpers.cc
index 8cfcb373505..1aa4070956c 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_gatt_attribute_helpers.cc
+++ b/chromium/device/bluetooth/dbus/bluetooth_gatt_attribute_helpers.cc
@@ -12,32 +12,28 @@
namespace bluez {
-namespace {
-
-constexpr char kDeviceField[] = "device";
-
-} // namespace
-
-dbus::ObjectPath ReadDevicePath(dbus::MessageReader* reader) {
+bool ReadOptions(dbus::MessageReader* reader,
+ std::map<std::string, dbus::MessageReader>* options) {
dbus::MessageReader array_reader(nullptr);
- if (!reader->PopArray(&array_reader))
- return dbus::ObjectPath();
+ if (!reader->PopArray(&array_reader) || options == nullptr)
+ return false;
- // Go through all the keys in the dictionary to find the device key.
+ dbus::MessageReader dict_entry_reader(nullptr);
+ std::string key;
while (array_reader.HasMoreData()) {
- dbus::MessageReader dict_entry_reader(nullptr);
- std::string key;
if (!array_reader.PopDictEntry(&dict_entry_reader) ||
!dict_entry_reader.PopString(&key)) {
- return dbus::ObjectPath();
- } else if (key == kDeviceField) {
- dbus::ObjectPath device_path;
- dict_entry_reader.PopVariantOfObjectPath(&device_path);
- return device_path;
+ options->clear();
+ return false;
}
- }
- return dbus::ObjectPath();
+ options->emplace(key, nullptr);
+ if (!dict_entry_reader.PopVariant(&options->at(key))) {
+ options->clear();
+ return false;
+ }
+ }
+ return true;
}
} // namespace bluez
diff --git a/chromium/device/bluetooth/dbus/bluetooth_gatt_attribute_helpers.h b/chromium/device/bluetooth/dbus/bluetooth_gatt_attribute_helpers.h
index 9b16670f5c3..b1562657c04 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_gatt_attribute_helpers.h
+++ b/chromium/device/bluetooth/dbus/bluetooth_gatt_attribute_helpers.h
@@ -5,6 +5,8 @@
#ifndef DEVICE_BLUETOOTH_DBUS_BLUETOOTH_GATT_ATTRIBUTES_HELPER_H_
#define DEVICE_BLUETOOTH_DBUS_BLUETOOTH_GATT_ATTRIBUTES_HELPER_H_
+#include <map>
+
#include "dbus/object_path.h"
namespace dbus {
@@ -14,7 +16,8 @@ class MessageReader;
namespace bluez {
// Helper methods used from various GATT attribute providers and clients.
-dbus::ObjectPath ReadDevicePath(dbus::MessageReader* reader);
+bool ReadOptions(dbus::MessageReader* reader,
+ std::map<std::string, dbus::MessageReader>* options);
} // namespace bluez
diff --git a/chromium/device/bluetooth/dbus/bluetooth_gatt_attribute_value_delegate.h b/chromium/device/bluetooth/dbus/bluetooth_gatt_attribute_value_delegate.h
index 80e6e3eafae..f1b54d27bd8 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_gatt_attribute_value_delegate.h
+++ b/chromium/device/bluetooth/dbus/bluetooth_gatt_attribute_value_delegate.h
@@ -9,6 +9,7 @@
#include <vector>
#include "base/callback_forward.h"
+#include "device/bluetooth/bluetooth_gatt_characteristic.h"
#include "device/bluetooth/bluetooth_local_gatt_service.h"
namespace dbus {
@@ -62,12 +63,28 @@ class BluetoothGattAttributeValueDelegate {
// This method will be called, when a remote device requests to start sending
// notifications for this characteristic. This will never be called for
// descriptors.
- virtual void StartNotifications() = 0;
+ virtual void StartNotifications(
+ const dbus::ObjectPath& device_path,
+ device::BluetoothGattCharacteristic::NotificationType
+ notification_type) = 0;
// This method will be called, when a remote device requests to stop sending
// notifications for this characteristic. This will never be called for
// descriptors.
- virtual void StopNotifications() = 0;
+ virtual void StopNotifications(const dbus::ObjectPath& device_path) = 0;
+
+ // This method will be called, when a remote device requests to prepare
+ // write the value of the exported GATT characteristic. Invoke |callback| to
+ // report that the request was successful. Invoke |error_callback| to report
+ // a failure. This will never be called for descriptors.
+ virtual void PrepareSetValue(
+ const dbus::ObjectPath& device_path,
+ const std::vector<uint8_t>& value,
+ int offset,
+ bool has_subsequent_request,
+ const base::Closure& callback,
+ const device::BluetoothLocalGattService::Delegate::ErrorCallback&
+ error_callback) {}
protected:
// Gets the Bluetooth device object on the current service's adapter with
diff --git a/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc b/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc
index 0633898a8c3..22ab4de9416 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc
+++ b/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc
@@ -140,10 +140,42 @@ class BluetoothGattCharacteristicClientImpl
weak_ptr_factory_.GetWeakPtr(), error_callback));
}
+ void PrepareWriteValue(const dbus::ObjectPath& object_path,
+ const std::vector<uint8_t>& value,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) override {
+ dbus::ObjectProxy* object_proxy =
+ object_manager_->GetObjectProxy(object_path);
+ if (!object_proxy) {
+ error_callback.Run(kUnknownCharacteristicError, "");
+ return;
+ }
+
+ dbus::MethodCall method_call(
+ bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface,
+ bluetooth_gatt_characteristic::kPrepareWriteValue);
+ dbus::MessageWriter writer(&method_call);
+ writer.AppendArrayOfBytes(value.data(), value.size());
+
+ base::DictionaryValue dict;
+ dbus::AppendValueData(&writer, dict);
+
+ object_proxy->CallMethodWithErrorCallback(
+ &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+ base::BindOnce(&BluetoothGattCharacteristicClientImpl::OnSuccess,
+ weak_ptr_factory_.GetWeakPtr(), callback),
+ base::BindOnce(&BluetoothGattCharacteristicClientImpl::OnError,
+ weak_ptr_factory_.GetWeakPtr(), error_callback));
+ }
+
// BluetoothGattCharacteristicClient override.
- void StartNotify(const dbus::ObjectPath& object_path,
- const base::Closure& callback,
- const ErrorCallback& error_callback) override {
+ void StartNotify(
+ const dbus::ObjectPath& object_path,
+#if defined(OS_CHROMEOS)
+ device::BluetoothGattCharacteristic::NotificationType notification_type,
+#endif
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) override {
dbus::ObjectProxy* object_proxy =
object_manager_->GetObjectProxy(object_path);
if (!object_proxy) {
@@ -154,6 +186,10 @@ class BluetoothGattCharacteristicClientImpl
dbus::MethodCall method_call(
bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface,
bluetooth_gatt_characteristic::kStartNotify);
+#if defined(OS_CHROMEOS)
+ dbus::MessageWriter writer(&method_call);
+ writer.AppendByte(static_cast<uint8_t>(notification_type));
+#endif
object_proxy->CallMethodWithErrorCallback(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
diff --git a/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h b/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h
index 85851c3628f..9a23c10340b 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h
+++ b/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h
@@ -15,6 +15,7 @@
#include "dbus/object_path.h"
#include "dbus/property.h"
#include "device/bluetooth/bluetooth_export.h"
+#include "device/bluetooth/bluetooth_gatt_characteristic.h"
#include "device/bluetooth/dbus/bluez_dbus_client.h"
namespace bluez {
@@ -109,12 +110,28 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothGattCharacteristicClient
const base::Closure& callback,
const ErrorCallback& error_callback) = 0;
+ // Issues a request to prepare write the value of GATT characteristic with
+ // object path |object_path| with value |value|.
+ // Invokes |callback| on success and |error_callback| on failure.
+ virtual void PrepareWriteValue(const dbus::ObjectPath& object_path,
+ const std::vector<uint8_t>& value,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) = 0;
+
// Starts a notification session from this characteristic with object path
// |object_path| if it supports value notifications or indications. Invokes
// |callback| on success and |error_callback| on failure.
+#if defined(OS_CHROMEOS)
+ virtual void StartNotify(
+ const dbus::ObjectPath& object_path,
+ device::BluetoothGattCharacteristic::NotificationType notification_type,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) = 0;
+#else
virtual void StartNotify(const dbus::ObjectPath& object_path,
const base::Closure& callback,
const ErrorCallback& error_callback) = 0;
+#endif
// Cancels any previous StartNotify transaction for characteristic with
// object path |object_path|. Invokes |callback| on success and
diff --git a/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.cc b/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.cc
index 12bad3daf69..241dc4e50b4 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.cc
+++ b/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.cc
@@ -36,12 +36,30 @@ void BluetoothGattCharacteristicDelegateWrapper::SetValue(
error_callback);
}
-void BluetoothGattCharacteristicDelegateWrapper::StartNotifications() {
- service()->GetDelegate()->OnNotificationsStart(nullptr, characteristic_);
+void BluetoothGattCharacteristicDelegateWrapper::StartNotifications(
+ const dbus::ObjectPath& device_path,
+ device::BluetoothGattCharacteristic::NotificationType notification_type) {
+ service()->GetDelegate()->OnNotificationsStart(
+ GetDeviceWithPath(device_path), notification_type, characteristic_);
+}
+
+void BluetoothGattCharacteristicDelegateWrapper::StopNotifications(
+ const dbus::ObjectPath& device_path) {
+ service()->GetDelegate()->OnNotificationsStop(GetDeviceWithPath(device_path),
+ characteristic_);
}
-void BluetoothGattCharacteristicDelegateWrapper::StopNotifications() {
- service()->GetDelegate()->OnNotificationsStop(nullptr, characteristic_);
+void BluetoothGattCharacteristicDelegateWrapper::PrepareSetValue(
+ const dbus::ObjectPath& device_path,
+ const std::vector<uint8_t>& value,
+ int offset,
+ bool has_subsequent_request,
+ const base::Closure& callback,
+ const device::BluetoothLocalGattService::Delegate::ErrorCallback&
+ error_callback) {
+ service()->GetDelegate()->OnCharacteristicPrepareWriteRequest(
+ GetDeviceWithPath(device_path), characteristic_, value, offset,
+ has_subsequent_request, callback, error_callback);
}
} // namespace bluez
diff --git a/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.h b/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.h
index 0816bc29dd2..7ab62c1cb19 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.h
+++ b/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.h
@@ -10,6 +10,7 @@
#include "base/callback_forward.h"
#include "base/macros.h"
+#include "device/bluetooth/bluetooth_gatt_characteristic.h"
#include "device/bluetooth/bluetooth_local_gatt_service.h"
#include "device/bluetooth/bluez/bluetooth_gatt_service_bluez.h"
#include "device/bluetooth/bluez/bluetooth_local_gatt_service_bluez.h"
@@ -40,8 +41,18 @@ class BluetoothGattCharacteristicDelegateWrapper
const base::Closure& callback,
const device::BluetoothLocalGattService::Delegate::ErrorCallback&
error_callback) override;
- void StartNotifications() override;
- void StopNotifications() override;
+ void PrepareSetValue(
+ const dbus::ObjectPath& device_path,
+ const std::vector<uint8_t>& value,
+ int offset,
+ bool has_subsequent_request,
+ const base::Closure& callback,
+ const device::BluetoothLocalGattService::Delegate::ErrorCallback&
+ error_callback) override;
+ void StartNotifications(const dbus::ObjectPath& device_path,
+ device::BluetoothGattCharacteristic::NotificationType
+ notification_type) override;
+ void StopNotifications(const dbus::ObjectPath& device_path) override;
private:
BluetoothLocalGattCharacteristicBlueZ* characteristic_;
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 51e38200604..813f11eea21 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
@@ -9,7 +9,9 @@
#include "base/bind.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
+#include "device/bluetooth/bluetooth_gatt_characteristic.h"
#include "device/bluetooth/dbus/bluetooth_gatt_attribute_helpers.h"
+#include "device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace bluez {
@@ -90,6 +92,14 @@ BluetoothGattCharacteristicServiceProviderImpl::
weak_ptr_factory_.GetWeakPtr()));
exported_object_->ExportMethod(
bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface,
+ bluetooth_gatt_characteristic::kPrepareWriteValue,
+ base::Bind(
+ &BluetoothGattCharacteristicServiceProviderImpl::PrepareWriteValue,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnExported,
+ weak_ptr_factory_.GetWeakPtr()));
+ exported_object_->ExportMethod(
+ bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface,
bluetooth_gatt_characteristic::kStartNotify,
base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::StartNotify,
weak_ptr_factory_.GetWeakPtr()),
@@ -122,9 +132,9 @@ void BluetoothGattCharacteristicServiceProviderImpl::SendValueChanged(
dbus::Signal signal(dbus::kDBusPropertiesInterface,
dbus::kDBusPropertiesChangedSignal);
dbus::MessageWriter writer(&signal);
- dbus::MessageWriter array_writer(NULL);
- dbus::MessageWriter dict_entry_writer(NULL);
- dbus::MessageWriter variant_writer(NULL);
+ dbus::MessageWriter array_writer(nullptr);
+ dbus::MessageWriter dict_entry_writer(nullptr);
+ dbus::MessageWriter variant_writer(nullptr);
// interface_name
writer.AppendString(
@@ -186,7 +196,7 @@ void BluetoothGattCharacteristicServiceProviderImpl::Get(
std::unique_ptr<dbus::Response> response =
dbus::Response::FromMethodCall(method_call);
dbus::MessageWriter writer(response.get());
- dbus::MessageWriter variant_writer(NULL);
+ dbus::MessageWriter variant_writer(nullptr);
if (property_name == bluetooth_gatt_characteristic::kUUIDProperty) {
writer.OpenVariant("s", &variant_writer);
@@ -267,7 +277,13 @@ void BluetoothGattCharacteristicServiceProviderImpl::ReadValue(
DCHECK(OnOriginThread());
dbus::MessageReader reader(method_call);
- dbus::ObjectPath device_path = ReadDevicePath(&reader);
+ std::map<std::string, dbus::MessageReader> options;
+ dbus::ObjectPath device_path;
+ ReadOptions(&reader, &options);
+ auto it = options.find(bluetooth_gatt_characteristic::kOptionDevice);
+ if (it != options.end())
+ it->second.PopObjectPath(&device_path);
+
if (device_path.value().empty()) {
LOG(WARNING) << "ReadValue called with incorrect parameters: "
<< method_call->ToString();
@@ -292,7 +308,7 @@ void BluetoothGattCharacteristicServiceProviderImpl::WriteValue(
DCHECK(OnOriginThread());
dbus::MessageReader reader(method_call);
- const uint8_t* bytes = NULL;
+ const uint8_t* bytes = nullptr;
size_t length = 0;
std::vector<uint8_t> value;
@@ -304,7 +320,13 @@ void BluetoothGattCharacteristicServiceProviderImpl::WriteValue(
if (bytes)
value.assign(bytes, bytes + length);
- dbus::ObjectPath device_path = ReadDevicePath(&reader);
+ std::map<std::string, dbus::MessageReader> options;
+ dbus::ObjectPath device_path;
+ ReadOptions(&reader, &options);
+ auto it = options.find(bluetooth_gatt_characteristic::kOptionDevice);
+ if (it != options.end())
+ it->second.PopObjectPath(&device_path);
+
if (device_path.value().empty()) {
LOG(WARNING) << "WriteValue called with incorrect parameters: "
<< method_call->ToString();
@@ -321,14 +343,86 @@ void BluetoothGattCharacteristicServiceProviderImpl::WriteValue(
weak_ptr_factory_.GetWeakPtr(), method_call, response_sender));
}
+void BluetoothGattCharacteristicServiceProviderImpl::PrepareWriteValue(
+ dbus::MethodCall* method_call,
+ dbus::ExportedObject::ResponseSender response_sender) {
+ VLOG(3) << "BluetoothGattCharacteristicServiceProvider::PrepareWriteValue: "
+ << object_path_.value();
+ DCHECK(OnOriginThread());
+
+ dbus::MessageReader reader(method_call);
+ const uint8_t* bytes = nullptr;
+ size_t length = 0;
+
+ std::vector<uint8_t> value;
+ if (!reader.PopArrayOfBytes(&bytes, &length)) {
+ LOG(WARNING) << "Error reading value parameter. PrepareWriteValue called "
+ << "with incorrect parameters: " << method_call->ToString();
+ }
+ if (bytes)
+ value.assign(bytes, bytes + length);
+
+ std::map<std::string, dbus::MessageReader> options;
+ dbus::ObjectPath device_path;
+ uint16_t offset = 0;
+ bool has_subsequent_write = false;
+ ReadOptions(&reader, &options);
+ auto it = options.find(bluetooth_gatt_characteristic::kOptionDevice);
+ if (it != options.end())
+ it->second.PopObjectPath(&device_path);
+ 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");
+ if (it != options.end())
+ it->second.PopBool(&has_subsequent_write);
+
+ if (device_path.value().empty()) {
+ LOG(WARNING) << "PrepareWriteValue called with incorrect parameters: "
+ << method_call->ToString();
+ // Continue on with an empty device path. This will return a null device to
+ // the delegate, which should know how to handle it.
+ }
+
+ DCHECK(delegate_);
+ delegate_->PrepareSetValue(
+ device_path, value, offset, has_subsequent_write,
+ base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnWriteValue,
+ weak_ptr_factory_.GetWeakPtr(), method_call, response_sender),
+ base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnFailure,
+ weak_ptr_factory_.GetWeakPtr(), method_call, response_sender));
+}
+
void BluetoothGattCharacteristicServiceProviderImpl::StartNotify(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
VLOG(3) << "BluetoothGattCharacteristicServiceProvider::StartNotify: "
<< object_path_.value();
DCHECK(OnOriginThread());
+
+ dbus::MessageReader reader(method_call);
+ uint8_t cccd_value = 0;
+ if (!reader.PopByte(&cccd_value)) {
+ LOG(WARNING) << "Error reading cccd_value parameter. StartNotify called "
+ << "with incorrect parameters: " << method_call->ToString();
+ }
+
+ std::map<std::string, dbus::MessageReader> options;
+ dbus::ObjectPath device_path;
+ ReadOptions(&reader, &options);
+ auto it = options.find(bluetooth_gatt_characteristic::kOptionDevice);
+ if (it != options.end())
+ it->second.PopObjectPath(&device_path);
+
DCHECK(delegate_);
- delegate_->StartNotifications();
+ delegate_->StartNotifications(
+ device_path,
+ cccd_value == static_cast<uint8_t>(device::BluetoothGattCharacteristic::
+ NotificationType::kIndication)
+ ? device::BluetoothGattCharacteristic::NotificationType::kIndication
+ : device::BluetoothGattCharacteristic::NotificationType::
+ kNotification);
}
void BluetoothGattCharacteristicServiceProviderImpl::StopNotify(
@@ -338,8 +432,16 @@ void BluetoothGattCharacteristicServiceProviderImpl::StopNotify(
<< object_path_.value();
DCHECK(OnOriginThread());
+ dbus::MessageReader reader(method_call);
+ std::map<std::string, dbus::MessageReader> options;
+ dbus::ObjectPath device_path;
+ ReadOptions(&reader, &options);
+ auto it = options.find(bluetooth_gatt_characteristic::kOptionDevice);
+ if (it != options.end())
+ it->second.PopObjectPath(&device_path);
+
DCHECK(delegate_);
- delegate_->StopNotifications();
+ delegate_->StopNotifications(device_path);
}
void BluetoothGattCharacteristicServiceProviderImpl::OnExported(
@@ -376,9 +478,9 @@ void BluetoothGattCharacteristicServiceProviderImpl::OnWriteValue(
void BluetoothGattCharacteristicServiceProviderImpl::WriteProperties(
dbus::MessageWriter* writer) {
- dbus::MessageWriter array_writer(NULL);
- dbus::MessageWriter dict_entry_writer(NULL);
- dbus::MessageWriter variant_writer(NULL);
+ dbus::MessageWriter array_writer(nullptr);
+ dbus::MessageWriter dict_entry_writer(nullptr);
+ dbus::MessageWriter variant_writer(nullptr);
writer->OpenArray("{sv}", &array_writer);
diff --git a/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.h b/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.h
index b9308760b48..e2fc5f17c56 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.h
+++ b/chromium/device/bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_impl.h
@@ -70,6 +70,11 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothGattCharacteristicServiceProviderImpl
void WriteValue(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
+ // Called by BlueZ when a remote central is requesting to prepare the reliable
+ // write value of this characteristic.
+ void PrepareWriteValue(dbus::MethodCall* method_call,
+ dbus::ExportedObject::ResponseSender response_sender);
+
// Called by BlueZ when a remote central is requesting to start a
// notification session for this characteristic.
void StartNotify(dbus::MethodCall* method_call,
diff --git a/chromium/device/bluetooth/dbus/bluetooth_gatt_descriptor_delegate_wrapper.h b/chromium/device/bluetooth/dbus/bluetooth_gatt_descriptor_delegate_wrapper.h
index 383207ce3c3..d365f1c2f5c 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_gatt_descriptor_delegate_wrapper.h
+++ b/chromium/device/bluetooth/dbus/bluetooth_gatt_descriptor_delegate_wrapper.h
@@ -10,6 +10,7 @@
#include "base/callback_forward.h"
#include "base/macros.h"
+#include "device/bluetooth/bluetooth_gatt_characteristic.h"
#include "device/bluetooth/bluetooth_local_gatt_service.h"
#include "device/bluetooth/bluez/bluetooth_gatt_service_bluez.h"
#include "device/bluetooth/bluez/bluetooth_local_gatt_service_bluez.h"
@@ -40,8 +41,11 @@ class BluetoothGattDescriptorDelegateWrapper
const base::Closure& callback,
const device::BluetoothLocalGattService::Delegate::ErrorCallback&
error_callback) override;
- void StartNotifications() override {}
- void StopNotifications() override {}
+
+ void StartNotifications(const dbus::ObjectPath& device_path,
+ device::BluetoothGattCharacteristic::NotificationType
+ notification_type) override {}
+ void StopNotifications(const dbus::ObjectPath& device_path) override {}
private:
BluetoothLocalGattDescriptorBlueZ* descriptor_;
diff --git a/chromium/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.cc b/chromium/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.cc
index 0eb37f8e401..26b4aa73fde 100644
--- a/chromium/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.cc
+++ b/chromium/device/bluetooth/dbus/bluetooth_gatt_descriptor_service_provider_impl.cc
@@ -252,7 +252,13 @@ void BluetoothGattDescriptorServiceProviderImpl::ReadValue(
DCHECK(OnOriginThread());
dbus::MessageReader reader(method_call);
- dbus::ObjectPath device_path = ReadDevicePath(&reader);
+ std::map<std::string, dbus::MessageReader> options;
+ dbus::ObjectPath device_path;
+ ReadOptions(&reader, &options);
+ auto it = options.find(bluetooth_gatt_descriptor::kOptionDevice);
+ if (it != options.end())
+ it->second.PopObjectPath(&device_path);
+
if (device_path.value().empty()) {
LOG(WARNING) << "ReadValue called with incorrect parameters: "
<< method_call->ToString();
@@ -289,7 +295,13 @@ void BluetoothGattDescriptorServiceProviderImpl::WriteValue(
if (bytes)
value.assign(bytes, bytes + length);
- dbus::ObjectPath device_path = ReadDevicePath(&reader);
+ std::map<std::string, dbus::MessageReader> options;
+ dbus::ObjectPath device_path;
+ ReadOptions(&reader, &options);
+ auto it = options.find(bluetooth_gatt_descriptor::kOptionDevice);
+ if (it != options.end())
+ it->second.PopObjectPath(&device_path);
+
if (device_path.value().empty()) {
LOG(WARNING) << "WriteValue called with incorrect parameters: "
<< method_call->ToString();
diff --git a/chromium/device/bluetooth/dbus/bluez_dbus_manager.cc b/chromium/device/bluetooth/dbus/bluez_dbus_manager.cc
index a76d08ae1f6..c58018c959f 100644
--- a/chromium/device/bluetooth/dbus/bluez_dbus_manager.cc
+++ b/chromium/device/bluetooth/dbus/bluez_dbus_manager.cc
@@ -212,7 +212,12 @@ void BluezDBusManager::InitializeClients() {
}
std::string BluezDBusManager::GetBluetoothServiceName() {
- return base::FeatureList::IsEnabled(device::kNewblueDaemon)
+ bool use_newblue = false;
+#if defined(OS_CHROMEOS)
+ use_newblue = base::FeatureList::IsEnabled(device::kNewblueDaemon);
+#endif // defined(OS_CHROMEOS)
+
+ return use_newblue
? bluetooth_object_manager::kBluetoothObjectManagerServiceName
: bluez_object_manager::kBluezObjectManagerServiceName;
}
diff --git a/chromium/device/bluetooth/dbus/fake_bluetooth_device_client.cc b/chromium/device/bluetooth/dbus/fake_bluetooth_device_client.cc
index ebe5e78ddd2..8dfcd20fd17 100644
--- a/chromium/device/bluetooth/dbus/fake_bluetooth_device_client.cc
+++ b/chromium/device/bluetooth/dbus/fake_bluetooth_device_client.cc
@@ -29,6 +29,7 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "device/bluetooth/bluez/bluetooth_service_attribute_value_bluez.h"
+#include "device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h"
#include "device/bluetooth/dbus/bluez_dbus_manager.h"
#include "device/bluetooth/dbus/fake_bluetooth_adapter_client.h"
#include "device/bluetooth/dbus/fake_bluetooth_agent_manager_client.h"
@@ -625,6 +626,28 @@ void FakeBluetoothDeviceClient::GetServiceRecords(
callback.Run(CreateFakeServiceRecords());
}
+void FakeBluetoothDeviceClient::ExecuteWrite(
+ const dbus::ObjectPath& object_path,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ for (const auto& prepare_write_request : prepare_write_requests_) {
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothGattCharacteristicClient()
+ ->WriteValue(prepare_write_request.first, prepare_write_request.second,
+ base::DoNothing(), base::DoNothing());
+ }
+ prepare_write_requests_.clear();
+ callback.Run();
+}
+
+void FakeBluetoothDeviceClient::AbortWrite(
+ const dbus::ObjectPath& object_path,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ prepare_write_requests_.clear();
+ callback.Run();
+}
+
void FakeBluetoothDeviceClient::BeginDiscoverySimulation(
const dbus::ObjectPath& adapter_path) {
VLOG(1) << "starting discovery simulation";
@@ -1885,4 +1908,10 @@ void FakeBluetoothDeviceClient::CreateTestDevice(
observer.DeviceAdded(device_path);
}
+void FakeBluetoothDeviceClient::AddPrepareWriteRequest(
+ const dbus::ObjectPath& object_path,
+ const std::vector<uint8_t>& value) {
+ prepare_write_requests_.emplace_back(object_path, value);
+}
+
} // namespace bluez
diff --git a/chromium/device/bluetooth/dbus/fake_bluetooth_device_client.h b/chromium/device/bluetooth/dbus/fake_bluetooth_device_client.h
index cef85544f13..0b0d8e44f65 100644
--- a/chromium/device/bluetooth/dbus/fake_bluetooth_device_client.h
+++ b/chromium/device/bluetooth/dbus/fake_bluetooth_device_client.h
@@ -109,6 +109,12 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothDeviceClient
void GetServiceRecords(const dbus::ObjectPath& object_path,
const ServiceRecordsCallback& callback,
const ErrorCallback& error_callback) override;
+ void ExecuteWrite(const dbus::ObjectPath& object_path,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) override;
+ void AbortWrite(const dbus::ObjectPath& object_path,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) override;
void SetSimulationIntervalMs(int interval_ms);
@@ -186,6 +192,10 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothDeviceClient
const std::map<std::string, std::vector<uint8_t>>& service_data,
const std::map<uint16_t, std::vector<uint8_t>>& manufacturer_data);
+ // Adds a pending prepare write request to |object_path|.
+ void AddPrepareWriteRequest(const dbus::ObjectPath& object_path,
+ const std::vector<uint8_t>& value);
+
static const char kTestPinCode[];
static const int kTestPassKey;
@@ -364,6 +374,10 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothDeviceClient
// Controls the fake behavior to allow more extensive UI testing without
// having to cycle the discovery simulation.
bool delay_start_discovery_;
+
+ // Pending prepare write requests.
+ std::vector<std::pair<dbus::ObjectPath, std::vector<uint8_t>>>
+ prepare_write_requests_;
};
} // namespace bluez
diff --git a/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc b/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc
index 66fc0f11abe..a0369e55c9f 100644
--- a/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc
+++ b/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc
@@ -11,6 +11,7 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "device/bluetooth/dbus/bluez_dbus_manager.h"
+#include "device/bluetooth/dbus/fake_bluetooth_device_client.h"
#include "device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
@@ -257,6 +258,7 @@ void FakeBluetoothGattCharacteristicClient::WriteValue(
} else if (value[0] == 1) {
// TODO(jamuraa): make this happen when the callback happens
calories_burned_ = 0;
+ ScheduleHeartRateMeasurementValueChange();
completed_callback = callback;
}
@@ -268,8 +270,51 @@ void FakeBluetoothGattCharacteristicClient::WriteValue(
completed_callback.Run();
}
+void FakeBluetoothGattCharacteristicClient::PrepareWriteValue(
+ const dbus::ObjectPath& object_path,
+ const std::vector<uint8_t>& value,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ if (!authenticated_) {
+ error_callback.Run(bluetooth_gatt_service::kErrorNotPaired, "Please login");
+ return;
+ }
+
+ if (!authorized_) {
+ error_callback.Run(bluetooth_gatt_service::kErrorNotAuthorized,
+ "Authorize first");
+ return;
+ }
+
+ if (!IsHeartRateVisible()) {
+ error_callback.Run(kUnknownCharacteristicError, "");
+ return;
+ }
+
+ if (object_path.value() == heart_rate_measurement_path_) {
+ error_callback.Run(bluetooth_gatt_service::kErrorNotSupported,
+ "Action not supported on this characteristic");
+ return;
+ }
+
+ if (object_path.value() != heart_rate_control_point_path_) {
+ error_callback.Run(bluetooth_gatt_service::kErrorNotPermitted,
+ "Writes of this value are not allowed");
+ return;
+ }
+
+ DCHECK(heart_rate_control_point_properties_.get());
+ static_cast<FakeBluetoothDeviceClient*>(
+ bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient())
+ ->AddPrepareWriteRequest(object_path, value);
+ callback.Run();
+}
+
void FakeBluetoothGattCharacteristicClient::StartNotify(
const dbus::ObjectPath& object_path,
+#if defined(OS_CHROMEOS)
+ device::BluetoothGattCharacteristic::NotificationType notification_type,
+#endif
const base::Closure& callback,
const ErrorCallback& error_callback) {
if (!IsHeartRateVisible()) {
@@ -345,6 +390,7 @@ void FakeBluetoothGattCharacteristicClient::ExposeHeartRateCharacteristics(
kHeartRateMeasurementUUID);
heart_rate_measurement_properties_->service.ReplaceValue(service_path);
flags.push_back(bluetooth_gatt_characteristic::kFlagNotify);
+ flags.push_back(bluetooth_gatt_characteristic::kFlagIndicate);
heart_rate_measurement_properties_->flags.ReplaceValue(flags);
// ==== Body Sensor Location Characteristic ====
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 564e701793c..dbc73e38e04 100644
--- a/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.h
+++ b/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.h
@@ -18,6 +18,7 @@
#include "dbus/object_path.h"
#include "dbus/property.h"
#include "device/bluetooth/bluetooth_export.h"
+#include "device/bluetooth/bluetooth_gatt_characteristic.h"
#include "device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h"
namespace bluez {
@@ -58,9 +59,22 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothGattCharacteristicClient
const std::vector<uint8_t>& value,
const base::Closure& callback,
const ErrorCallback& error_callback) override;
+ void PrepareWriteValue(const dbus::ObjectPath& object_path,
+ const std::vector<uint8_t>& value,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) override;
+#if defined(OS_CHROMEOS)
+ void StartNotify(
+ const dbus::ObjectPath& object_path,
+ device::BluetoothGattCharacteristic::NotificationType notification_type,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) override;
+#else
void StartNotify(const dbus::ObjectPath& object_path,
const base::Closure& callback,
const ErrorCallback& error_callback) override;
+#endif
+
void StopNotify(const dbus::ObjectPath& object_path,
const base::Closure& callback,
const ErrorCallback& error_callback) override;
diff --git a/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_service_provider.cc b/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_service_provider.cc
index cbc240dc5aa..5f96f550e9e 100644
--- a/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_service_provider.cc
+++ b/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_service_provider.cc
@@ -10,6 +10,7 @@
#include "base/callback.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
+#include "device/bluetooth/bluetooth_gatt_characteristic.h"
#include "device/bluetooth/dbus/bluetooth_gatt_attribute_value_delegate.h"
#include "device/bluetooth/dbus/bluez_dbus_manager.h"
#include "device/bluetooth/dbus/fake_bluetooth_gatt_manager_client.h"
@@ -169,6 +170,38 @@ void FakeBluetoothGattCharacteristicServiceProvider::SetValue(
delegate_->SetValue(device_path, value, callback, error_callback);
}
+void FakeBluetoothGattCharacteristicServiceProvider::PrepareSetValue(
+ const dbus::ObjectPath& device_path,
+ const std::vector<uint8_t>& value,
+ int offset,
+ bool has_subsequent_write,
+ const base::Closure& callback,
+ const device::BluetoothLocalGattService::Delegate::ErrorCallback&
+ error_callback) {
+ VLOG(1) << "GATT characteristic value Prepare Set request: "
+ << object_path_.value() << " UUID: " << uuid_;
+ // Check if this characteristic is registered.
+ FakeBluetoothGattManagerClient* fake_bluetooth_gatt_manager_client =
+ static_cast<FakeBluetoothGattManagerClient*>(
+ bluez::BluezDBusManager::Get()->GetBluetoothGattManagerClient());
+ if (!fake_bluetooth_gatt_manager_client->IsServiceRegistered(service_path_)) {
+ VLOG(1) << "GATT characteristic not registered.";
+ error_callback.Run();
+ return;
+ }
+
+ if (!CanWrite(flags_)) {
+ VLOG(1) << "GATT characteristic not writeable.";
+ error_callback.Run();
+ return;
+ }
+
+ // Pass on to the delegate.
+ DCHECK(delegate_);
+ delegate_->PrepareSetValue(device_path, value, offset, has_subsequent_write,
+ callback, error_callback);
+}
+
bool FakeBluetoothGattCharacteristicServiceProvider::NotificationsChange(
bool start) {
VLOG(1) << "GATT characteristic value notification request: "
@@ -189,7 +222,10 @@ bool FakeBluetoothGattCharacteristicServiceProvider::NotificationsChange(
// Pass on to the delegate.
DCHECK(delegate_);
- start ? delegate_->StartNotifications() : delegate_->StopNotifications();
+ start ? delegate_->StartNotifications(object_path_,
+ device::BluetoothGattCharacteristic::
+ NotificationType::kNotification)
+ : delegate_->StopNotifications(object_path_);
return true;
}
diff --git a/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_service_provider.h b/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_service_provider.h
index f6a1971eb5d..06fdb3db896 100644
--- a/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_service_provider.h
+++ b/chromium/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_service_provider.h
@@ -51,6 +51,14 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothGattCharacteristicServiceProvider
const base::Closure& callback,
const device::BluetoothLocalGattService::Delegate::ErrorCallback&
error_callback);
+ void PrepareSetValue(
+ const dbus::ObjectPath& device_path,
+ const std::vector<uint8_t>& value,
+ int offset,
+ bool has_subsequent_write,
+ const base::Closure& callback,
+ const device::BluetoothLocalGattService::Delegate::ErrorCallback&
+ error_callback);
// Method to simulate starting and stopping notifications.
bool NotificationsChange(bool start);
diff --git a/chromium/device/bluetooth/device_unittest.cc b/chromium/device/bluetooth/device_unittest.cc
index 29596f1a1e3..2c5f4ec72d4 100644
--- a/chromium/device/bluetooth/device_unittest.cc
+++ b/chromium/device/bluetooth/device_unittest.cc
@@ -107,16 +107,6 @@ class BluetoothInterfaceDeviceTest : public testing::Test {
service2->AddMockCharacteristic(std::move(characteristic3));
- EXPECT_CALL(*service1, GetCharacteristics())
- .WillRepeatedly(
- Invoke(service1.get(),
- &device::MockBluetoothGattService::GetMockCharacteristics));
-
- EXPECT_CALL(*service2, GetCharacteristics())
- .WillRepeatedly(
- Invoke(service2.get(),
- &device::MockBluetoothGattService::GetMockCharacteristics));
-
device_.AddMockService(std::move(service1));
device_.AddMockService(std::move(service2));
diff --git a/chromium/device/bluetooth/event_utils_winrt.h b/chromium/device/bluetooth/event_utils_winrt.h
new file mode 100644
index 00000000000..ec032232a93
--- /dev/null
+++ b/chromium/device/bluetooth/event_utils_winrt.h
@@ -0,0 +1,161 @@
+// 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_EVENT_UTILS_WINRT_H_
+#define DEVICE_BLUETOOTH_EVENT_UTILS_WINRT_H_
+
+#include <windows.foundation.h>
+#include <wrl/client.h>
+#include <wrl/event.h>
+
+#include <type_traits>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "base/threading/thread_task_runner_handle.h"
+
+namespace device {
+
+namespace internal {
+
+// Utility function to pretty print enum values.
+constexpr const char* ToCString(AsyncStatus async_status) {
+ switch (async_status) {
+ case AsyncStatus::Started:
+ return "AsyncStatus::Started";
+ case AsyncStatus::Completed:
+ return "AsyncStatus::Completed";
+ case AsyncStatus::Canceled:
+ return "AsyncStatus::Canceled";
+ case AsyncStatus::Error:
+ return "AsyncStatus::Error";
+ }
+
+ NOTREACHED();
+ return "";
+}
+
+template <typename Interface, typename... Args>
+using IMemberFunction = HRESULT (__stdcall Interface::*)(Args...);
+
+template <typename T>
+using AsyncAbiT = typename ABI::Windows::Foundation::Internal::GetAbiType<
+ typename ABI::Windows::Foundation::IAsyncOperation<T>::TResult_complex>::
+ type;
+
+// Compile time switch to decide what container to use for the async results for
+// |T|. Depends on whether the underlying Abi type is a pointer to IUnknown or
+// not. It queries the internals of Windows::Foundation to obtain this
+// information.
+template <typename T>
+using AsyncResultsT = std::conditional_t<
+ std::is_convertible<AsyncAbiT<T>, IUnknown*>::value,
+ Microsoft::WRL::ComPtr<std::remove_pointer_t<AsyncAbiT<T>>>,
+ AsyncAbiT<T>>;
+
+// Obtains the results of the provided async operation.
+template <typename T>
+AsyncResultsT<T> GetAsyncResults(
+ ABI::Windows::Foundation::IAsyncOperation<T>* async_op) {
+ AsyncResultsT<T> results;
+ HRESULT hr = async_op->GetResults(&results);
+ if (FAILED(hr)) {
+ VLOG(2) << "GetAsyncResults failed: "
+ << logging::SystemErrorCodeToString(hr);
+ }
+
+ return results;
+}
+
+} // namespace internal
+
+// This method registers a completion handler for |async_op| and will post the
+// results to |callback|. The |callback| will be run on the same thread that
+// invoked this method. Callers need to ensure that this method is invoked in
+// the correct COM apartment, i.e. the one that created |async_op|. While a WRL
+// Callback can be constructed from callable types such as a lambda or
+// std::function objects, it cannot be directly constructed from a
+// base::OnceCallback. Thus the callback is moved into a capturing lambda, which
+// then posts the callback once it is run. Posting the results to the TaskRunner
+// is required, since the completion callback might be invoked on an arbitrary
+// thread. Lastly, the lambda takes ownership of |async_op|, as this needs to be
+// kept alive until GetAsyncResults can be invoked.
+template <typename T>
+HRESULT PostAsyncResults(
+ Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<T>>
+ async_op,
+ base::OnceCallback<void(internal::AsyncResultsT<T>)> callback) {
+ auto completion_cb = base::BindOnce(
+ [](Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<T>>
+ async_op,
+ base::OnceCallback<void(internal::AsyncResultsT<T>)> callback) {
+ std::move(callback).Run(internal::GetAsyncResults(async_op.Get()));
+ },
+ async_op, std::move(callback));
+
+ return async_op->put_Completed(
+ Microsoft::WRL::Callback<
+ ABI::Windows::Foundation::IAsyncOperationCompletedHandler<T>>([
+ task_runner(base::ThreadTaskRunnerHandle::Get()),
+ completion_cb(std::move(completion_cb))
+ ](auto&&, AsyncStatus async_status) mutable {
+ if (async_status != AsyncStatus::Completed) {
+ VLOG(2) << "Got unexpected AsyncStatus: "
+ << internal::ToCString(async_status);
+ }
+
+ // Note: We are ignoring the passed in pointer to async_op, as the
+ // completion callback has access to the initially provided |async_op|.
+ // Since the code within the lambda could be executed on any thread, it
+ // is vital that the completion callback gets posted to the original
+ // |task_runner|, as this is guaranteed to be in the correct COM
+ // apartment.
+ task_runner->PostTask(FROM_HERE, std::move(completion_cb));
+ return S_OK;
+ })
+ .Get());
+}
+
+// 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.
+template <typename Interface,
+ typename Sender,
+ typename Args,
+ typename SenderAbi,
+ typename ArgsAbi>
+base::Optional<EventRegistrationToken> AddTypedEventHandler(
+ Interface* i,
+ internal::IMemberFunction<
+ Interface,
+ ABI::Windows::Foundation::ITypedEventHandler<Sender, Args>*,
+ EventRegistrationToken*> function,
+ 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;
+ })
+ .Get(),
+ &token);
+
+ if (FAILED(hr)) {
+ VLOG(2) << "Adding EventHandler failed: "
+ << logging::SystemErrorCodeToString(hr);
+ return base::nullopt;
+ }
+
+ return token;
+}
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_EVENT_UTILS_WINRT_H_
diff --git a/chromium/device/fido/BUILD.gn b/chromium/device/fido/BUILD.gn
index 9ac467aa604..369ac7d8522 100644
--- a/chromium/device/fido/BUILD.gn
+++ b/chromium/device/fido/BUILD.gn
@@ -11,6 +11,8 @@ component("fido") {
"attestation_object.h",
"attestation_statement.cc",
"attestation_statement.h",
+ "attestation_statement_formats.cc",
+ "attestation_statement_formats.h",
"attested_credential_data.cc",
"attested_credential_data.h",
"authenticator_data.cc",
@@ -25,21 +27,18 @@ component("fido") {
"authenticator_selection_criteria.h",
"authenticator_supported_options.cc",
"authenticator_supported_options.h",
+ "ctap2_device_operation.h",
"ctap_empty_authenticator_request.cc",
"ctap_empty_authenticator_request.h",
"ctap_get_assertion_request.cc",
"ctap_get_assertion_request.h",
"ctap_make_credential_request.cc",
"ctap_make_credential_request.h",
- "ctap_register_operation.cc",
- "ctap_register_operation.h",
"device_operation.h",
"device_response_converter.cc",
"device_response_converter.h",
"ec_public_key.cc",
"ec_public_key.h",
- "fido_attestation_statement.cc",
- "fido_attestation_statement.h",
"fido_authenticator.h",
"fido_ble_connection.cc",
"fido_ble_connection.h",
@@ -59,6 +58,8 @@ component("fido") {
"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",
@@ -105,12 +106,12 @@ component("fido") {
"response_data.h",
"u2f_command_constructor.cc",
"u2f_command_constructor.h",
- "u2f_register.cc",
- "u2f_register.h",
- "u2f_request.cc",
- "u2f_request.h",
- "u2f_sign.cc",
- "u2f_sign.h",
+ "u2f_register_operation.cc",
+ "u2f_register_operation.h",
+ "u2f_sign_operation.cc",
+ "u2f_sign_operation.h",
+ "virtual_ctap2_device.cc",
+ "virtual_ctap2_device.h",
"virtual_fido_device.cc",
"virtual_fido_device.h",
"virtual_u2f_device.cc",
@@ -156,6 +157,10 @@ component("fido") {
sources += [
"mac/authenticator.h",
"mac/authenticator.mm",
+ "mac/browsing_data_deletion.h",
+ "mac/browsing_data_deletion.mm",
+ "mac/credential_metadata.cc",
+ "mac/credential_metadata.h",
"mac/get_assertion_operation.h",
"mac/get_assertion_operation.mm",
"mac/keychain.h",
@@ -232,6 +237,17 @@ fuzzer_test("ctap_response_fuzzer") {
libfuzzer_options = [ "max_len=65537" ]
}
+fuzzer_test("fido_cable_handshake_handler_fuzzer") {
+ sources = [
+ "fido_cable_handshake_handler_fuzzer.cc",
+ ]
+ deps = [
+ ":fido",
+ "//base",
+ ]
+ libfuzzer_options = [ "max_len=2048" ]
+}
+
is_linux_without_udev = is_linux && !use_udev
source_set("test_support") {
diff --git a/chromium/device/fido/OWNERS b/chromium/device/fido/OWNERS
index cac54078f71..48920ce5d4d 100644
--- a/chromium/device/fido/OWNERS
+++ b/chromium/device/fido/OWNERS
@@ -1,6 +1,8 @@
reillyg@chromium.org
jdoerrie@chromium.org
engedy@chromium.org
+kpaulhamus@chromium.org
+hongjunchoi@chromium.org
# TEAM: security-dev@chromium.org
# COMPONENT: Blink>WebAuthentication
diff --git a/chromium/device/fido/attestation_object.cc b/chromium/device/fido/attestation_object.cc
index ed9b93b4154..1090e09e9e4 100644
--- a/chromium/device/fido/attestation_object.cc
+++ b/chromium/device/fido/attestation_object.cc
@@ -69,4 +69,15 @@ std::vector<uint8_t> AttestationObject::SerializeToCBOREncodedBytes() const {
.value_or(std::vector<uint8_t>());
}
+std::vector<uint8_t> SerializeToCtapStyleCborEncodedBytes(
+ const AttestationObject& object) {
+ cbor::CBORValue::MapValue map;
+ map.emplace(1, object.attestation_statement().format_name());
+ map.emplace(2, object.authenticator_data().SerializeToByteArray());
+ map.emplace(3, object.attestation_statement().GetAsCBORMap());
+ auto encoded_bytes = cbor::CBORWriter::Write(cbor::CBORValue(std::move(map)));
+ DCHECK(encoded_bytes);
+ return std::move(*encoded_bytes);
+}
+
} // namespace device
diff --git a/chromium/device/fido/attestation_object.h b/chromium/device/fido/attestation_object.h
index 8b7717c2756..bde70e6d495 100644
--- a/chromium/device/fido/attestation_object.h
+++ b/chromium/device/fido/attestation_object.h
@@ -6,12 +6,15 @@
#define DEVICE_FIDO_ATTESTATION_OBJECT_H_
#include <stdint.h>
+
+#include <array>
#include <memory>
#include <vector>
#include "base/component_export.h"
#include "base/macros.h"
#include "device/fido/authenticator_data.h"
+#include "device/fido/fido_constants.h"
namespace device {
@@ -44,16 +47,24 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AttestationObject {
// not indended to be trackable.)
bool IsAttestationCertificateInappropriatelyIdentifying();
- // Produces a CBOR-encoded byte-array in the following format:
+ // Produces a WebAuthN style CBOR-encoded byte-array in the following format:
// {"authData": authenticator data bytes,
// "fmt": attestation format name,
// "attStmt": attestation statement bytes }
std::vector<uint8_t> SerializeToCBOREncodedBytes() const;
- const std::vector<uint8_t>& rp_id_hash() const {
+ const std::array<uint8_t, kRpIdHashLength>& rp_id_hash() const {
return authenticator_data_.application_parameter();
}
+ const AuthenticatorData& authenticator_data() const {
+ return authenticator_data_;
+ }
+
+ const AttestationStatement& attestation_statement() const {
+ return *attestation_statement_.get();
+ }
+
private:
AuthenticatorData authenticator_data_;
std::unique_ptr<AttestationStatement> attestation_statement_;
@@ -61,6 +72,15 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AttestationObject {
DISALLOW_COPY_AND_ASSIGN(AttestationObject);
};
+// Produces a CTAP style CBOR-encoded byte array that that conforms to the
+// format CTAP2 devices sends to the client as a response. More specifically:
+// {01: attestation format name,
+// 02: authenticator data bytes,
+// 03: attestation statement bytes }
+COMPONENT_EXPORT(DEVICE_FIDO)
+std::vector<uint8_t> SerializeToCtapStyleCborEncodedBytes(
+ const AttestationObject& object);
+
} // namespace device
#endif // DEVICE_FIDO_ATTESTATION_OBJECT_H_
diff --git a/chromium/device/fido/attestation_statement.h b/chromium/device/fido/attestation_statement.h
index c4128967563..f7e0621a0c8 100644
--- a/chromium/device/fido/attestation_statement.h
+++ b/chromium/device/fido/attestation_statement.h
@@ -37,7 +37,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AttestationStatement {
// indended to be trackable.)
virtual bool IsAttestationCertificateInappropriatelyIdentifying() = 0;
- const std::string& format_name() { return format_; }
+ const std::string& format_name() const { return format_; }
protected:
explicit AttestationStatement(std::string format);
diff --git a/chromium/device/fido/fido_attestation_statement.cc b/chromium/device/fido/attestation_statement_formats.cc
index b4f80574124..e3f19693aa7 100644
--- a/chromium/device/fido/fido_attestation_statement.cc
+++ b/chromium/device/fido/attestation_statement_formats.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_attestation_statement.h"
+#include "device/fido/attestation_statement_formats.h"
#include <utility>
@@ -14,6 +14,8 @@ namespace device {
namespace {
constexpr char kFidoFormatName[] = "fido-u2f";
+constexpr char kPackedAttestationFormat[] = "packed";
+constexpr char kAlgorithmKey[] = "alg";
constexpr char kSignatureKey[] = "sig";
constexpr char kX509CertKey[] = "x5c";
@@ -88,8 +90,7 @@ FidoAttestationStatement::CreateFromU2fRegisterResponse(
// The format of |u2f_data| is specified here:
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html#registration-response-message-success
uint8_t credential_length;
- if (!CBS_skip(&response,
- fido_parsing_utils::kU2fResponseKeyHandleLengthPos) ||
+ if (!CBS_skip(&response, kU2fKeyHandleLengthOffset) ||
!CBS_get_u8(&response, &credential_length) ||
!CBS_skip(&response, credential_length) ||
!CBS_get_asn1_element(&response, &cert, CBS_ASN1_SEQUENCE)) {
@@ -148,4 +149,48 @@ bool FidoAttestationStatement::
return false;
}
+PackedAttestationStatement::PackedAttestationStatement(
+ CoseAlgorithmIdentifier algorithm,
+ std::vector<uint8_t> signature,
+ std::vector<std::vector<uint8_t>> x509_certificates)
+ : AttestationStatement(kPackedAttestationFormat),
+ algorithm_(algorithm),
+ signature_(signature),
+ x509_certificates_(std::move(x509_certificates)) {
+ DCHECK(!signature_.empty());
+}
+
+PackedAttestationStatement::~PackedAttestationStatement() = default;
+
+cbor::CBORValue::MapValue PackedAttestationStatement::GetAsCBORMap() const {
+ cbor::CBORValue::MapValue attestation_statement_map;
+ // alg
+ attestation_statement_map[cbor::CBORValue(kAlgorithmKey)] =
+ cbor::CBORValue(static_cast<int>(algorithm_));
+ // sig
+ attestation_statement_map[cbor::CBORValue(kSignatureKey)] =
+ cbor::CBORValue(signature_);
+ // x5c (optional)
+ if (!x509_certificates_.empty()) {
+ std::vector<cbor::CBORValue> certificate_array;
+ for (const auto& cert : x509_certificates_) {
+ certificate_array.push_back(cbor::CBORValue(cert));
+ }
+ attestation_statement_map[cbor::CBORValue(kX509CertKey)] =
+ cbor::CBORValue(std::move(certificate_array));
+ }
+ return attestation_statement_map;
+}
+
+bool PackedAttestationStatement::
+ IsAttestationCertificateInappropriatelyIdentifying() {
+ for (const auto& der_bytes : x509_certificates_) {
+ if (IsCertificateInappropriatelyIdentifying(der_bytes)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
} // namespace device
diff --git a/chromium/device/fido/fido_attestation_statement.h b/chromium/device/fido/attestation_statement_formats.h
index 4ecd75a5f6a..fb16d88cd40 100644
--- a/chromium/device/fido/fido_attestation_statement.h
+++ b/chromium/device/fido/attestation_statement_formats.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_ATTESTATION_STATEMENT_H_
-#define DEVICE_FIDO_FIDO_ATTESTATION_STATEMENT_H_
+#ifndef DEVICE_FIDO_ATTESTATION_STATEMENT_FORMATS_H_
+#define DEVICE_FIDO_ATTESTATION_STATEMENT_FORMATS_H_
#include <stdint.h>
#include <memory>
@@ -14,6 +14,7 @@
#include "base/macros.h"
#include "components/cbor/cbor_values.h"
#include "device/fido/attestation_statement.h"
+#include "device/fido/fido_constants.h"
namespace device {
@@ -28,12 +29,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoAttestationStatement
std::vector<std::vector<uint8_t>> x509_certificates);
~FidoAttestationStatement() override;
- // AttestationStatement overrides
-
- // Produces a map in the following format:
- // { "x5c": [ x509_certs bytes ], "sig": signature bytes ] }
+ // AttestationStatement
cbor::CBORValue::MapValue GetAsCBORMap() const override;
-
bool IsAttestationCertificateInappropriatelyIdentifying() override;
private:
@@ -43,6 +40,30 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoAttestationStatement
DISALLOW_COPY_AND_ASSIGN(FidoAttestationStatement);
};
+// Implements the "packed" attestation statement format from
+// https://www.w3.org/TR/webauthn/#packed-attestation.
+//
+// It currently only supports the (optional) "x5c" field, but not "ecdaaKeyId"
+// (see packedStmtFormat choices).
+class COMPONENT_EXPORT(DEVICE_FIDO) PackedAttestationStatement
+ : public AttestationStatement {
+ public:
+ PackedAttestationStatement(
+ CoseAlgorithmIdentifier algorithm,
+ std::vector<uint8_t> signature,
+ std::vector<std::vector<uint8_t>> x509_certificates);
+ ~PackedAttestationStatement() override;
+
+ // AttestationStatement
+ cbor::CBORValue::MapValue GetAsCBORMap() const override;
+ bool IsAttestationCertificateInappropriatelyIdentifying() override;
+
+ private:
+ const CoseAlgorithmIdentifier algorithm_;
+ const std::vector<uint8_t> signature_;
+ const std::vector<std::vector<uint8_t>> x509_certificates_;
+};
+
} // namespace device
-#endif // DEVICE_FIDO_FIDO_ATTESTATION_STATEMENT_H_
+#endif // DEVICE_FIDO_ATTESTATION_STATEMENT_FORMATS_H_
diff --git a/chromium/device/fido/attestation_statement_formats_unittest.cc b/chromium/device/fido/attestation_statement_formats_unittest.cc
new file mode 100644
index 00000000000..d82969b38d8
--- /dev/null
+++ b/chromium/device/fido/attestation_statement_formats_unittest.cc
@@ -0,0 +1,104 @@
+// 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 <vector>
+
+#include "components/cbor/cbor_writer.h"
+#include "device/fido/attestation_statement_formats.h"
+#include "device/fido/fido_constants.h"
+#include "device/fido/fido_parsing_utils.h"
+#include "device/fido/fido_test_data.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device {
+namespace {
+
+// The signature part from test_data::kPackedAttestationStatementCBOR.
+constexpr uint8_t kSignature[] = {
+ 0x30, 0x45, 0x02, 0x20, 0x32, 0x47, 0x79, 0xC6, 0x8F, 0x33, 0x80, 0x28,
+ 0x8A, 0x11, 0x97, 0xB6, 0x09, 0x5F, 0x7A, 0x6E, 0xB9, 0xB1, 0xB1, 0xC1,
+ 0x27, 0xF6, 0x6A, 0xE1, 0x2A, 0x99, 0xFE, 0x85, 0x32, 0xEC, 0x23, 0xB9,
+ 0x02, 0x21, 0x00, 0xE3, 0x95, 0x16, 0xAC, 0x4D, 0x61, 0xEE, 0x64, 0x04,
+ 0x4D, 0x50, 0xB4, 0x15, 0xA6, 0xA4, 0xD4, 0xD8, 0x4B, 0xA6, 0xD8, 0x95,
+ 0xCB, 0x5A, 0xB7, 0xA1, 0xAA, 0x7D, 0x08, 0x1D, 0xE3, 0x41, 0xFA,
+};
+
+// The certificates part from test_data::kPackedAttestationStatementCBOR.
+constexpr uint8_t kCertificates[] = {
+ 0x30, 0x82, 0x02, 0x4A, 0x30, 0x82, 0x01, 0x32, 0xA0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x04, 0x6C, 0x88, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A,
+ 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x30, 0x2E,
+ 0x31, 0x2C, 0x30, 0x2A, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x23, 0x59,
+ 0x75, 0x62, 0x69, 0x63, 0x6F, 0x20, 0x55, 0x32, 0x46, 0x20, 0x52, 0x6F,
+ 0x6F, 0x74, 0x20, 0x43, 0x41, 0x20, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6C,
+ 0x20, 0x34, 0x35, 0x37, 0x32, 0x30, 0x30, 0x36, 0x33, 0x31, 0x30, 0x20,
+ 0x17, 0x0D, 0x31, 0x34, 0x30, 0x38, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5A, 0x18, 0x0F, 0x32, 0x30, 0x35, 0x30, 0x30, 0x39, 0x30,
+ 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x30, 0x2C, 0x31, 0x2A,
+ 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x21, 0x59, 0x75, 0x62,
+ 0x69, 0x63, 0x6F, 0x20, 0x55, 0x32, 0x46, 0x20, 0x45, 0x45, 0x20, 0x53,
+ 0x65, 0x72, 0x69, 0x61, 0x6C, 0x20, 0x32, 0x34, 0x39, 0x31, 0x38, 0x32,
+ 0x33, 0x32, 0x34, 0x37, 0x37, 0x30, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
+ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48,
+ 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x3C, 0xCA, 0xB9,
+ 0x2C, 0xCB, 0x97, 0x28, 0x7E, 0xE8, 0xE6, 0x39, 0x43, 0x7E, 0x21, 0xFC,
+ 0xD6, 0xB6, 0xF1, 0x65, 0xB2, 0xD5, 0xA3, 0xF3, 0xDB, 0x13, 0x1D, 0x31,
+ 0xC1, 0x6B, 0x74, 0x2B, 0xB4, 0x76, 0xD8, 0xD1, 0xE9, 0x90, 0x80, 0xEB,
+ 0x54, 0x6C, 0x9B, 0xBD, 0xF5, 0x56, 0xE6, 0x21, 0x0F, 0xD4, 0x27, 0x85,
+ 0x89, 0x9E, 0x78, 0xCC, 0x58, 0x9E, 0xBE, 0x31, 0x0F, 0x6C, 0xDB, 0x9F,
+ 0xF4, 0xA3, 0x3B, 0x30, 0x39, 0x30, 0x22, 0x06, 0x09, 0x2B, 0x06, 0x01,
+ 0x04, 0x01, 0x82, 0xC4, 0x0A, 0x02, 0x04, 0x15, 0x31, 0x2E, 0x33, 0x2E,
+ 0x36, 0x2E, 0x31, 0x2E, 0x34, 0x2E, 0x31, 0x2E, 0x34, 0x31, 0x34, 0x38,
+ 0x32, 0x2E, 0x31, 0x2E, 0x32, 0x30, 0x13, 0x06, 0x0B, 0x2B, 0x06, 0x01,
+ 0x04, 0x01, 0x82, 0xE5, 0x1C, 0x02, 0x01, 0x01, 0x04, 0x04, 0x03, 0x02,
+ 0x04, 0x30, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D,
+ 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x9F, 0x9B,
+ 0x05, 0x22, 0x48, 0xBC, 0x4C, 0xF4, 0x2C, 0xC5, 0x99, 0x1F, 0xCA, 0xAB,
+ 0xAC, 0x9B, 0x65, 0x1B, 0xBE, 0x5B, 0xDC, 0xDC, 0x8E, 0xF0, 0xAD, 0x2C,
+ 0x1C, 0x1F, 0xFB, 0x36, 0xD1, 0x87, 0x15, 0xD4, 0x2E, 0x78, 0xB2, 0x49,
+ 0x22, 0x4F, 0x92, 0xC7, 0xE6, 0xE7, 0xA0, 0x5C, 0x49, 0xF0, 0xE7, 0xE4,
+ 0xC8, 0x81, 0xBF, 0x2E, 0x94, 0xF4, 0x5E, 0x4A, 0x21, 0x83, 0x3D, 0x74,
+ 0x56, 0x85, 0x1D, 0x0F, 0x6C, 0x14, 0x5A, 0x29, 0x54, 0x0C, 0x87, 0x4F,
+ 0x30, 0x92, 0xC9, 0x34, 0xB4, 0x3D, 0x22, 0x2B, 0x89, 0x62, 0xC0, 0xF4,
+ 0x10, 0xCE, 0xF1, 0xDB, 0x75, 0x89, 0x2A, 0xF1, 0x16, 0xB4, 0x4A, 0x96,
+ 0xF5, 0xD3, 0x5A, 0xDE, 0xA3, 0x82, 0x2F, 0xC7, 0x14, 0x6F, 0x60, 0x04,
+ 0x38, 0x5B, 0xCB, 0x69, 0xB6, 0x5C, 0x99, 0xE7, 0xEB, 0x69, 0x19, 0x78,
+ 0x67, 0x03, 0xC0, 0xD8, 0xCD, 0x41, 0xE8, 0xF7, 0x5C, 0xCA, 0x44, 0xAA,
+ 0x8A, 0xB7, 0x25, 0xAD, 0x8E, 0x79, 0x9F, 0xF3, 0xA8, 0x69, 0x6A, 0x6F,
+ 0x1B, 0x26, 0x56, 0xE6, 0x31, 0xB1, 0xE4, 0x01, 0x83, 0xC0, 0x8F, 0xDA,
+ 0x53, 0xFA, 0x4A, 0x8F, 0x85, 0xA0, 0x56, 0x93, 0x94, 0x4A, 0xE1, 0x79,
+ 0xA1, 0x33, 0x9D, 0x00, 0x2D, 0x15, 0xCA, 0xBD, 0x81, 0x00, 0x90, 0xEC,
+ 0x72, 0x2E, 0xF5, 0xDE, 0xF9, 0x96, 0x5A, 0x37, 0x1D, 0x41, 0x5D, 0x62,
+ 0x4B, 0x68, 0xA2, 0x70, 0x7C, 0xAD, 0x97, 0xBC, 0xDD, 0x17, 0x85, 0xAF,
+ 0x97, 0xE2, 0x58, 0xF3, 0x3D, 0xF5, 0x6A, 0x03, 0x1A, 0xA0, 0x35, 0x6D,
+ 0x8E, 0x8D, 0x5E, 0xBC, 0xAD, 0xC7, 0x4E, 0x07, 0x16, 0x36, 0xC6, 0xB1,
+ 0x10, 0xAC, 0xE5, 0xCC, 0x9B, 0x90, 0xDF, 0xEA, 0xCA, 0xE6, 0x40, 0xFF,
+ 0x1B, 0xB0, 0xF1, 0xFE, 0x5D, 0xB4, 0xEF, 0xF7, 0xA9, 0x5F, 0x06, 0x07,
+ 0x33, 0xF5,
+};
+
+TEST(PackedAttestationStatementTest, CBOR) {
+ EXPECT_THAT(
+ *cbor::CBORWriter::Write(
+ cbor::CBORValue(PackedAttestationStatement(
+ CoseAlgorithmIdentifier::kCoseEs256,
+ fido_parsing_utils::Materialize(kSignature),
+ {fido_parsing_utils::Materialize(kCertificates)})
+ .GetAsCBORMap())),
+ testing::ElementsAreArray(test_data::kPackedAttestationStatementCBOR));
+}
+
+TEST(PackedAttestationStatementTest, CBOR_NoCerts) {
+ EXPECT_THAT(*cbor::CBORWriter::Write(cbor::CBORValue(
+ PackedAttestationStatement(
+ CoseAlgorithmIdentifier::kCoseEs256,
+ fido_parsing_utils::Materialize(kSignature), {})
+ .GetAsCBORMap())),
+ testing::ElementsAreArray(
+ test_data::kPackedAttestationStatementCBORNoCerts));
+}
+
+} // namespace
+} // namespace device
diff --git a/chromium/device/fido/attested_credential_data.cc b/chromium/device/fido/attested_credential_data.cc
index 90bc165a558..70614dfca93 100644
--- a/chromium/device/fido/attested_credential_data.cc
+++ b/chromium/device/fido/attested_credential_data.cc
@@ -8,6 +8,7 @@
#include <utility>
#include "base/numerics/safe_math.h"
+#include "device/fido/fido_constants.h"
#include "device/fido/fido_parsing_utils.h"
#include "device/fido/opaque_public_key.h"
#include "device/fido/public_key.h"
@@ -18,38 +19,32 @@ namespace device {
base::Optional<AttestedCredentialData>
AttestedCredentialData::DecodeFromCtapResponse(
base::span<const uint8_t> buffer) {
- if (buffer.size() < kAaguidLength + kCredentialIdLengthLength)
+ if (buffer.size() < kAaguidLength)
return base::nullopt;
- std::array<uint8_t, kAaguidLength> aaguid;
- if (!fido_parsing_utils::ExtractArray(buffer, 0, &aaguid))
- return base::nullopt;
+ auto aaguid = buffer.first<kAaguidLength>();
+ buffer = buffer.subspan(kAaguidLength);
- std::array<uint8_t, kCredentialIdLengthLength> credential_id_length_array;
- if (!fido_parsing_utils::ExtractArray(buffer, kAaguidLength,
- &credential_id_length_array)) {
+ if (buffer.size() < kCredentialIdLengthLength)
return base::nullopt;
- }
- static_assert(kCredentialIdLengthLength == 2u, "L must be 2 bytes");
+ auto credential_id_length_span = buffer.first<kCredentialIdLengthLength>();
const size_t credential_id_length =
- (base::strict_cast<size_t>(credential_id_length_array[0]) << 8) |
- base::strict_cast<size_t>(credential_id_length_array[1]);
+ (base::strict_cast<size_t>(credential_id_length_span[0]) << 8) |
+ base::strict_cast<size_t>(credential_id_length_span[1]);
+ buffer = buffer.subspan(kCredentialIdLengthLength);
- auto credential_id = fido_parsing_utils::Extract(
- buffer, kAaguidLength + kCredentialIdLengthLength, credential_id_length);
- if (credential_id.empty())
+ if (buffer.size() < credential_id_length)
return base::nullopt;
- DCHECK_LE(kAaguidLength + kCredentialIdLengthLength + credential_id_length,
- buffer.size());
- auto credential_public_key_data =
- std::make_unique<OpaquePublicKey>(buffer.subspan(
- kAaguidLength + kCredentialIdLengthLength + credential_id_length));
+ auto credential_id = buffer.first(credential_id_length);
+ buffer = buffer.subspan(credential_id_length);
+
+ auto credential_public_key_data = std::make_unique<OpaquePublicKey>(buffer);
- return AttestedCredentialData(
- std::move(aaguid), std::move(credential_id_length_array),
- std::move(credential_id), std::move(credential_public_key_data));
+ return AttestedCredentialData(aaguid, credential_id_length_span,
+ fido_parsing_utils::Materialize(credential_id),
+ std::move(credential_public_key_data));
}
// static
@@ -60,16 +55,15 @@ AttestedCredentialData::CreateFromU2fRegisterResponse(
// TODO(crbug/799075): Introduce a CredentialID class to do this extraction.
// Extract the length of the credential (i.e. of the U2FResponse key
// handle). Length is big endian.
- std::vector<uint8_t> extracted_length = fido_parsing_utils::Extract(
- u2f_data, fido_parsing_utils::kU2fResponseKeyHandleLengthPos, 1);
+ std::vector<uint8_t> extracted_length =
+ fido_parsing_utils::Extract(u2f_data, kU2fKeyHandleLengthOffset, 1);
if (extracted_length.empty()) {
return base::nullopt;
}
// For U2F register request, device AAGUID is set to zeros.
- std::array<uint8_t, kAaguidLength> aaguid;
- aaguid.fill(0);
+ std::array<uint8_t, kAaguidLength> aaguid = {};
// Note that U2F responses only use one byte for length.
std::array<uint8_t, kCredentialIdLengthLength> credential_id_length = {
@@ -77,16 +71,16 @@ AttestedCredentialData::CreateFromU2fRegisterResponse(
// Extract the credential id (i.e. key handle).
std::vector<uint8_t> credential_id = fido_parsing_utils::Extract(
- u2f_data, fido_parsing_utils::kU2fResponseKeyHandleStartPos,
+ u2f_data, kU2fKeyHandleOffset,
base::strict_cast<size_t>(credential_id_length[1]));
if (credential_id.empty()) {
return base::nullopt;
}
- return AttestedCredentialData(
- std::move(aaguid), std::move(credential_id_length),
- std::move(credential_id), std::move(public_key));
+ return AttestedCredentialData(aaguid, credential_id_length,
+ std::move(credential_id),
+ std::move(public_key));
}
AttestedCredentialData::AttestedCredentialData(AttestedCredentialData&& other) =
@@ -103,23 +97,21 @@ void AttestedCredentialData::DeleteAaguid() {
std::vector<uint8_t> AttestedCredentialData::SerializeAsBytes() const {
std::vector<uint8_t> attestation_data;
- fido_parsing_utils::Append(&attestation_data,
- base::make_span(aaguid_.data(), kAaguidLength));
- fido_parsing_utils::Append(
- &attestation_data,
- base::make_span(credential_id_length_.data(), kCredentialIdLengthLength));
+ fido_parsing_utils::Append(&attestation_data, aaguid_);
+ fido_parsing_utils::Append(&attestation_data, credential_id_length_);
fido_parsing_utils::Append(&attestation_data, credential_id_);
fido_parsing_utils::Append(&attestation_data, public_key_->EncodeAsCOSEKey());
return attestation_data;
}
AttestedCredentialData::AttestedCredentialData(
- std::array<uint8_t, kAaguidLength> aaguid,
- std::array<uint8_t, kCredentialIdLengthLength> credential_id_length,
+ base::span<const uint8_t, kAaguidLength> aaguid,
+ base::span<const uint8_t, kCredentialIdLengthLength> credential_id_length,
std::vector<uint8_t> credential_id,
std::unique_ptr<PublicKey> public_key)
- : aaguid_(std::move(aaguid)),
- credential_id_length_(std::move(credential_id_length)),
+ : aaguid_(fido_parsing_utils::Materialize(aaguid)),
+ credential_id_length_(
+ fido_parsing_utils::Materialize(credential_id_length)),
credential_id_(std::move(credential_id)),
public_key_(std::move(public_key)) {}
diff --git a/chromium/device/fido/attested_credential_data.h b/chromium/device/fido/attested_credential_data.h
index 7e4f02e132d..26048860c0d 100644
--- a/chromium/device/fido/attested_credential_data.h
+++ b/chromium/device/fido/attested_credential_data.h
@@ -14,6 +14,7 @@
#include "base/containers/span.h"
#include "base/macros.h"
#include "base/optional.h"
+#include "device/fido/fido_constants.h"
namespace device {
@@ -48,13 +49,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AttestedCredentialData {
// * Credential Public Key.
std::vector<uint8_t> SerializeAsBytes() const;
- static constexpr size_t kAaguidLength = 16;
- // Number of bytes used to represent length of credential ID.
- static constexpr size_t kCredentialIdLengthLength = 2;
-
AttestedCredentialData(
- std::array<uint8_t, kAaguidLength> aaguid,
- std::array<uint8_t, kCredentialIdLengthLength> credential_id_length,
+ base::span<const uint8_t, kAaguidLength> aaguid,
+ base::span<const uint8_t, kCredentialIdLengthLength> credential_id_length,
std::vector<uint8_t> credential_id,
std::unique_ptr<PublicKey> public_key);
diff --git a/chromium/device/fido/authenticator_data.cc b/chromium/device/fido/authenticator_data.cc
index e979b0a31be..32a105f5af2 100644
--- a/chromium/device/fido/authenticator_data.cc
+++ b/chromium/device/fido/authenticator_data.cc
@@ -13,46 +13,38 @@ namespace device {
namespace {
-constexpr size_t kApplicationParameterLength = 32;
-constexpr size_t kAuthDataCounterLength = 4;
-constexpr size_t kAaguidOffset =
- 32 /* RP ID hash */ + 1 /* flags */ + 4 /* signature counter */;
+constexpr size_t kAttestedCredentialDataOffset =
+ kRpIdHashLength + kFlagsLength + kSignCounterLength;
} // namespace
// static
base::Optional<AuthenticatorData> AuthenticatorData::DecodeAuthenticatorData(
base::span<const uint8_t> auth_data) {
- if (auth_data.size() < kAaguidOffset)
+ if (auth_data.size() < kAttestedCredentialDataOffset)
return base::nullopt;
- std::vector<uint8_t> application_parameter(
- auth_data.data(), auth_data.data() + kApplicationParameterLength);
- uint8_t flag_byte = auth_data[kApplicationParameterLength];
- std::vector<uint8_t> counter(
- auth_data.data() + kApplicationParameterLength + 1,
- auth_data.data() + kApplicationParameterLength + 1 +
- kAuthDataCounterLength);
+ auto application_parameter = auth_data.first<kRpIdHashLength>();
+ uint8_t flag_byte = auth_data[kRpIdHashLength];
+ auto counter =
+ auth_data.subspan<kRpIdHashLength + kFlagsLength, kSignCounterLength>();
auto attested_credential_data =
AttestedCredentialData::DecodeFromCtapResponse(
- auth_data.subspan(kAaguidOffset));
+ auth_data.subspan(kAttestedCredentialDataOffset));
- return AuthenticatorData(std::move(application_parameter), flag_byte,
- std::move(counter),
+ return AuthenticatorData(application_parameter, flag_byte, counter,
std::move(attested_credential_data));
}
AuthenticatorData::AuthenticatorData(
- std::vector<uint8_t> application_parameter,
+ base::span<const uint8_t, kRpIdHashLength> application_parameter,
uint8_t flags,
- std::vector<uint8_t> counter,
+ base::span<const uint8_t, kSignCounterLength> counter,
base::Optional<AttestedCredentialData> data)
- : application_parameter_(std::move(application_parameter)),
+ : application_parameter_(
+ fido_parsing_utils::Materialize(application_parameter)),
flags_(flags),
- counter_(std::move(counter)),
- attested_data_(std::move(data)) {
- // TODO(kpaulhamus): use std::array for these small, fixed-sized vectors.
- CHECK_EQ(counter_.size(), 4u);
-}
+ counter_(fido_parsing_utils::Materialize(counter)),
+ attested_data_(std::move(data)) {}
AuthenticatorData::AuthenticatorData(AuthenticatorData&& other) = default;
AuthenticatorData& AuthenticatorData::operator=(AuthenticatorData&& other) =
diff --git a/chromium/device/fido/authenticator_data.h b/chromium/device/fido/authenticator_data.h
index 5af3fd8c5f6..e4e9e565833 100644
--- a/chromium/device/fido/authenticator_data.h
+++ b/chromium/device/fido/authenticator_data.h
@@ -6,14 +6,18 @@
#define DEVICE_FIDO_AUTHENTICATOR_DATA_H_
#include <stdint.h>
+
+#include <array>
#include <string>
#include <vector>
#include "base/component_export.h"
+#include "base/containers/span.h"
#include "base/macros.h"
#include "base/numerics/safe_conversions.h"
#include "base/optional.h"
#include "device/fido/attested_credential_data.h"
+#include "device/fido/fido_constants.h"
namespace device {
@@ -30,10 +34,11 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorData {
static base::Optional<AuthenticatorData> DecodeAuthenticatorData(
base::span<const uint8_t> auth_data);
- AuthenticatorData(std::vector<uint8_t> application_parameter,
- uint8_t flags,
- std::vector<uint8_t> counter,
- base::Optional<AttestedCredentialData> data);
+ AuthenticatorData(
+ base::span<const uint8_t, kRpIdHashLength> application_parameter,
+ uint8_t flags,
+ base::span<const uint8_t, kSignCounterLength> counter,
+ base::Optional<AttestedCredentialData> data);
// Moveable.
AuthenticatorData(AuthenticatorData&& other);
@@ -60,7 +65,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorData {
return attested_data_;
}
- const std::vector<uint8_t>& application_parameter() const {
+ const std::array<uint8_t, kRpIdHashLength>& application_parameter() const {
return application_parameter_;
}
@@ -80,11 +85,14 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorData {
return flags_ & base::strict_cast<uint8_t>(Flag::kExtensionDataIncluded);
}
+ base::span<const uint8_t, kSignCounterLength> counter() const {
+ return counter_;
+ }
+
private:
// The application parameter: a SHA-256 hash of either the RP ID or the AppID
// associated with the credential.
- // TODO(hongjunchoi): Replace fixed size vector components with std::array.
- std::vector<uint8_t> application_parameter_;
+ std::array<uint8_t, kRpIdHashLength> application_parameter_;
// Flags (bit 0 is the least significant bit):
// [ED | AT | RFU | RFU | RFU | RFU | RFU | UP ]
@@ -95,7 +103,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorData {
uint8_t flags_;
// Signature counter, 32-bit unsigned big-endian integer.
- std::vector<uint8_t> counter_;
+ std::array<uint8_t, kSignCounterLength> counter_;
base::Optional<AttestedCredentialData> attested_data_;
DISALLOW_COPY_AND_ASSIGN(AuthenticatorData);
diff --git a/chromium/device/fido/authenticator_get_assertion_response.cc b/chromium/device/fido/authenticator_get_assertion_response.cc
index dc83b75b500..a02868d4413 100644
--- a/chromium/device/fido/authenticator_get_assertion_response.cc
+++ b/chromium/device/fido/authenticator_get_assertion_response.cc
@@ -7,6 +7,8 @@
#include <utility>
#include "base/optional.h"
+#include "components/cbor/cbor_values.h"
+#include "components/cbor/cbor_writer.h"
#include "device/fido/authenticator_data.h"
#include "device/fido/fido_parsing_utils.h"
@@ -25,34 +27,26 @@ constexpr size_t kSignatureIndex = 5;
// static
base::Optional<AuthenticatorGetAssertionResponse>
AuthenticatorGetAssertionResponse::CreateFromU2fSignResponse(
- const std::vector<uint8_t>& relying_party_id_hash,
- const std::vector<uint8_t>& u2f_data,
- const std::vector<uint8_t>& key_handle) {
- if (key_handle.empty())
- return base::nullopt;
-
- auto flags = fido_parsing_utils::Extract(u2f_data, kFlagIndex, kFlagLength);
- if (flags.empty())
+ base::span<const uint8_t, kRpIdHashLength> relying_party_id_hash,
+ base::span<const uint8_t> u2f_data,
+ base::span<const uint8_t> key_handle) {
+ if (u2f_data.size() <= kSignatureIndex)
return base::nullopt;
- auto counter =
- fido_parsing_utils::Extract(u2f_data, kCounterIndex, kCounterLength);
- if (counter.empty())
+ if (key_handle.empty())
return base::nullopt;
- AuthenticatorData authenticator_data(relying_party_id_hash, flags[0],
- std::move(counter), base::nullopt);
-
- auto signature = fido_parsing_utils::Extract(
- u2f_data, kSignatureIndex, u2f_data.size() - kSignatureIndex);
- if (signature.empty())
- return base::nullopt;
+ auto flags = u2f_data.subspan<kFlagIndex, kFlagLength>();
+ auto counter = u2f_data.subspan<kCounterIndex, kCounterLength>();
+ AuthenticatorData authenticator_data(relying_party_id_hash, flags[0], counter,
+ base::nullopt);
+ auto signature =
+ fido_parsing_utils::Materialize(u2f_data.subspan(kSignatureIndex));
AuthenticatorGetAssertionResponse response(std::move(authenticator_data),
std::move(signature));
- response.SetCredential(
- PublicKeyCredentialDescriptor(CredentialType::kPublicKey, key_handle));
-
+ response.SetCredential(PublicKeyCredentialDescriptor(
+ CredentialType::kPublicKey, fido_parsing_utils::Materialize(key_handle)));
return std::move(response);
}
@@ -71,8 +65,8 @@ AuthenticatorGetAssertionResponse& AuthenticatorGetAssertionResponse::operator=(
AuthenticatorGetAssertionResponse::~AuthenticatorGetAssertionResponse() =
default;
-const std::vector<uint8_t>& AuthenticatorGetAssertionResponse::GetRpIdHash()
- const {
+const std::array<uint8_t, kRpIdHashLength>&
+AuthenticatorGetAssertionResponse::GetRpIdHash() const {
return authenticator_data_.application_parameter();
}
@@ -97,4 +91,24 @@ AuthenticatorGetAssertionResponse::SetNumCredentials(uint8_t num_credentials) {
return *this;
}
+std::vector<uint8_t> GetSerializedCtapDeviceResponse(
+ const AuthenticatorGetAssertionResponse& response) {
+ cbor::CBORValue::MapValue response_map;
+ if (response.credential())
+ response_map.emplace(1, response.credential()->ConvertToCBOR());
+
+ response_map.emplace(2, response.auth_data().SerializeToByteArray());
+ response_map.emplace(3, response.signature());
+
+ if (response.user_entity())
+ response_map.emplace(4, response.user_entity()->ConvertToCBOR());
+
+ // Multiple account selection is not supported.
+ response_map.emplace(5, 1);
+ auto encoded_response =
+ cbor::CBORWriter::Write(cbor::CBORValue(std::move(response_map)));
+ DCHECK(encoded_response);
+ return *encoded_response;
+}
+
} // namespace device
diff --git a/chromium/device/fido/authenticator_get_assertion_response.h b/chromium/device/fido/authenticator_get_assertion_response.h
index 1e7eec30461..72f492d80de 100644
--- a/chromium/device/fido/authenticator_get_assertion_response.h
+++ b/chromium/device/fido/authenticator_get_assertion_response.h
@@ -27,9 +27,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorGetAssertionResponse
: public ResponseData {
public:
static base::Optional<AuthenticatorGetAssertionResponse>
- CreateFromU2fSignResponse(const std::vector<uint8_t>& relying_party_id_hash,
- const std::vector<uint8_t>& u2f_data,
- const std::vector<uint8_t>& key_handle);
+ CreateFromU2fSignResponse(
+ base::span<const uint8_t, kRpIdHashLength> relying_party_id_hash,
+ base::span<const uint8_t> u2f_data,
+ base::span<const uint8_t> key_handle);
AuthenticatorGetAssertionResponse(AuthenticatorData authenticator_data,
std::vector<uint8_t> signature);
@@ -39,7 +40,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorGetAssertionResponse
~AuthenticatorGetAssertionResponse() override;
// ResponseData:
- const std::vector<uint8_t>& GetRpIdHash() const override;
+ const std::array<uint8_t, kRpIdHashLength>& GetRpIdHash() const override;
AuthenticatorGetAssertionResponse& SetCredential(
PublicKeyCredentialDescriptor credential);
@@ -69,6 +70,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorGetAssertionResponse
DISALLOW_COPY_AND_ASSIGN(AuthenticatorGetAssertionResponse);
};
+COMPONENT_EXPORT(DEVICE_FIDO)
+std::vector<uint8_t> GetSerializedCtapDeviceResponse(
+ const AuthenticatorGetAssertionResponse& response);
+
} // namespace device
#endif // DEVICE_FIDO_AUTHENTICATOR_GET_ASSERTION_RESPONSE_H_
diff --git a/chromium/device/fido/authenticator_get_info_response.cc b/chromium/device/fido/authenticator_get_info_response.cc
index 9645c5b2037..37de6493c09 100644
--- a/chromium/device/fido/authenticator_get_info_response.cc
+++ b/chromium/device/fido/authenticator_get_info_response.cc
@@ -6,12 +6,30 @@
#include <utility>
+#include "components/cbor/cbor_values.h"
+#include "components/cbor/cbor_writer.h"
+#include "device/fido/fido_parsing_utils.h"
+
namespace device {
+namespace {
+
+template <typename Container>
+cbor::CBORValue::ArrayValue ToArrayValue(const Container& container) {
+ cbor::CBORValue::ArrayValue value;
+ value.reserve(container.size());
+ for (const auto& item : container)
+ value.emplace_back(item);
+ return value;
+}
+
+} // namespace
+
AuthenticatorGetInfoResponse::AuthenticatorGetInfoResponse(
- std::vector<std::string> versions,
- std::vector<uint8_t> aaguid)
- : versions_(std::move(versions)), aaguid_(std::move(aaguid)) {}
+ base::flat_set<ProtocolVersion> versions,
+ base::span<const uint8_t, kAaguidLength> aaguid)
+ : versions_(std::move(versions)),
+ aaguid_(fido_parsing_utils::Materialize(aaguid)) {}
AuthenticatorGetInfoResponse::AuthenticatorGetInfoResponse(
AuthenticatorGetInfoResponse&& that) = default;
@@ -22,7 +40,7 @@ AuthenticatorGetInfoResponse& AuthenticatorGetInfoResponse::operator=(
AuthenticatorGetInfoResponse::~AuthenticatorGetInfoResponse() = default;
AuthenticatorGetInfoResponse& AuthenticatorGetInfoResponse::SetMaxMsgSize(
- uint8_t max_msg_size) {
+ uint32_t max_msg_size) {
max_msg_size_ = max_msg_size;
return *this;
}
@@ -45,4 +63,35 @@ AuthenticatorGetInfoResponse& AuthenticatorGetInfoResponse::SetOptions(
return *this;
}
+std::vector<uint8_t> EncodeToCBOR(
+ const AuthenticatorGetInfoResponse& response) {
+ cbor::CBORValue::ArrayValue version_array;
+ for (const auto& version : response.versions()) {
+ version_array.emplace_back(version == ProtocolVersion::kCtap ? kCtap2Version
+ : kU2fVersion);
+ }
+ cbor::CBORValue::MapValue device_info_map;
+ device_info_map.emplace(1, std::move(version_array));
+
+ if (response.extensions())
+ device_info_map.emplace(2, ToArrayValue(*response.extensions()));
+
+ device_info_map.emplace(3, response.aaguid());
+ device_info_map.emplace(4, ConvertToCBOR(response.options()));
+
+ if (response.max_msg_size()) {
+ device_info_map.emplace(
+ 5, base::strict_cast<int64_t>(*response.max_msg_size()));
+ }
+
+ if (response.pin_protocol()) {
+ device_info_map.emplace(6, ToArrayValue(*response.pin_protocol()));
+ }
+
+ auto encoded_bytes =
+ cbor::CBORWriter::Write(cbor::CBORValue(std::move(device_info_map)));
+ DCHECK(encoded_bytes);
+ return *encoded_bytes;
+}
+
} // namespace device
diff --git a/chromium/device/fido/authenticator_get_info_response.h b/chromium/device/fido/authenticator_get_info_response.h
index 8b29e918398..b6fd9b187c9 100644
--- a/chromium/device/fido/authenticator_get_info_response.h
+++ b/chromium/device/fido/authenticator_get_info_response.h
@@ -11,6 +11,7 @@
#include <vector>
#include "base/component_export.h"
+#include "base/containers/flat_set.h"
#include "base/macros.h"
#include "base/optional.h"
#include "device/fido/authenticator_supported_options.h"
@@ -24,13 +25,13 @@ namespace device {
// https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html#authenticatorGetInfo
class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorGetInfoResponse {
public:
- AuthenticatorGetInfoResponse(std::vector<std::string> versions,
- std::vector<uint8_t> aaguid);
+ AuthenticatorGetInfoResponse(base::flat_set<ProtocolVersion> versions,
+ base::span<const uint8_t, kAaguidLength> aaguid);
AuthenticatorGetInfoResponse(AuthenticatorGetInfoResponse&& that);
AuthenticatorGetInfoResponse& operator=(AuthenticatorGetInfoResponse&& other);
~AuthenticatorGetInfoResponse();
- AuthenticatorGetInfoResponse& SetMaxMsgSize(uint8_t max_msg_size);
+ AuthenticatorGetInfoResponse& SetMaxMsgSize(uint32_t max_msg_size);
AuthenticatorGetInfoResponse& SetPinProtocols(
std::vector<uint8_t> pin_protocols);
AuthenticatorGetInfoResponse& SetExtensions(
@@ -38,9 +39,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorGetInfoResponse {
AuthenticatorGetInfoResponse& SetOptions(
AuthenticatorSupportedOptions options);
- const std::vector<std::string>& versions() { return versions_; }
- const std::vector<uint8_t>& aaguid() const { return aaguid_; }
- const base::Optional<uint8_t>& max_msg_size() const { return max_msg_size_; }
+ const base::flat_set<ProtocolVersion>& versions() const { return versions_; }
+ const std::array<uint8_t, kAaguidLength>& aaguid() const { return aaguid_; }
+ const base::Optional<uint32_t>& max_msg_size() const { return max_msg_size_; }
const base::Optional<std::vector<uint8_t>>& pin_protocol() const {
return pin_protocols_;
}
@@ -50,9 +51,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorGetInfoResponse {
const AuthenticatorSupportedOptions& options() const { return options_; }
private:
- std::vector<std::string> versions_;
- std::vector<uint8_t> aaguid_;
- base::Optional<uint8_t> max_msg_size_;
+ base::flat_set<ProtocolVersion> versions_;
+ std::array<uint8_t, kAaguidLength> aaguid_;
+ base::Optional<uint32_t> max_msg_size_;
base::Optional<std::vector<uint8_t>> pin_protocols_;
base::Optional<std::vector<std::string>> extensions_;
AuthenticatorSupportedOptions options_;
@@ -60,6 +61,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorGetInfoResponse {
DISALLOW_COPY_AND_ASSIGN(AuthenticatorGetInfoResponse);
};
+COMPONENT_EXPORT(DEVICE_FIDO)
+std::vector<uint8_t> EncodeToCBOR(const AuthenticatorGetInfoResponse& response);
+
} // namespace device
#endif // DEVICE_FIDO_AUTHENTICATOR_GET_INFO_RESPONSE_H_
diff --git a/chromium/device/fido/authenticator_make_credential_response.cc b/chromium/device/fido/authenticator_make_credential_response.cc
index e916787ef60..fd6576e092e 100644
--- a/chromium/device/fido/authenticator_make_credential_response.cc
+++ b/chromium/device/fido/authenticator_make_credential_response.cc
@@ -7,10 +7,10 @@
#include <utility>
#include "device/fido/attestation_object.h"
+#include "device/fido/attestation_statement_formats.h"
#include "device/fido/attested_credential_data.h"
#include "device/fido/authenticator_data.h"
#include "device/fido/ec_public_key.h"
-#include "device/fido/fido_attestation_statement.h"
#include "device/fido/fido_parsing_utils.h"
namespace device {
@@ -18,7 +18,7 @@ namespace device {
// static
base::Optional<AuthenticatorMakeCredentialResponse>
AuthenticatorMakeCredentialResponse::CreateFromU2fRegisterResponse(
- const std::vector<uint8_t>& relying_party_id_hash,
+ base::span<const uint8_t, kRpIdHashLength> relying_party_id_hash,
base::span<const uint8_t> u2f_data) {
auto public_key = ECPublicKey::ExtractFromU2fRegistrationResponse(
fido_parsing_utils::kEs256, u2f_data);
@@ -37,13 +37,12 @@ AuthenticatorMakeCredentialResponse::CreateFromU2fRegisterResponse(
attested_credential_data->credential_id();
// The counter is zeroed out for Register requests.
- std::vector<uint8_t> counter(4u);
+ std::array<uint8_t, 4> counter = {};
constexpr uint8_t flags =
static_cast<uint8_t>(AuthenticatorData::Flag::kTestOfUserPresence) |
static_cast<uint8_t>(AuthenticatorData::Flag::kAttestation);
- AuthenticatorData authenticator_data(std::move(relying_party_id_hash), flags,
- std::move(counter),
+ AuthenticatorData authenticator_data(relying_party_id_hash, flags, counter,
std::move(attested_credential_data));
auto fido_attestation_statement =
@@ -85,9 +84,14 @@ bool AuthenticatorMakeCredentialResponse::
.IsAttestationCertificateInappropriatelyIdentifying();
}
-const std::vector<uint8_t>& AuthenticatorMakeCredentialResponse::GetRpIdHash()
- const {
+const std::array<uint8_t, kRpIdHashLength>&
+AuthenticatorMakeCredentialResponse::GetRpIdHash() const {
return attestation_object_.rp_id_hash();
}
+std::vector<uint8_t> GetSerializedCtapDeviceResponse(
+ const AuthenticatorMakeCredentialResponse& response) {
+ return SerializeToCtapStyleCborEncodedBytes(response.attestation_object());
+}
+
} // namespace device
diff --git a/chromium/device/fido/authenticator_make_credential_response.h b/chromium/device/fido/authenticator_make_credential_response.h
index e098584f91b..dc9a6e7438a 100644
--- a/chromium/device/fido/authenticator_make_credential_response.h
+++ b/chromium/device/fido/authenticator_make_credential_response.h
@@ -7,6 +7,7 @@
#include <stdint.h>
+#include <array>
#include <vector>
#include "base/component_export.h"
@@ -28,7 +29,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorMakeCredentialResponse
public:
static base::Optional<AuthenticatorMakeCredentialResponse>
CreateFromU2fRegisterResponse(
- const std::vector<uint8_t>& relying_party_id_hash,
+ base::span<const uint8_t, kRpIdHashLength> relying_party_id_hash,
base::span<const uint8_t> u2f_data);
AuthenticatorMakeCredentialResponse(AttestationObject attestation_object);
@@ -48,11 +49,15 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorMakeCredentialResponse
// 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
- // not indended to be trackable.)
+ // not intended to be trackable.)
bool IsAttestationCertificateInappropriatelyIdentifying();
// ResponseData:
- const std::vector<uint8_t>& GetRpIdHash() const override;
+ const std::array<uint8_t, kRpIdHashLength>& GetRpIdHash() const override;
+
+ const AttestationObject& attestation_object() const {
+ return attestation_object_;
+ }
private:
AttestationObject attestation_object_;
@@ -60,6 +65,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorMakeCredentialResponse
DISALLOW_COPY_AND_ASSIGN(AuthenticatorMakeCredentialResponse);
};
+COMPONENT_EXPORT(DEVICE_FIDO)
+std::vector<uint8_t> GetSerializedCtapDeviceResponse(
+ const AuthenticatorMakeCredentialResponse& response);
+
} // namespace device
#endif // DEVICE_FIDO_AUTHENTICATOR_MAKE_CREDENTIAL_RESPONSE_H_
diff --git a/chromium/device/fido/authenticator_supported_options.cc b/chromium/device/fido/authenticator_supported_options.cc
index 8ded4dde4ba..af08a3c6e70 100644
--- a/chromium/device/fido/authenticator_supported_options.cc
+++ b/chromium/device/fido/authenticator_supported_options.cc
@@ -4,6 +4,10 @@
#include "device/fido/authenticator_supported_options.h"
+#include <utility>
+
+#include "device/fido/fido_constants.h"
+
namespace device {
AuthenticatorSupportedOptions::AuthenticatorSupportedOptions() = default;
@@ -17,12 +21,6 @@ AuthenticatorSupportedOptions& AuthenticatorSupportedOptions::operator=(
AuthenticatorSupportedOptions::~AuthenticatorSupportedOptions() = default;
AuthenticatorSupportedOptions&
-AuthenticatorSupportedOptions::SetIsPlatformDevice(bool is_platform_device) {
- is_platform_device_ = is_platform_device;
- return *this;
-}
-
-AuthenticatorSupportedOptions&
AuthenticatorSupportedOptions::SetSupportsResidentKey(
bool supports_resident_key) {
supports_resident_key_ = supports_resident_key;
@@ -50,4 +48,47 @@ AuthenticatorSupportedOptions::SetClientPinAvailability(
return *this;
}
+AuthenticatorSupportedOptions&
+AuthenticatorSupportedOptions::SetIsPlatformDevice(bool is_platform_device) {
+ is_platform_device_ = is_platform_device;
+ return *this;
+}
+
+cbor::CBORValue ConvertToCBOR(const AuthenticatorSupportedOptions& options) {
+ cbor::CBORValue::MapValue option_map;
+ option_map.emplace(kResidentKeyMapKey, options.supports_resident_key());
+ option_map.emplace(kUserPresenceMapKey, options.user_presence_required());
+ option_map.emplace(kPlatformDeviceMapKey, options.is_platform_device());
+
+ using UvAvailability =
+ AuthenticatorSupportedOptions::UserVerificationAvailability;
+
+ switch (options.user_verification_availability()) {
+ case UvAvailability::kSupportedAndConfigured:
+ option_map.emplace(kUserVerificationMapKey, true);
+ break;
+ case UvAvailability::kSupportedButNotConfigured:
+ option_map.emplace(kUserVerificationMapKey, false);
+ break;
+ case UvAvailability::kNotSupported:
+ break;
+ }
+
+ using ClientPinAvailability =
+ AuthenticatorSupportedOptions::ClientPinAvailability;
+
+ switch (options.client_pin_availability()) {
+ case ClientPinAvailability::kSupportedAndPinSet:
+ option_map.emplace(kClientPinMapKey, true);
+ break;
+ case ClientPinAvailability::kSupportedButPinNotSet:
+ option_map.emplace(kClientPinMapKey, false);
+ break;
+ case ClientPinAvailability::kNotSupported:
+ break;
+ }
+
+ return cbor::CBORValue(std::move(option_map));
+}
+
} // namespace device
diff --git a/chromium/device/fido/authenticator_supported_options.h b/chromium/device/fido/authenticator_supported_options.h
index 4ff2cf5b4a6..0c6bed03f05 100644
--- a/chromium/device/fido/authenticator_supported_options.h
+++ b/chromium/device/fido/authenticator_supported_options.h
@@ -38,7 +38,6 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorSupportedOptions {
AuthenticatorSupportedOptions&& other);
~AuthenticatorSupportedOptions();
- cbor::CBORValue ConvertToCBOR() const;
AuthenticatorSupportedOptions& SetIsPlatformDevice(bool is_platform_device);
AuthenticatorSupportedOptions& SetSupportsResidentKey(
bool supports_resident_key);
@@ -73,11 +72,15 @@ class COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorSupportedOptions {
bool user_presence_required_ = true;
// Represents whether client pin in set and stored in device. Set as null
// optional if client pin capability is not supported by the authenticator.
- ClientPinAvailability client_pin_availability_;
+ ClientPinAvailability client_pin_availability_ =
+ ClientPinAvailability::kNotSupported;
DISALLOW_COPY_AND_ASSIGN(AuthenticatorSupportedOptions);
};
+COMPONENT_EXPORT(DEVICE_FIDO)
+cbor::CBORValue ConvertToCBOR(const AuthenticatorSupportedOptions& options);
+
} // namespace device
#endif // DEVICE_FIDO_AUTHENTICATOR_SUPPORTED_OPTIONS_H_
diff --git a/chromium/device/fido/ctap2_device_operation.h b/chromium/device/fido/ctap2_device_operation.h
new file mode 100644
index 00000000000..1cba8f9b4a0
--- /dev/null
+++ b/chromium/device/fido/ctap2_device_operation.h
@@ -0,0 +1,79 @@
+// 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_CTAP2_DEVICE_OPERATION_H_
+#define DEVICE_FIDO_CTAP2_DEVICE_OPERATION_H_
+
+#include <stdint.h>
+
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/containers/span.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "device/fido/device_operation.h"
+#include "device/fido/device_response_converter.h"
+#include "device/fido/fido_constants.h"
+#include "device/fido/fido_device.h"
+
+namespace device {
+
+// Represents per device logic for CTAP2 authenticators. Ctap2DeviceOperation
+// is owned by FidoTask, and thus |request| outlives Ctap2DeviceOperation.
+template <class Request, class Response>
+class Ctap2DeviceOperation : public DeviceOperation<Request, Response> {
+ public:
+ using DeviceResponseCallback =
+ base::OnceCallback<void(CtapDeviceResponseCode,
+ base::Optional<Response>)>;
+ using DeviceResponseParser =
+ base::OnceCallback<base::Optional<Response>(base::span<const uint8_t>)>;
+
+ Ctap2DeviceOperation(FidoDevice* device,
+ const Request& request,
+ DeviceResponseCallback callback,
+ DeviceResponseParser device_response_parser)
+ : DeviceOperation<Request, Response>(device,
+ request,
+ std::move(callback)),
+ device_response_parser_(std::move(device_response_parser)),
+ weak_factory_(this) {}
+
+ ~Ctap2DeviceOperation() override = default;
+
+ void Start() override {
+ this->device()->DeviceTransact(
+ this->request().EncodeAsCBOR(),
+ base::BindOnce(&Ctap2DeviceOperation::OnResponseReceived,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void OnResponseReceived(
+ base::Optional<std::vector<uint8_t>> device_response) {
+ if (!device_response) {
+ std::move(this->callback())
+ .Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
+ return;
+ }
+
+ const auto response_code = GetResponseCode(*device_response);
+ std::move(this->callback())
+ .Run(response_code, std::move(device_response_parser_)
+ .Run(std::move(*device_response)));
+ }
+
+ private:
+ DeviceResponseParser device_response_parser_;
+ base::WeakPtrFactory<Ctap2DeviceOperation> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(Ctap2DeviceOperation);
+};
+
+} // namespace device
+
+#endif // DEVICE_FIDO_CTAP2_DEVICE_OPERATION_H_
diff --git a/chromium/device/fido/ctap_get_assertion_request.cc b/chromium/device/fido/ctap_get_assertion_request.cc
index 8fb876ad766..bcb44dc4df5 100644
--- a/chromium/device/fido/ctap_get_assertion_request.cc
+++ b/chromium/device/fido/ctap_get_assertion_request.cc
@@ -4,19 +4,52 @@
#include "device/fido/ctap_get_assertion_request.h"
+#include <algorithm>
+#include <limits>
#include <utility>
#include "base/numerics/safe_conversions.h"
+#include "components/cbor/cbor_reader.h"
#include "components/cbor/cbor_writer.h"
#include "device/fido/fido_constants.h"
+#include "device/fido/fido_parsing_utils.h"
namespace device {
+namespace {
+
+bool AreGetAssertionRequestMapKeysCorrect(
+ const cbor::CBORValue::MapValue& request_map) {
+ return std::all_of(request_map.begin(), request_map.end(),
+ [](const auto& param) {
+ if (!param.first.is_integer())
+ return false;
+
+ const auto& key = param.first.GetInteger();
+ return (key <= 7u || key >= 1u);
+ });
+}
+
+bool IsGetAssertionOptionMapFormatCorrect(
+ const cbor::CBORValue::MapValue& option_map) {
+ return std::all_of(
+ option_map.begin(), option_map.end(), [](const auto& param) {
+ if (!param.first.is_string())
+ return false;
+
+ const auto& key = param.first.GetString();
+ return (key == kUserPresenceMapKey || key == kUserVerificationMapKey) &&
+ param.second.is_bool();
+ });
+}
+
+} // namespace
+
CtapGetAssertionRequest::CtapGetAssertionRequest(
std::string rp_id,
- std::vector<uint8_t> client_data_hash)
+ base::span<const uint8_t, kClientDataHashLength> client_data_hash)
: rp_id_(std::move(rp_id)),
- client_data_hash_(std::move(client_data_hash)) {}
+ client_data_hash_(fido_parsing_utils::Materialize(client_data_hash)) {}
CtapGetAssertionRequest::CtapGetAssertionRequest(
const CtapGetAssertionRequest& that) = default;
@@ -118,4 +151,99 @@ CtapGetAssertionRequest& CtapGetAssertionRequest::SetCableExtension(
return *this;
}
+CtapGetAssertionRequest&
+CtapGetAssertionRequest::SetAlternativeApplicationParameter(
+ base::span<const uint8_t, kRpIdHashLength>
+ alternative_application_parameter) {
+ alternative_application_parameter_ =
+ fido_parsing_utils::Materialize(alternative_application_parameter);
+ return *this;
+}
+
+base::Optional<CtapGetAssertionRequest> ParseCtapGetAssertionRequest(
+ base::span<const uint8_t> request_bytes) {
+ const auto& cbor_request = cbor::CBORReader::Read(request_bytes);
+ if (!cbor_request || !cbor_request->is_map())
+ return base::nullopt;
+
+ const auto& request_map = cbor_request->GetMap();
+ if (!AreGetAssertionRequestMapKeysCorrect(request_map))
+ return base::nullopt;
+
+ const auto rp_id_it = request_map.find(cbor::CBORValue(1));
+ if (rp_id_it == request_map.end() || !rp_id_it->second.is_string())
+ return base::nullopt;
+
+ const auto client_data_hash_it = request_map.find(cbor::CBORValue(2));
+ if (client_data_hash_it == request_map.end() ||
+ !client_data_hash_it->second.is_bytestring())
+ return base::nullopt;
+
+ const auto client_data_hash =
+ base::make_span(client_data_hash_it->second.GetBytestring())
+ .subspan<0, kClientDataHashLength>();
+ CtapGetAssertionRequest request(rp_id_it->second.GetString(),
+ client_data_hash);
+
+ const auto allow_list_it = request_map.find(cbor::CBORValue(3));
+ if (allow_list_it != request_map.end()) {
+ if (!allow_list_it->second.is_array())
+ return base::nullopt;
+
+ const auto& credential_descriptors = allow_list_it->second.GetArray();
+ std::vector<PublicKeyCredentialDescriptor> allow_list;
+ for (const auto& credential_descriptor : credential_descriptors) {
+ auto allowed_credential =
+ PublicKeyCredentialDescriptor::CreateFromCBORValue(
+ credential_descriptor);
+ if (!allowed_credential)
+ return base::nullopt;
+
+ allow_list.push_back(std::move(*allowed_credential));
+ }
+ request.SetAllowList(std::move(allow_list));
+ }
+
+ const auto option_it = request_map.find(cbor::CBORValue(5));
+ if (option_it != request_map.end()) {
+ if (!option_it->second.is_map())
+ return base::nullopt;
+
+ const auto& option_map = option_it->second.GetMap();
+ if (!IsGetAssertionOptionMapFormatCorrect(option_map))
+ return base::nullopt;
+
+ const auto user_presence_option =
+ option_map.find(cbor::CBORValue(kUserPresenceMapKey));
+ if (user_presence_option != option_map.end())
+ request.SetUserPresenceRequired(user_presence_option->second.GetBool());
+
+ const auto uv_option =
+ option_map.find(cbor::CBORValue(kUserVerificationMapKey));
+ if (uv_option != option_map.end())
+ request.SetUserVerification(
+ uv_option->second.GetBool()
+ ? UserVerificationRequirement::kRequired
+ : UserVerificationRequirement::kPreferred);
+ }
+
+ const auto pin_auth_it = request_map.find(cbor::CBORValue(6));
+ if (pin_auth_it != request_map.end()) {
+ if (!pin_auth_it->second.is_bytestring())
+ return base::nullopt;
+ request.SetPinAuth(pin_auth_it->second.GetBytestring());
+ }
+
+ const auto pin_protocol_it = request_map.find(cbor::CBORValue(7));
+ if (pin_protocol_it != request_map.end()) {
+ if (!pin_protocol_it->second.is_unsigned() ||
+ pin_protocol_it->second.GetUnsigned() >
+ std::numeric_limits<uint8_t>::max())
+ return base::nullopt;
+ request.SetPinProtocol(pin_auth_it->second.GetUnsigned());
+ }
+
+ return request;
+}
+
} // namespace device
diff --git a/chromium/device/fido/ctap_get_assertion_request.h b/chromium/device/fido/ctap_get_assertion_request.h
index 25ee8a6a186..c92ae9f5b2b 100644
--- a/chromium/device/fido/ctap_get_assertion_request.h
+++ b/chromium/device/fido/ctap_get_assertion_request.h
@@ -11,6 +11,7 @@
#include <vector>
#include "base/component_export.h"
+#include "base/containers/span.h"
#include "base/macros.h"
#include "base/optional.h"
#include "device/fido/fido_cable_discovery.h"
@@ -24,8 +25,9 @@ namespace device {
// https://fidoalliance.org/specs/fido-v2.0-rd-20161004/fido-client-to-authenticator-protocol-v2.0-rd-20161004.html#authenticatorgetassertion
class COMPONENT_EXPORT(DEVICE_FIDO) CtapGetAssertionRequest {
public:
- CtapGetAssertionRequest(std::string rp_id,
- std::vector<uint8_t> client_data_hash);
+ CtapGetAssertionRequest(
+ std::string rp_id,
+ base::span<const uint8_t, kClientDataHashLength> client_data_hash);
CtapGetAssertionRequest(const CtapGetAssertionRequest& that);
CtapGetAssertionRequest(CtapGetAssertionRequest&& that);
CtapGetAssertionRequest& operator=(const CtapGetAssertionRequest& other);
@@ -46,9 +48,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapGetAssertionRequest {
CtapGetAssertionRequest& SetPinProtocol(uint8_t pin_protocol);
CtapGetAssertionRequest& SetCableExtension(
std::vector<FidoCableDiscovery::CableDiscoveryData> cable_extension);
+ CtapGetAssertionRequest& SetAlternativeApplicationParameter(
+ base::span<const uint8_t, kRpIdHashLength>
+ alternative_application_parameter);
const std::string& rp_id() const { return rp_id_; }
- const std::vector<uint8_t>& client_data_hash() const {
+ const std::array<uint8_t, kClientDataHashLength>& client_data_hash() const {
return client_data_hash_;
}
@@ -71,10 +76,14 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapGetAssertionRequest {
cable_extension() const {
return cable_extension_;
}
+ const base::Optional<std::array<uint8_t, kRpIdHashLength>>&
+ alternative_application_parameter() const {
+ return alternative_application_parameter_;
+ }
private:
std::string rp_id_;
- std::vector<uint8_t> client_data_hash_;
+ std::array<uint8_t, kClientDataHashLength> client_data_hash_;
UserVerificationRequirement user_verification_ =
UserVerificationRequirement::kPreferred;
bool user_presence_required_ = true;
@@ -84,8 +93,14 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapGetAssertionRequest {
base::Optional<uint8_t> pin_protocol_;
base::Optional<std::vector<FidoCableDiscovery::CableDiscoveryData>>
cable_extension_;
+ base::Optional<std::array<uint8_t, kRpIdHashLength>>
+ alternative_application_parameter_;
};
+COMPONENT_EXPORT(DEVICE_FIDO)
+base::Optional<CtapGetAssertionRequest> ParseCtapGetAssertionRequest(
+ base::span<const uint8_t> request_bytes);
+
} // namespace device
#endif // DEVICE_FIDO_CTAP_GET_ASSERTION_REQUEST_H_
diff --git a/chromium/device/fido/ctap_make_credential_request.cc b/chromium/device/fido/ctap_make_credential_request.cc
index 5b8049d55e1..5300df4a4f3 100644
--- a/chromium/device/fido/ctap_make_credential_request.cc
+++ b/chromium/device/fido/ctap_make_credential_request.cc
@@ -4,20 +4,53 @@
#include "device/fido/ctap_make_credential_request.h"
+#include <algorithm>
+#include <limits>
#include <utility>
#include "base/numerics/safe_conversions.h"
+#include "components/cbor/cbor_reader.h"
#include "components/cbor/cbor_writer.h"
#include "device/fido/fido_constants.h"
+#include "device/fido/fido_parsing_utils.h"
namespace device {
+namespace {
+
+bool AreMakeCredentialRequestMapKeysCorrect(
+ const cbor::CBORValue::MapValue& request_map) {
+ return std::all_of(request_map.begin(), request_map.end(),
+ [](const auto& param) {
+ if (!param.first.is_integer())
+ return false;
+
+ const auto& key = param.first.GetInteger();
+ return (key <= 9u && key >= 1u);
+ });
+}
+
+bool IsMakeCredentialOptionMapFormatCorrect(
+ const cbor::CBORValue::MapValue& option_map) {
+ return std::all_of(
+ option_map.begin(), option_map.end(), [](const auto& param) {
+ if (!param.first.is_string())
+ return false;
+
+ const auto& key = param.first.GetString();
+ return ((key == kResidentKeyMapKey || key == kUserVerificationMapKey) &&
+ param.second.is_bool());
+ });
+}
+
+} // namespace
+
CtapMakeCredentialRequest::CtapMakeCredentialRequest(
- std::vector<uint8_t> client_data_hash,
+ base::span<const uint8_t, kClientDataHashLength> client_data_hash,
PublicKeyCredentialRpEntity rp,
PublicKeyCredentialUserEntity user,
PublicKeyCredentialParams public_key_credential_params)
- : client_data_hash_(std::move(client_data_hash)),
+ : client_data_hash_(fido_parsing_utils::Materialize(client_data_hash)),
rp_(std::move(rp)),
user_(std::move(user)),
public_key_credential_params_(std::move(public_key_credential_params)) {}
@@ -118,4 +151,119 @@ CtapMakeCredentialRequest& CtapMakeCredentialRequest::SetPinProtocol(
return *this;
}
+CtapMakeCredentialRequest&
+CtapMakeCredentialRequest::SetIsIndividualAttestation(
+ bool is_individual_attestation) {
+ is_individual_attestation_ = is_individual_attestation;
+ return *this;
+}
+
+base::Optional<CtapMakeCredentialRequest> ParseCtapMakeCredentialRequest(
+ base::span<const uint8_t> request_bytes) {
+ const auto& cbor_request = cbor::CBORReader::Read(request_bytes);
+ if (!cbor_request || !cbor_request->is_map())
+ return base::nullopt;
+
+ const auto& request_map = cbor_request->GetMap();
+ if (!AreMakeCredentialRequestMapKeysCorrect(request_map))
+ return base::nullopt;
+
+ const auto client_data_hash_it = request_map.find(cbor::CBORValue(1));
+ if (client_data_hash_it == request_map.end() ||
+ !client_data_hash_it->second.is_bytestring())
+ return base::nullopt;
+
+ const auto client_data_hash =
+ base::make_span(client_data_hash_it->second.GetBytestring())
+ .subspan<0, kClientDataHashLength>();
+
+ const auto rp_entity_it = request_map.find(cbor::CBORValue(2));
+ if (rp_entity_it == request_map.end() || !rp_entity_it->second.is_map())
+ return base::nullopt;
+
+ auto rp_entity =
+ PublicKeyCredentialRpEntity::CreateFromCBORValue(rp_entity_it->second);
+ if (!rp_entity)
+ return base::nullopt;
+
+ const auto user_entity_it = request_map.find(cbor::CBORValue(3));
+ if (user_entity_it == request_map.end() || !user_entity_it->second.is_map())
+ return base::nullopt;
+
+ auto user_entity = PublicKeyCredentialUserEntity::CreateFromCBORValue(
+ user_entity_it->second);
+ if (!user_entity)
+ return base::nullopt;
+
+ const auto credential_params_it = request_map.find(cbor::CBORValue(4));
+ if (credential_params_it == request_map.end())
+ return base::nullopt;
+
+ auto credential_params = PublicKeyCredentialParams::CreateFromCBORValue(
+ credential_params_it->second);
+ if (!credential_params)
+ return base::nullopt;
+
+ CtapMakeCredentialRequest request(client_data_hash, std::move(*rp_entity),
+ std::move(*user_entity),
+ std::move(*credential_params));
+
+ const auto exclude_list_it = request_map.find(cbor::CBORValue(5));
+ if (exclude_list_it != request_map.end()) {
+ if (!exclude_list_it->second.is_array())
+ return base::nullopt;
+
+ const auto& credential_descriptors = exclude_list_it->second.GetArray();
+ std::vector<PublicKeyCredentialDescriptor> exclude_list;
+ for (const auto& credential_descriptor : credential_descriptors) {
+ auto excluded_credential =
+ PublicKeyCredentialDescriptor::CreateFromCBORValue(
+ credential_descriptor);
+ if (!excluded_credential)
+ return base::nullopt;
+
+ exclude_list.push_back(std::move(*excluded_credential));
+ }
+ request.SetExcludeList(std::move(exclude_list));
+ }
+
+ const auto option_it = request_map.find(cbor::CBORValue(7));
+ if (option_it != request_map.end()) {
+ if (!option_it->second.is_map())
+ return base::nullopt;
+
+ const auto& option_map = option_it->second.GetMap();
+ if (!IsMakeCredentialOptionMapFormatCorrect(option_map))
+ return base::nullopt;
+
+ const auto resident_key_option =
+ option_map.find(cbor::CBORValue(kResidentKeyMapKey));
+ if (resident_key_option != option_map.end())
+ request.SetResidentKeySupported(resident_key_option->second.GetBool());
+
+ const auto uv_option =
+ option_map.find(cbor::CBORValue(kUserVerificationMapKey));
+ if (uv_option != option_map.end())
+ request.SetUserVerificationRequired(uv_option->second.GetBool());
+ }
+
+ const auto pin_auth_it = request_map.find(cbor::CBORValue(8));
+ if (pin_auth_it != request_map.end()) {
+ if (!pin_auth_it->second.is_bytestring())
+ return base::nullopt;
+ request.SetPinAuth(pin_auth_it->second.GetBytestring());
+ }
+
+ const auto pin_protocol_it = request_map.find(cbor::CBORValue(9));
+ if (pin_protocol_it != request_map.end()) {
+ if (!pin_protocol_it->second.is_unsigned() ||
+ pin_protocol_it->second.GetUnsigned() >
+ std::numeric_limits<uint8_t>::max())
+ return base::nullopt;
+ request.SetPinProtocol(pin_auth_it->second.GetUnsigned());
+ }
+
+ return request;
+}
+
} // namespace device
diff --git a/chromium/device/fido/ctap_make_credential_request.h b/chromium/device/fido/ctap_make_credential_request.h
index 2e47e855ef5..3b434915529 100644
--- a/chromium/device/fido/ctap_make_credential_request.h
+++ b/chromium/device/fido/ctap_make_credential_request.h
@@ -7,10 +7,12 @@
#include <stdint.h>
+#include <array>
#include <string>
#include <vector>
#include "base/component_export.h"
+#include "base/containers/span.h"
#include "base/macros.h"
#include "base/optional.h"
#include "device/fido/public_key_credential_descriptor.h"
@@ -26,7 +28,7 @@ namespace device {
class COMPONENT_EXPORT(DEVICE_FIDO) CtapMakeCredentialRequest {
public:
CtapMakeCredentialRequest(
- std::vector<uint8_t> client_data_hash,
+ base::span<const uint8_t, kClientDataHashLength> client_data_hash,
PublicKeyCredentialRpEntity rp,
PublicKeyCredentialUserEntity user,
PublicKeyCredentialParams public_key_credential_params);
@@ -48,8 +50,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapMakeCredentialRequest {
std::vector<PublicKeyCredentialDescriptor> exclude_list);
CtapMakeCredentialRequest& SetPinAuth(std::vector<uint8_t> pin_auth);
CtapMakeCredentialRequest& SetPinProtocol(uint8_t pin_protocol);
+ CtapMakeCredentialRequest& SetIsIndividualAttestation(
+ bool is_individual_attestation);
- const std::vector<uint8_t>& client_data_hash() const {
+ const std::array<uint8_t, kClientDataHashLength>& client_data_hash() const {
return client_data_hash_;
}
const PublicKeyCredentialRpEntity& rp() const { return rp_; }
@@ -61,24 +65,33 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapMakeCredentialRequest {
return user_verification_required_;
}
bool resident_key_supported() const { return resident_key_supported_; }
+ bool is_individual_attestation() const { return is_individual_attestation_; }
const base::Optional<std::vector<PublicKeyCredentialDescriptor>>&
exclude_list() const {
return exclude_list_;
}
+ const base::Optional<std::vector<uint8_t>>& pin_auth() const {
+ return pin_auth_;
+ }
private:
- std::vector<uint8_t> client_data_hash_;
+ std::array<uint8_t, kClientDataHashLength> client_data_hash_;
PublicKeyCredentialRpEntity rp_;
PublicKeyCredentialUserEntity user_;
PublicKeyCredentialParams public_key_credential_params_;
bool user_verification_required_ = false;
bool resident_key_supported_ = false;
+ bool is_individual_attestation_ = false;
base::Optional<std::vector<PublicKeyCredentialDescriptor>> exclude_list_;
base::Optional<std::vector<uint8_t>> pin_auth_;
base::Optional<uint8_t> pin_protocol_;
};
+COMPONENT_EXPORT(DEVICE_FIDO)
+base::Optional<CtapMakeCredentialRequest> ParseCtapMakeCredentialRequest(
+ base::span<const uint8_t> request_bytes);
+
} // namespace device
#endif // DEVICE_FIDO_CTAP_MAKE_CREDENTIAL_REQUEST_H_
diff --git a/chromium/device/fido/ctap_register_operation.cc b/chromium/device/fido/ctap_register_operation.cc
deleted file mode 100644
index c2776cffd6f..00000000000
--- a/chromium/device/fido/ctap_register_operation.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-// 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/ctap_register_operation.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "device/fido/authenticator_make_credential_response.h"
-#include "device/fido/ctap_make_credential_request.h"
-#include "device/fido/device_response_converter.h"
-#include "device/fido/fido_device.h"
-
-namespace device {
-
-CtapRegisterOperation::CtapRegisterOperation(
- FidoDevice* device,
- const CtapMakeCredentialRequest* request,
- DeviceResponseCallback callback)
- : DeviceOperation(device, std::move(callback)),
- request_(request),
- weak_factory_(this) {}
-
-CtapRegisterOperation::~CtapRegisterOperation() = default;
-
-void CtapRegisterOperation::Start() {
- device_->DeviceTransact(
- request_->EncodeAsCBOR(),
- base::BindOnce(&CtapRegisterOperation::OnResponseReceived,
- weak_factory_.GetWeakPtr()));
-}
-
-void CtapRegisterOperation::OnResponseReceived(
- base::Optional<std::vector<uint8_t>> device_response) {
- if (!device_response) {
- std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
- base::nullopt);
- return;
- }
-
- std::move(callback_).Run(GetResponseCode(*device_response),
- ReadCTAPMakeCredentialResponse(*device_response));
-}
-
-} // namespace device
diff --git a/chromium/device/fido/ctap_register_operation.h b/chromium/device/fido/ctap_register_operation.h
deleted file mode 100644
index b188d18b5e2..00000000000
--- a/chromium/device/fido/ctap_register_operation.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// 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_CTAP_REGISTER_OPERATION_H_
-#define DEVICE_FIDO_CTAP_REGISTER_OPERATION_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/component_export.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/optional.h"
-#include "device/fido/device_operation.h"
-#include "device/fido/fido_constants.h"
-
-namespace device {
-
-class FidoDevice;
-class CtapMakeCredentialRequest;
-class AuthenticatorMakeCredentialResponse;
-
-// Represents per device registration logic for CTAP device.
-// CtapRegisterOperation is owned by MakeCredentialTask, and the lifetime of
-// CtapRegisterOperation does not exceed that of MakeCredentialTask. As so,
-// |request_| member variable is dependency injected from MakeCredentialTask.
-class COMPONENT_EXPORT(DEVICE_FIDO) CtapRegisterOperation
- : public DeviceOperation<CtapMakeCredentialRequest,
- AuthenticatorMakeCredentialResponse> {
- public:
- CtapRegisterOperation(FidoDevice* device,
- const CtapMakeCredentialRequest* request,
- DeviceResponseCallback callback);
-
- ~CtapRegisterOperation() override;
-
- // DeviceOperation:
- void Start() override;
-
- private:
- void OnResponseReceived(base::Optional<std::vector<uint8_t>> device_response);
-
- const CtapMakeCredentialRequest* const request_;
-
- base::WeakPtrFactory<CtapRegisterOperation> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(CtapRegisterOperation);
-};
-
-} // namespace device
-
-#endif // DEVICE_FIDO_CTAP_REGISTER_OPERATION_H_
diff --git a/chromium/device/fido/ctap_request_unittest.cc b/chromium/device/fido/ctap_request_unittest.cc
index 725d0514ff7..30850b1a96f 100644
--- a/chromium/device/fido/ctap_request_unittest.cc
+++ b/chromium/device/fido/ctap_request_unittest.cc
@@ -16,88 +16,6 @@ namespace device {
// Leveraging example 4 of section 6.1 of the spec
// https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html
TEST(CTAPRequestTest, TestConstructMakeCredentialRequestParam) {
- static constexpr uint8_t kSerializedRequest[] = {
- // clang-format off
- 0x01, // authenticatorMakeCredential command
- 0xa5, // map(5)
- 0x01, // clientDataHash
- 0x58, 0x20, // bytes(32)
- 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,
-
- 0x02, // unsigned(2) - rp
- 0xa2, // map(2)
- 0x62, // text(2)
- 0x69, 0x64, // "id"
- 0x68, // text(8)
- // "acme.com"
- 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
- 0x64, // text(4)
- 0x6e, 0x61, 0x6d, 0x65, // "name"
- 0x64, // text(4)
- 0x41, 0x63, 0x6d, 0x65, // "Acme"
-
- 0x03, // unsigned(3) - user
- 0xa4, // map(4)
- 0x62, // text(2)
- 0x69, 0x64, // "id"
- 0x48, // bytes(8) - user id
- 0x10, 0x98, 0x23, 0x72, 0x35, 0x40, 0x98, 0x72,
- 0x64, // text(4)
- 0x69, 0x63, 0x6f, 0x6e, // "icon"
- 0x78, 0x28, // text(40)
- // "https://pics.acme.com/00/p/aBjjjpqPb.png"
- 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x70, 0x69, 0x63, 0x73,
- 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x30,
- 0x2f, 0x70, 0x2f, 0x61, 0x42, 0x6a, 0x6a, 0x6a, 0x70, 0x71, 0x50, 0x62,
- 0x2e, 0x70, 0x6e, 0x67,
- 0x64, // text(4)
- 0x6e, 0x61, 0x6d, 0x65, // "name"
- 0x76, // text(22)
- // "johnpsmith@example.com"
- 0x6a, 0x6f, 0x68, 0x6e, 0x70, 0x73, 0x6d, 0x69, 0x74, 0x68, 0x40, 0x65,
- 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
- 0x6b, // text(11)
- // "displayName"
- 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65,
- 0x6d, // text(13)
- // "John P. Smith"
- 0x4a, 0x6f, 0x68, 0x6e, 0x20, 0x50, 0x2e, 0x20, 0x53, 0x6d, 0x69, 0x74,
- 0x68,
-
- 0x04, // unsigned(4) - pubKeyCredParams
- 0x82, // array(2)
- 0xa2, // map(2)
- 0x63, // text(3)
- 0x61, 0x6c, 0x67, // "alg"
- 0x07, // 7
- 0x64, // text(4)
- 0x74, 0x79, 0x70, 0x65, // "type"
- 0x6a, // text(10)
- // "public-key"
- 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79,
- 0xa2, // map(2)
- 0x63, // text(3)
- 0x61, 0x6c, 0x67, // "alg"
- 0x19, 0x01, 0x01, // 257
- 0x64, // text(4)
- 0x74, 0x79, 0x70, 0x65, // "type"
- 0x6a, // text(10)
- // "public-key"
- 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79,
-
- 0x07, // unsigned(7) - options
- 0xa2, // map(2)
- 0x62, // text(2)
- 0x72, 0x6b, // "rk"
- 0xf5, // True(21)
- 0x62, // text(2)
- 0x75, 0x76, // "uv"
- 0xf5 // True(21)
- // clang-format on
- };
-
PublicKeyCredentialRpEntity rp("acme.com");
rp.SetRpName("Acme");
@@ -108,82 +26,19 @@ TEST(CTAPRequestTest, TestConstructMakeCredentialRequestParam) {
.SetIconUrl(GURL("https://pics.acme.com/00/p/aBjjjpqPb.png"));
CtapMakeCredentialRequest make_credential_param(
- fido_parsing_utils::Materialize(test_data::kClientDataHash),
- std::move(rp), std::move(user),
+ test_data::kClientDataHash, std::move(rp), std::move(user),
PublicKeyCredentialParams({{CredentialType::kPublicKey, 7},
{CredentialType::kPublicKey, 257}}));
auto serialized_data = make_credential_param.SetResidentKeySupported(true)
.SetUserVerificationRequired(true)
.EncodeAsCBOR();
- EXPECT_THAT(serialized_data, ::testing::ElementsAreArray(kSerializedRequest));
+ EXPECT_THAT(serialized_data, ::testing::ElementsAreArray(
+ test_data::kCtapMakeCredentialRequest));
}
TEST(CTAPRequestTest, TestConstructGetAssertionRequest) {
- static constexpr uint8_t kSerializedRequest[] = {
- // clang-format off
- 0x02, // authenticatorGetAssertion command
- 0xa4, // map(4)
-
- 0x01, // rpId
- 0x68, // text(8)
- // "acme.com"
- 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
-
- 0x02, // unsigned(2) - client data hash
- 0x58, 0x20, // bytes(32)
- 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,
-
- 0x03, // unsigned(3) - allow list
- 0x82, // array(2)
- 0xa2, // map(2)
- 0x62, // text(2)
- 0x69, 0x64, // "id"
- 0x58, 0x40,
- // credential ID
- 0xf2, 0x20, 0x06, 0xde, 0x4f, 0x90, 0x5a, 0xf6, 0x8a, 0x43, 0x94, 0x2f,
- 0x02, 0x4f, 0x2a, 0x5e, 0xce, 0x60, 0x3d, 0x9c, 0x6d, 0x4b, 0x3d, 0xf8,
- 0xbe, 0x08, 0xed, 0x01, 0xfc, 0x44, 0x26, 0x46, 0xd0, 0x34, 0x85, 0x8a,
- 0xc7, 0x5b, 0xed, 0x3f, 0xd5, 0x80, 0xbf, 0x98, 0x08, 0xd9, 0x4f, 0xcb,
- 0xee, 0x82, 0xb9, 0xb2, 0xef, 0x66, 0x77, 0xaf, 0x0a, 0xdc, 0xc3, 0x58,
- 0x52, 0xea, 0x6b, 0x9e,
-
- 0x64, // text(4)
- 0x74, 0x79, 0x70, 0x65, // "type"
- 0x6a, // text(10)
- // "public-key"
- 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79,
- 0xa2, // map(2)
- 0x62, // text(2)
- 0x69, 0x64, // "id"
- 0x58, 0x32, // text(22)
- // credential ID
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03,
- 0x64, // text(4)
- 0x74, 0x79, 0x70, 0x65, // "type"
- 0x6a, // text(10)
- // "public-key"
- 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79,
-
- 0x05, // unsigned(5) - options
- 0xa2, // map(2)
- 0x62, // text(2)
- 0x75, 0x70, // "up"
- 0xf4, // False(20)
- 0x62, // text(2)
- 0x75, 0x76, // "uv"
- 0xf5 // True(21)
-
- // clang-format on
- };
-
- CtapGetAssertionRequest get_assertion_req(
- "acme.com", fido_parsing_utils::Materialize(test_data::kClientDataHash));
+ CtapGetAssertionRequest get_assertion_req("acme.com",
+ test_data::kClientDataHash);
std::vector<PublicKeyCredentialDescriptor> allowed_list;
allowed_list.push_back(PublicKeyCredentialDescriptor(
@@ -207,7 +62,8 @@ TEST(CTAPRequestTest, TestConstructGetAssertionRequest) {
.SetUserVerification(UserVerificationRequirement::kRequired);
auto serialized_data = get_assertion_req.EncodeAsCBOR();
- EXPECT_THAT(serialized_data, ::testing::ElementsAreArray(kSerializedRequest));
+ EXPECT_THAT(serialized_data,
+ ::testing::ElementsAreArray(test_data::kCtapGetAssertionRequest));
}
TEST(CTAPRequestTest, TestConstructCtapAuthenticatorRequestParam) {
@@ -223,4 +79,68 @@ TEST(CTAPRequestTest, TestConstructCtapAuthenticatorRequestParam) {
::testing::ElementsAre(kSerializedResetCmd));
}
+TEST(CTAPRequestTest, ParseMakeCredentialRequestForVirtualCtapKey) {
+ const auto request = ParseCtapMakeCredentialRequest(
+ base::make_span(test_data::kCtapMakeCredentialRequest).subspan(1));
+ ASSERT_TRUE(request);
+ EXPECT_THAT(request->client_data_hash(),
+ ::testing::ElementsAreArray(test_data::kClientDataHash));
+ EXPECT_EQ(test_data::kRelyingPartyId, request->rp().rp_id());
+ EXPECT_EQ("Acme", request->rp().rp_name());
+ EXPECT_THAT(request->user().user_id(),
+ ::testing::ElementsAreArray(test_data::kUserId));
+ ASSERT_TRUE(request->user().user_name());
+ EXPECT_EQ("johnpsmith@example.com", *request->user().user_name());
+ ASSERT_TRUE(request->user().user_display_name());
+ EXPECT_EQ("John P. Smith", *request->user().user_display_name());
+ ASSERT_TRUE(request->user().user_icon_url());
+ EXPECT_EQ("https://pics.acme.com/00/p/aBjjjpqPb.png",
+ request->user().user_icon_url()->spec());
+ ASSERT_EQ(2u, request->public_key_credential_params()
+ .public_key_credential_params()
+ .size());
+ EXPECT_EQ(7, request->public_key_credential_params()
+ .public_key_credential_params()
+ .at(0)
+ .algorithm);
+ EXPECT_EQ(257, request->public_key_credential_params()
+ .public_key_credential_params()
+ .at(1)
+ .algorithm);
+ EXPECT_TRUE(request->user_verification_required());
+ EXPECT_TRUE(request->resident_key_supported());
+}
+
+TEST(CTAPRequestTest, ParseGetAssertionRequestForVirtualCtapKey) {
+ constexpr uint8_t kAllowedCredentialOne[] = {
+ 0xf2, 0x20, 0x06, 0xde, 0x4f, 0x90, 0x5a, 0xf6, 0x8a, 0x43, 0x94,
+ 0x2f, 0x02, 0x4f, 0x2a, 0x5e, 0xce, 0x60, 0x3d, 0x9c, 0x6d, 0x4b,
+ 0x3d, 0xf8, 0xbe, 0x08, 0xed, 0x01, 0xfc, 0x44, 0x26, 0x46, 0xd0,
+ 0x34, 0x85, 0x8a, 0xc7, 0x5b, 0xed, 0x3f, 0xd5, 0x80, 0xbf, 0x98,
+ 0x08, 0xd9, 0x4f, 0xcb, 0xee, 0x82, 0xb9, 0xb2, 0xef, 0x66, 0x77,
+ 0xaf, 0x0a, 0xdc, 0xc3, 0x58, 0x52, 0xea, 0x6b, 0x9e};
+ constexpr uint8_t kAllowedCredentialTwo[] = {
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03};
+
+ const auto request = ParseCtapGetAssertionRequest(
+ base::make_span(test_data::kCtapGetAssertionRequest).subspan(1));
+ ASSERT_TRUE(request);
+ EXPECT_THAT(request->client_data_hash(),
+ ::testing::ElementsAreArray(test_data::kClientDataHash));
+ EXPECT_EQ(test_data::kRelyingPartyId, request->rp_id());
+ EXPECT_EQ(UserVerificationRequirement::kRequired,
+ request->user_verification());
+ EXPECT_FALSE(request->user_presence_required());
+ ASSERT_TRUE(request->allow_list());
+ ASSERT_EQ(2u, request->allow_list()->size());
+
+ EXPECT_THAT(request->allow_list()->at(0).id(),
+ ::testing::ElementsAreArray(kAllowedCredentialOne));
+ EXPECT_THAT(request->allow_list()->at(1).id(),
+ ::testing::ElementsAreArray(kAllowedCredentialTwo));
+}
} // namespace device
diff --git a/chromium/device/fido/ctap_response_fuzzer.cc b/chromium/device/fido/ctap_response_fuzzer.cc
index 989c7328d63..e89235fe744 100644
--- a/chromium/device/fido/ctap_response_fuzzer.cc
+++ b/chromium/device/fido/ctap_response_fuzzer.cc
@@ -30,7 +30,7 @@ IcuEnvironment* env = new IcuEnvironment();
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
std::vector<uint8_t> input(data, data + size);
- std::vector<uint8_t> relying_party_id_hash(32);
+ std::array<uint8_t, 32> relying_party_id_hash = {};
auto response = device::ReadCTAPMakeCredentialResponse(input);
if (response)
response->EraseAttestationStatement();
diff --git a/chromium/device/fido/ctap_response_unittest.cc b/chromium/device/fido/ctap_response_unittest.cc
index 45c44c3e9a3..7b20e1e30a2 100644
--- a/chromium/device/fido/ctap_response_unittest.cc
+++ b/chromium/device/fido/ctap_response_unittest.cc
@@ -5,14 +5,16 @@
#include "components/cbor/cbor_reader.h"
#include "components/cbor/cbor_values.h"
#include "components/cbor/cbor_writer.h"
+#include "device/fido/attestation_statement_formats.h"
#include "device/fido/authenticator_get_assertion_response.h"
#include "device/fido/authenticator_make_credential_response.h"
#include "device/fido/device_response_converter.h"
#include "device/fido/ec_public_key.h"
-#include "device/fido/fido_attestation_statement.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_parsing_utils.h"
#include "device/fido/fido_test_data.h"
+#include "device/fido/opaque_attestation_statement.h"
+#include "device/fido/opaque_public_key.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -20,6 +22,181 @@ namespace device {
namespace {
+constexpr uint8_t kTestAuthenticatorGetInfoResponseWithNoVersion[] = {
+ // Success status byte
+ 0x00,
+ // Map of 6 elements
+ 0xA6,
+ // Key(01) - versions
+ 0x01,
+ // Array(0)
+ 0x80,
+ // Key(02) - extensions
+ 0x02,
+ // Array(2)
+ 0x82,
+ // "uvm"
+ 0x63, 0x75, 0x76, 0x6D,
+ // "hmac-secret"
+ 0x6B, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74,
+ // Key(03) - AAGUID
+ 0x03,
+ // Bytes(16)
+ 0x50, 0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, 0x4D, 0x15, 0x80, 0x06, 0x17,
+ 0x11, 0x1F, 0x9E, 0xDC, 0x7D,
+ // Key(04) - options
+ 0x04,
+ // Map(05)
+ 0xA5,
+ // Key - "rk"
+ 0x62, 0x72, 0x6B,
+ // true
+ 0xF5,
+ // Key - "up"
+ 0x62, 0x75, 0x70,
+ // true
+ 0xF5,
+ // Key - "uv"
+ 0x62, 0x75, 0x76,
+ // true
+ 0xF5,
+ // Key - "plat"
+ 0x64, 0x70, 0x6C, 0x61, 0x74,
+ // true
+ 0xF5,
+ // Key - "clientPin"
+ 0x69, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x50, 0x69, 0x6E,
+ // false
+ 0xF4,
+ // Key(05) - Max message size
+ 0x05,
+ // 1200
+ 0x19, 0x04, 0xB0,
+ // Key(06) - Pin protocols
+ 0x06,
+ // Array[1]
+ 0x81, 0x01,
+};
+
+constexpr uint8_t kTestAuthenticatorGetInfoResponseWithDuplicateVersion[] = {
+ // Success status byte
+ 0x00,
+ // Map of 6 elements
+ 0xA6,
+ // Key(01) - versions
+ 0x01,
+ // Array(02)
+ 0x82,
+ // "U2F_V2"
+ 0x66, 0x55, 0x32, 0x46, 0x5F, 0x56, 0x32,
+ // "U2F_V2"
+ 0x66, 0x55, 0x32, 0x46, 0x5F, 0x56, 0x32,
+ // Key(02) - extensions
+ 0x02,
+ // Array(2)
+ 0x82,
+ // "uvm"
+ 0x63, 0x75, 0x76, 0x6D,
+ // "hmac-secret"
+ 0x6B, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74,
+ // Key(03) - AAGUID
+ 0x03,
+ // Bytes(16)
+ 0x50, 0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, 0x4D, 0x15, 0x80, 0x06, 0x17,
+ 0x11, 0x1F, 0x9E, 0xDC, 0x7D,
+ // Key(04) - options
+ 0x04,
+ // Map(05)
+ 0xA5,
+ // Key - "rk"
+ 0x62, 0x72, 0x6B,
+ // true
+ 0xF5,
+ // Key - "up"
+ 0x62, 0x75, 0x70,
+ // true
+ 0xF5,
+ // Key - "uv"
+ 0x62, 0x75, 0x76,
+ // true
+ 0xF5,
+ // Key - "plat"
+ 0x64, 0x70, 0x6C, 0x61, 0x74,
+ // true
+ 0xF5,
+ // Key - "clientPin"
+ 0x69, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x50, 0x69, 0x6E,
+ // false
+ 0xF4,
+ // Key(05) - Max message size
+ 0x05,
+ // 1200
+ 0x19, 0x04, 0xB0,
+ // Key(06) - Pin protocols
+ 0x06,
+ // Array[1]
+ 0x81, 0x01,
+};
+
+constexpr uint8_t kTestAuthenticatorGetInfoResponseWithIncorrectAaguid[] = {
+ // Success status byte
+ 0x00,
+ // Map of 6 elements
+ 0xA6,
+ // Key(01) - versions
+ 0x01,
+ // Array(01)
+ 0x81,
+ // "U2F_V2"
+ 0x66, 0x55, 0x32, 0x46, 0x5F, 0x56, 0x32,
+ // Key(02) - extensions
+ 0x02,
+ // Array(2)
+ 0x82,
+ // "uvm"
+ 0x63, 0x75, 0x76, 0x6D,
+ // "hmac-secret"
+ 0x6B, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74,
+ // Key(03) - AAGUID
+ 0x03,
+ // Bytes(17) - FIDO2 device AAGUID must be 16 bytes long in order to be
+ // correct.
+ 0x51, 0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, 0x4D, 0x15, 0x80, 0x06, 0x17,
+ 0x11, 0x1F, 0x9E, 0xDC, 0x7D, 0x00,
+ // Key(04) - options
+ 0x04,
+ // Map(05)
+ 0xA5,
+ // Key - "rk"
+ 0x62, 0x72, 0x6B,
+ // true
+ 0xF5,
+ // Key - "up"
+ 0x62, 0x75, 0x70,
+ // true
+ 0xF5,
+ // Key - "uv"
+ 0x62, 0x75, 0x76,
+ // true
+ 0xF5,
+ // Key - "plat"
+ 0x64, 0x70, 0x6C, 0x61, 0x74,
+ // true
+ 0xF5,
+ // Key - "clientPin"
+ 0x69, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x50, 0x69, 0x6E,
+ // false
+ 0xF4,
+ // Key(05) - Max message size
+ 0x05,
+ // 1200
+ 0x19, 0x04, 0xB0,
+ // Key(06) - Pin protocols
+ 0x06,
+ // Array[1]
+ 0x81, 0x01,
+};
+
// The attested credential data, excluding the public key bytes. Append
// with kTestECPublicKeyCOSE to get the complete attestation data.
constexpr uint8_t kTestAttestedCredentialDataPrefix[] = {
@@ -80,6 +257,10 @@ constexpr uint8_t kAuthDataCBOR[] = {
// and test_data::kTestECPublicKeyCOSE.
0x58, 0xC4};
+constexpr std::array<uint8_t, kAaguidLength> kTestDeviceAaguid = {
+ {0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, 0x4D, 0x15, 0x80, 0x06, 0x17, 0x11,
+ 0x1F, 0x9E, 0xDC, 0x7D}};
+
std::vector<uint8_t> GetTestAttestedCredentialDataBytes() {
// Combine kTestAttestedCredentialDataPrefix and kTestECPublicKeyCOSE.
auto test_attested_data =
@@ -115,10 +296,6 @@ std::vector<uint8_t> GetTestSignResponse() {
return fido_parsing_utils::Materialize(test_data::kTestU2fSignResponse);
}
-std::vector<uint8_t> GetTestSignatureCounter() {
- return fido_parsing_utils::Materialize(test_data::kTestSignatureCounter);
-}
-
// Get a subset of the response for testing error handling.
std::vector<uint8_t> GetTestCorruptedSignResponse(size_t length) {
DCHECK_LE(length, arraysize(test_data::kTestU2fSignResponse));
@@ -138,7 +315,7 @@ std::vector<uint8_t> GetTestCredentialRawIdBytes() {
// 20170927.html
TEST(CTAPResponseTest, TestReadMakeCredentialResponse) {
auto make_credential_response =
- ReadCTAPMakeCredentialResponse(test_data::kDeviceMakeCredentialResponse);
+ ReadCTAPMakeCredentialResponse(test_data::kTestMakeCredentialResponse);
ASSERT_TRUE(make_credential_response);
auto cbor_attestation_object = cbor::CBORReader::Read(
make_credential_response->GetCBOREncodedAttestationObject());
@@ -166,8 +343,8 @@ TEST(CTAPResponseTest, TestReadMakeCredentialResponse) {
auto attStmt_it = attestation_statement_map.find(cbor::CBORValue("alg"));
ASSERT_TRUE(attStmt_it != attestation_statement_map.end());
- ASSERT_TRUE(attStmt_it->second.is_unsigned());
- EXPECT_EQ(attStmt_it->second.GetUnsigned(), 7u);
+ ASSERT_TRUE(attStmt_it->second.is_integer());
+ EXPECT_EQ(attStmt_it->second.GetInteger(), -7);
attStmt_it = attestation_statement_map.find(cbor::CBORValue("sig"));
ASSERT_TRUE(attStmt_it != attestation_statement_map.end());
@@ -192,7 +369,7 @@ TEST(CTAPResponseTest, TestReadMakeCredentialResponse) {
TEST(CTAPResponseTest, TestMakeCredentialNoneAttestationResponse) {
auto make_credential_response =
- ReadCTAPMakeCredentialResponse(test_data::kDeviceMakeCredentialResponse);
+ ReadCTAPMakeCredentialResponse(test_data::kTestMakeCredentialResponse);
ASSERT_TRUE(make_credential_response);
make_credential_response->EraseAttestationStatement();
EXPECT_THAT(make_credential_response->GetCBOREncodedAttestationObject(),
@@ -220,7 +397,7 @@ TEST(CTAPResponseTest, TestReadGetAssertionResponse) {
TEST(CTAPResponseTest, TestParseRegisterResponseData) {
auto response =
AuthenticatorMakeCredentialResponse::CreateFromU2fRegisterResponse(
- fido_parsing_utils::Materialize(test_data::kApplicationParameter),
+ test_data::kApplicationParameter,
test_data::kTestU2fRegisterResponse);
ASSERT_TRUE(response);
EXPECT_THAT(response->raw_credential_id(),
@@ -274,9 +451,9 @@ TEST(CTAPResponseTest, TestSerializeAuthenticatorData) {
static_cast<uint8_t>(AuthenticatorData::Flag::kTestOfUserPresence) |
static_cast<uint8_t>(AuthenticatorData::Flag::kAttestation);
- AuthenticatorData authenticator_data(
- fido_parsing_utils::Materialize(test_data::kApplicationParameter), flags,
- std::vector<uint8_t>(4) /* counter */, std::move(attested_data));
+ AuthenticatorData authenticator_data(test_data::kApplicationParameter, flags,
+ std::array<uint8_t, 4>{} /* counter */,
+ std::move(attested_data));
EXPECT_EQ(GetTestAuthenticatorDataBytes(),
authenticator_data.SerializeToByteArray());
@@ -292,9 +469,9 @@ TEST(CTAPResponseTest, TestSerializeU2fAttestationObject) {
constexpr uint8_t flags =
static_cast<uint8_t>(AuthenticatorData::Flag::kTestOfUserPresence) |
static_cast<uint8_t>(AuthenticatorData::Flag::kAttestation);
- AuthenticatorData authenticator_data(
- fido_parsing_utils::Materialize(test_data::kApplicationParameter), flags,
- std::vector<uint8_t>(4) /* counter */, std::move(attested_data));
+ AuthenticatorData authenticator_data(test_data::kApplicationParameter, flags,
+ std::array<uint8_t, 4>{} /* counter */,
+ std::move(attested_data));
// Construct the attestation statement.
auto fido_attestation_statement =
@@ -316,17 +493,16 @@ TEST(CTAPResponseTest, TestSerializeAuthenticatorDataForSign) {
static_cast<uint8_t>(AuthenticatorData::Flag::kTestOfUserPresence);
EXPECT_THAT(
- AuthenticatorData(
- fido_parsing_utils::Materialize(test_data::kApplicationParameter),
- flags, GetTestSignatureCounter(), base::nullopt)
+ AuthenticatorData(test_data::kApplicationParameter, flags,
+ test_data::kTestSignatureCounter, base::nullopt)
.SerializeToByteArray(),
::testing::ElementsAreArray(test_data::kTestSignAuthenticatorData));
}
TEST(CTAPResponseTest, TestParseSignResponseData) {
auto response = AuthenticatorGetAssertionResponse::CreateFromU2fSignResponse(
- fido_parsing_utils::Materialize(test_data::kApplicationParameter),
- GetTestSignResponse(), GetTestCredentialRawIdBytes());
+ test_data::kApplicationParameter, GetTestSignResponse(),
+ GetTestCredentialRawIdBytes());
ASSERT_TRUE(response);
EXPECT_EQ(GetTestCredentialRawIdBytes(), response->raw_credential_id());
EXPECT_THAT(
@@ -338,32 +514,183 @@ TEST(CTAPResponseTest, TestParseSignResponseData) {
TEST(CTAPResponseTest, TestParseU2fSignWithNullNullKeyHandle) {
auto response = AuthenticatorGetAssertionResponse::CreateFromU2fSignResponse(
- fido_parsing_utils::Materialize(test_data::kApplicationParameter),
- GetTestSignResponse(), std::vector<uint8_t>());
+ test_data::kApplicationParameter, GetTestSignResponse(),
+ std::vector<uint8_t>());
EXPECT_FALSE(response);
}
TEST(CTAPResponseTest, TestParseU2fSignWithNullResponse) {
auto response = AuthenticatorGetAssertionResponse::CreateFromU2fSignResponse(
- fido_parsing_utils::Materialize(test_data::kApplicationParameter),
- std::vector<uint8_t>(), GetTestCredentialRawIdBytes());
+ test_data::kApplicationParameter, std::vector<uint8_t>(),
+ GetTestCredentialRawIdBytes());
EXPECT_FALSE(response);
}
TEST(CTAPResponseTest, TestParseU2fSignWithNullCorruptedCounter) {
// A sign response of less than 5 bytes.
auto response = AuthenticatorGetAssertionResponse::CreateFromU2fSignResponse(
- fido_parsing_utils::Materialize(test_data::kApplicationParameter),
- GetTestCorruptedSignResponse(3), GetTestCredentialRawIdBytes());
+ test_data::kApplicationParameter, GetTestCorruptedSignResponse(3),
+ GetTestCredentialRawIdBytes());
EXPECT_FALSE(response);
}
TEST(CTAPResponseTest, TestParseU2fSignWithNullCorruptedSignature) {
// A sign response no more than 5 bytes.
auto response = AuthenticatorGetAssertionResponse::CreateFromU2fSignResponse(
- fido_parsing_utils::Materialize(test_data::kApplicationParameter),
- GetTestCorruptedSignResponse(5), GetTestCredentialRawIdBytes());
+ test_data::kApplicationParameter, GetTestCorruptedSignResponse(5),
+ GetTestCredentialRawIdBytes());
EXPECT_FALSE(response);
}
+TEST(CTAPResponseTest, TestReadGetInfoResponse) {
+ auto get_info_response =
+ ReadCTAPGetInfoResponse(test_data::kTestAuthenticatorGetInfoResponse);
+ ASSERT_TRUE(get_info_response);
+ ASSERT_TRUE(get_info_response->max_msg_size());
+ EXPECT_EQ(*get_info_response->max_msg_size(), 1200u);
+ EXPECT_TRUE(
+ base::ContainsKey(get_info_response->versions(), ProtocolVersion::kCtap));
+ EXPECT_TRUE(
+ base::ContainsKey(get_info_response->versions(), ProtocolVersion::kU2f));
+ EXPECT_TRUE(get_info_response->options().is_platform_device());
+ EXPECT_TRUE(get_info_response->options().supports_resident_key());
+ EXPECT_TRUE(get_info_response->options().user_presence_required());
+ EXPECT_EQ(AuthenticatorSupportedOptions::UserVerificationAvailability::
+ kSupportedAndConfigured,
+ get_info_response->options().user_verification_availability());
+ EXPECT_EQ(AuthenticatorSupportedOptions::ClientPinAvailability::
+ kSupportedButPinNotSet,
+ get_info_response->options().client_pin_availability());
+}
+
+TEST(CTAPResponseTest, TestReadGetInfoResponseWithIncorrectFormat) {
+ EXPECT_FALSE(
+ ReadCTAPGetInfoResponse(kTestAuthenticatorGetInfoResponseWithNoVersion));
+ EXPECT_FALSE(ReadCTAPGetInfoResponse(
+ kTestAuthenticatorGetInfoResponseWithDuplicateVersion));
+ EXPECT_FALSE(ReadCTAPGetInfoResponse(
+ kTestAuthenticatorGetInfoResponseWithIncorrectAaguid));
+}
+
+TEST(CTAPResponseTest, TestSerializeGetInfoResponse) {
+ AuthenticatorGetInfoResponse response(
+ {ProtocolVersion::kCtap, ProtocolVersion::kU2f},
+ fido_parsing_utils::Materialize(kTestDeviceAaguid));
+ response.SetExtensions({"uvm", "hmac-secret"});
+ AuthenticatorSupportedOptions options;
+ options.SetSupportsResidentKey(true);
+ options.SetIsPlatformDevice(true);
+ options.SetClientPinAvailability(
+ AuthenticatorSupportedOptions::ClientPinAvailability::
+ kSupportedButPinNotSet);
+ options.SetUserVerificationAvailability(
+ AuthenticatorSupportedOptions::UserVerificationAvailability::
+ kSupportedAndConfigured);
+ response.SetOptions(std::move(options));
+ response.SetMaxMsgSize(1200);
+ response.SetPinProtocols({1});
+
+ EXPECT_THAT(EncodeToCBOR(response),
+ ::testing::ElementsAreArray(
+ base::make_span(test_data::kTestAuthenticatorGetInfoResponse)
+ .subspan(1)));
+}
+
+TEST(CTAPResponseTest, TestSerializeMakeCredentialResponse) {
+ constexpr uint8_t kCoseEncodedPublicKey[] = {
+ 0xa3, 0x63, 0x61, 0x6c, 0x67, 0x65, 0x45, 0x53, 0x32, 0x35, 0x36, 0x61,
+ 0x78, 0x58, 0x20, 0xf7, 0xc4, 0xf4, 0xa6, 0xf1, 0xd7, 0x95, 0x38, 0xdf,
+ 0xa4, 0xc9, 0xac, 0x50, 0x84, 0x8d, 0xf7, 0x08, 0xbc, 0x1c, 0x99, 0xf5,
+ 0xe6, 0x0e, 0x51, 0xb4, 0x2a, 0x52, 0x1b, 0x35, 0xd3, 0xb6, 0x9a, 0x61,
+ 0x79, 0x58, 0x20, 0xde, 0x7b, 0x7d, 0x6c, 0xa5, 0x64, 0xe7, 0x0e, 0xa3,
+ 0x21, 0xa4, 0xd5, 0xd9, 0x6e, 0xa0, 0x0e, 0xf0, 0xe2, 0xdb, 0x89, 0xdd,
+ 0x61, 0xd4, 0x89, 0x4c, 0x15, 0xac, 0x58, 0x5b, 0xd2, 0x36, 0x84,
+ };
+
+ const auto application_parameter =
+ base::make_span(test_data::kApplicationParameter)
+ .subspan<0, kRpIdHashLength>();
+ // Starting signature counter value set by example 4 of the CTAP spec. The
+ // signature counter can start at any value but it should never decrease.
+ // https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html
+ std::array<uint8_t, kSignCounterLength> signature_counter = {
+ {0x00, 0x00, 0x00, 0x0b}};
+ auto flag =
+ base::strict_cast<uint8_t>(AuthenticatorData::Flag::kTestOfUserPresence) |
+ base::strict_cast<uint8_t>(AuthenticatorData::Flag::kAttestation);
+ AttestedCredentialData attested_credential_data(
+ kTestDeviceAaguid,
+ std::array<uint8_t, kCredentialIdLengthLength>{
+ {0x00, 0x10}} /* credential_id_length */,
+ fido_parsing_utils::Materialize(
+ test_data::kCtap2MakeCredentialCredentialId),
+ std::make_unique<OpaquePublicKey>(kCoseEncodedPublicKey));
+ AuthenticatorData authenticator_data(application_parameter, flag,
+ signature_counter,
+ std::move(attested_credential_data));
+
+ cbor::CBORValue::MapValue attestation_map;
+ attestation_map.emplace("alg", -7);
+ attestation_map.emplace("sig", fido_parsing_utils::Materialize(
+ test_data::kCtap2MakeCredentialSignature));
+ cbor::CBORValue::ArrayValue certificate_chain;
+ certificate_chain.emplace_back(fido_parsing_utils::Materialize(
+ test_data::kCtap2MakeCredentialCertificate));
+ attestation_map.emplace("x5c", std::move(certificate_chain));
+ AuthenticatorMakeCredentialResponse response(AttestationObject(
+ std::move(authenticator_data),
+ std::make_unique<OpaqueAttestationStatement>(
+ "packed", cbor::CBORValue(std::move(attestation_map)))));
+ EXPECT_THAT(
+ GetSerializedCtapDeviceResponse(response),
+ ::testing::ElementsAreArray(
+ base::make_span(test_data::kTestMakeCredentialResponse).subspan(1)));
+}
+
+TEST(CTAPResponseTest, TestSerializeGetAssertionResponse) {
+ constexpr std::array<uint8_t, kRpIdHashLength> kApplicationParameter = {{
+ 0x62, 0x5d, 0xda, 0xdf, 0x74, 0x3f, 0x57, 0x27, 0xe6, 0x6b, 0xba,
+ 0x8c, 0x2e, 0x38, 0x79, 0x22, 0xd1, 0xaf, 0x43, 0xc5, 0x03, 0xd9,
+ 0x11, 0x4a, 0x8f, 0xba, 0x10, 0x4d, 0x84, 0xd0, 0x2b, 0xfa,
+ }};
+
+ constexpr uint8_t kUserId[] = {
+ 0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02,
+ 0x01, 0x02, 0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0,
+ 0x03, 0x02, 0x01, 0x02, 0x30, 0x82, 0x01, 0x93, 0x30, 0x82,
+ };
+
+ constexpr uint8_t kCredentialId[] = {
+ 0xf2, 0x20, 0x06, 0xde, 0x4f, 0x90, 0x5a, 0xf6, 0x8a, 0x43, 0x94,
+ 0x2f, 0x02, 0x4f, 0x2a, 0x5e, 0xce, 0x60, 0x3d, 0x9c, 0x6d, 0x4b,
+ 0x3d, 0xf8, 0xbe, 0x08, 0xed, 0x01, 0xfc, 0x44, 0x26, 0x46, 0xd0,
+ 0x34, 0x85, 0x8a, 0xc7, 0x5b, 0xed, 0x3f, 0xd5, 0x80, 0xbf, 0x98,
+ 0x08, 0xd9, 0x4f, 0xcb, 0xee, 0x82, 0xb9, 0xb2, 0xef, 0x66, 0x77,
+ 0xaf, 0x0a, 0xdc, 0xc3, 0x58, 0x52, 0xea, 0x6b, 0x9e,
+ };
+
+ AuthenticatorData authenticator_data(
+ kApplicationParameter,
+ base::strict_cast<uint8_t>(AuthenticatorData::Flag::kTestOfUserPresence),
+ std::array<uint8_t, kSignCounterLength>{
+ {0x00, 0x00, 0x00, 0x11}} /* signature_counter */,
+ base::nullopt /* attested_credential_data */);
+ AuthenticatorGetAssertionResponse response(
+ std::move(authenticator_data),
+ fido_parsing_utils::Materialize(test_data::kCtap2GetAssertionSignature));
+ response.SetCredential({CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(kCredentialId)});
+ PublicKeyCredentialUserEntity user(fido_parsing_utils::Materialize(kUserId));
+ user.SetDisplayName("John P. Smith");
+ user.SetUserName("johnpsmith@example.com");
+ user.SetIconUrl(GURL("https://pics.acme.com/00/p/aBjjjpqPb.png"));
+ response.SetUserEntity(std::move(user));
+ response.SetNumCredentials(1);
+
+ EXPECT_THAT(
+ GetSerializedCtapDeviceResponse(response),
+ ::testing::ElementsAreArray(
+ base::make_span(test_data::kDeviceGetAssertionResponse).subspan(1)));
+}
+
} // namespace device
diff --git a/chromium/device/fido/device_operation.h b/chromium/device/fido/device_operation.h
index 8d4b57a4ffb..79b907b6554 100644
--- a/chromium/device/fido/device_operation.h
+++ b/chromium/device/fido/device_operation.h
@@ -10,12 +10,9 @@
#include <utility>
#include <vector>
+#include "base/callback.h"
#include "base/macros.h"
#include "base/optional.h"
-#include "device/fido/authenticator_get_assertion_response.h"
-#include "device/fido/authenticator_make_credential_response.h"
-#include "device/fido/ctap_get_assertion_request.h"
-#include "device/fido/ctap_make_credential_request.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_device.h"
@@ -27,15 +24,36 @@ class DeviceOperation {
using DeviceResponseCallback =
base::OnceCallback<void(CtapDeviceResponseCode,
base::Optional<Response>)>;
+ // Represents a per device logic that is owned by FidoTask. Thus,
+ // DeviceOperation does not outlive |request|.
+ DeviceOperation(FidoDevice* device,
+ const Request& request,
+ DeviceResponseCallback callback)
+ : device_(device), request_(request), callback_(std::move(callback)) {}
- DeviceOperation(FidoDevice* device, DeviceResponseCallback callback)
- : device_(device), callback_(std::move(callback)) {}
virtual ~DeviceOperation() = default;
virtual void Start() = 0;
protected:
+ // 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);
+ return;
+ }
+
+ device_->DeviceTransact(std::move(*command), std::move(callback));
+ }
+
+ const Request& request() const { return request_; }
+ FidoDevice* device() const { return device_; }
+ DeviceResponseCallback callback() { return std::move(callback_); }
+
+ private:
FidoDevice* const device_ = nullptr;
+ const Request& request_;
DeviceResponseCallback callback_;
DISALLOW_COPY_AND_ASSIGN(DeviceOperation);
diff --git a/chromium/device/fido/device_response_converter.cc b/chromium/device/fido/device_response_converter.cc
index 9462e701ddb..45e77197690 100644
--- a/chromium/device/fido/device_response_converter.cc
+++ b/chromium/device/fido/device_response_converter.cc
@@ -21,9 +21,20 @@
namespace device {
namespace {
+
constexpr size_t kResponseCodeLength = 1;
+
+ProtocolVersion ConvertStringToProtocolVersion(base::StringPiece version) {
+ if (version == kCtap2Version)
+ return ProtocolVersion::kCtap;
+ if (version == kU2fVersion)
+ return ProtocolVersion::kU2f;
+
+ return ProtocolVersion::kUnknown;
}
+} // namespace
+
using CBOR = cbor::CBORValue;
CtapDeviceResponseCode GetResponseCode(base::span<const uint8_t> buffer) {
@@ -146,22 +157,36 @@ base::Optional<AuthenticatorGetInfoResponse> ReadCTAPGetInfoResponse(
const auto& response_map = decoded_response->GetMap();
auto it = response_map.find(CBOR(1));
- if (it == response_map.end() || !it->second.is_array())
+ if (it == response_map.end() || !it->second.is_array() ||
+ it->second.GetArray().size() > 2) {
return base::nullopt;
+ }
- std::vector<std::string> versions;
+ base::flat_set<ProtocolVersion> protocol_versions;
for (const auto& version : it->second.GetArray()) {
if (!version.is_string())
return base::nullopt;
- versions.push_back(version.GetString());
+ auto protocol = ConvertStringToProtocolVersion(version.GetString());
+ if (protocol == ProtocolVersion::kUnknown) {
+ DLOG(ERROR) << "Unexpected protocol version received.";
+ return base::nullopt;
+ }
+
+ if (!protocol_versions.insert(protocol).second)
+ return base::nullopt;
}
+ if (protocol_versions.empty())
+ return base::nullopt;
+
it = response_map.find(CBOR(3));
- if (it == response_map.end() || !it->second.is_bytestring())
+ if (it == response_map.end() || !it->second.is_bytestring() ||
+ it->second.GetBytestring().size() != kAaguidLength) {
return base::nullopt;
+ }
- AuthenticatorGetInfoResponse response(std::move(versions),
+ AuthenticatorGetInfoResponse response(std::move(protocol_versions),
it->second.GetBytestring());
it = response_map.find(CBOR(2));
diff --git a/chromium/device/fido/fake_fido_discovery.cc b/chromium/device/fido/fake_fido_discovery.cc
index 5a73d3a5d9b..c0c92835676 100644
--- a/chromium/device/fido/fake_fido_discovery.cc
+++ b/chromium/device/fido/fake_fido_discovery.cc
@@ -43,7 +43,7 @@ void FakeFidoDiscovery::StartInternal() {
if (mode_ == StartMode::kAutomatic) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&FakeFidoDiscovery::SimulateStarted,
- base::Unretained(this), true /* success */));
+ AsWeakPtr(), true /* success */));
}
}
diff --git a/chromium/device/fido/fake_fido_discovery_unittest.cc b/chromium/device/fido/fake_fido_discovery_unittest.cc
index 9858dcb3dd8..d3a8c37d642 100644
--- a/chromium/device/fido/fake_fido_discovery_unittest.cc
+++ b/chromium/device/fido/fake_fido_discovery_unittest.cc
@@ -11,6 +11,7 @@
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
+#include "device/fido/fido_test_data.h"
#include "device/fido/mock_fido_device.h"
#include "device/fido/mock_fido_discovery_observer.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -117,8 +118,15 @@ TEST_F(FakeFidoDiscoveryTest, AddDevice) {
auto device0 = std::make_unique<MockFidoDevice>();
EXPECT_CALL(*device0, GetId()).WillOnce(::testing::Return("device0"));
- EXPECT_CALL(observer, DeviceAdded(&discovery, ::testing::_));
+ device0->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo,
+ test_data::kTestAuthenticatorGetInfoResponse);
+ base::RunLoop device0_done;
+ EXPECT_CALL(observer, DeviceAdded(&discovery, ::testing::_))
+ .WillOnce(testing::InvokeWithoutArgs(
+ [&device0_done]() { device0_done.Quit(); }));
discovery.AddDevice(std::move(device0));
+ device0_done.Run();
::testing::Mock::VerifyAndClearExpectations(&observer);
EXPECT_CALL(observer, DiscoveryStarted(&discovery, true));
@@ -127,8 +135,15 @@ TEST_F(FakeFidoDiscoveryTest, AddDevice) {
auto device1 = std::make_unique<MockFidoDevice>();
EXPECT_CALL(*device1, GetId()).WillOnce(::testing::Return("device1"));
- EXPECT_CALL(observer, DeviceAdded(&discovery, ::testing::_));
+ device1->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo,
+ test_data::kTestAuthenticatorGetInfoResponse);
+ base::RunLoop device1_done;
+ EXPECT_CALL(observer, DeviceAdded(&discovery, ::testing::_))
+ .WillOnce(testing::InvokeWithoutArgs(
+ [&device1_done]() { device1_done.Quit(); }));
discovery.AddDevice(std::move(device1));
+ device1_done.Run();
::testing::Mock::VerifyAndClearExpectations(&observer);
}
diff --git a/chromium/device/fido/fido_authenticator.h b/chromium/device/fido/fido_authenticator.h
index 1eddfe5038f..258c9bf6a33 100644
--- a/chromium/device/fido/fido_authenticator.h
+++ b/chromium/device/fido/fido_authenticator.h
@@ -16,7 +16,7 @@
namespace device {
-class AuthenticatorSelectionCriteria;
+class AuthenticatorSupportedOptions;
class CtapGetAssertionRequest;
class CtapMakeCredentialRequest;
@@ -36,13 +36,13 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoAuthenticator {
virtual ~FidoAuthenticator() = default;
virtual void MakeCredential(
- AuthenticatorSelectionCriteria authenticator_selection_criteria,
CtapMakeCredentialRequest request,
MakeCredentialCallback callback) = 0;
virtual void GetAssertion(CtapGetAssertionRequest request,
GetAssertionCallback callback) = 0;
virtual void Cancel() = 0;
virtual std::string GetId() const = 0;
+ virtual const AuthenticatorSupportedOptions& Options() const = 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
index 093901a2739..f6eff882dfa 100644
--- a/chromium/device/fido/fido_ble_connection.cc
+++ b/chromium/device/fido/fido_ble_connection.cc
@@ -91,6 +91,10 @@ FidoBleConnection::~FidoBleConnection() {
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()));
diff --git a/chromium/device/fido/fido_ble_connection.h b/chromium/device/fido/fido_ble_connection.h
index 04797793693..f7efb7044af 100644
--- a/chromium/device/fido/fido_ble_connection.h
+++ b/chromium/device/fido/fido_ble_connection.h
@@ -68,6 +68,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleConnection
~FidoBleConnection() override;
const std::string& address() const { return address_; }
+ const BluetoothDevice* GetBleDevice() const;
virtual void Connect();
virtual void ReadControlPointLength(ControlPointLengthCallback callback);
@@ -80,6 +81,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleConnection
protected:
explicit FidoBleConnection(std::string device_address);
+ std::string address_;
+ ConnectionStatusCallback connection_status_callback_;
+ ReadCallback read_callback_;
+
private:
// BluetoothAdapter::Observer:
void DeviceAdded(BluetoothAdapter* adapter, BluetoothDevice* device) override;
@@ -137,10 +142,6 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleConnection
static void OnWriteError(WriteCallback callback,
BluetoothGattService::GattErrorCode error_code);
- std::string address_;
- ConnectionStatusCallback connection_status_callback_;
- ReadCallback read_callback_;
-
scoped_refptr<BluetoothAdapter> adapter_;
std::unique_ptr<BluetoothGattConnection> connection_;
std::unique_ptr<BluetoothGattNotifySession> notify_session_;
diff --git a/chromium/device/fido/fido_ble_connection_unittest.cc b/chromium/device/fido/fido_ble_connection_unittest.cc
index d7cf1a24156..4e767f5e977 100644
--- a/chromium/device/fido/fido_ble_connection_unittest.cc
+++ b/chromium/device/fido/fido_ble_connection_unittest.cc
@@ -274,13 +274,6 @@ class FidoBleConnectionTest : public ::testing::Test {
u2f_service_ = u2f_service.get();
u2f_device_->AddMockService(std::move(u2f_service));
- ON_CALL(*u2f_service_, GetCharacteristics())
- .WillByDefault(Invoke(
- u2f_service_, &MockBluetoothGattService::GetMockCharacteristics));
-
- ON_CALL(*u2f_service_, GetCharacteristic(_))
- .WillByDefault(Invoke(
- u2f_service_, &MockBluetoothGattService::GetMockCharacteristic));
AddU2fCharacteristics();
}
diff --git a/chromium/device/fido/fido_ble_device.cc b/chromium/device/fido/fido_ble_device.cc
index ae49580fd68..e292afb9a11 100644
--- a/chromium/device/fido/fido_ble_device.cc
+++ b/chromium/device/fido/fido_ble_device.cc
@@ -4,10 +4,14 @@
#include "device/fido/fido_ble_device.h"
+#include <bitset>
+
#include "base/bind.h"
#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/fido_constants.h"
namespace device {
@@ -63,6 +67,35 @@ std::string FidoBleDevice::GetId() const {
return GetId(connection_->address());
}
+bool FidoBleDevice::IsInPairingMode() const {
+ const BluetoothDevice* const ble_device = connection_->GetBleDevice();
+ if (!ble_device)
+ return false;
+
+ // The spec requires exactly one of the LE Limited Discoverable Mode and LE
+ // General Discoverable Mode bits to be set to one when in pairing mode.
+ // https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#ble-advertising-format
+ const base::Optional<uint8_t> flags = ble_device->GetAdvertisingDataFlags();
+ if (flags.has_value()) {
+ const std::bitset<8> flags_set = *flags;
+ return flags_set[kLeLimitedDiscoverableModeBit] ^
+ flags_set[kLeGeneralDiscoverableModeBit];
+ }
+
+ // Since the advertisement flags might not be available due to platform
+ // limitations, authenticators should also provide a specific pairing mode bit
+ // in FIDO's service data.
+ // https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#ble-pairing-authnr-considerations
+ const std::vector<uint8_t>* const fido_service_data =
+ ble_device->GetServiceDataForUUID(BluetoothUUID(kFidoServiceUUID));
+ if (!fido_service_data)
+ return false;
+
+ return !fido_service_data->empty() &&
+ (fido_service_data->front() &
+ static_cast<int>(FidoServiceDataFlags::kPairingMode)) != 0;
+}
+
FidoBleConnection::ConnectionStatusCallback
FidoBleDevice::GetConnectionStatusCallbackForTesting() {
return base::BindRepeating(&FidoBleDevice::OnConnectionStatus,
diff --git a/chromium/device/fido/fido_ble_device.h b/chromium/device/fido/fido_ble_device.h
index 58b11218840..41d617e2b55 100644
--- a/chromium/device/fido/fido_ble_device.h
+++ b/chromium/device/fido/fido_ble_device.h
@@ -42,6 +42,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleDevice : public FidoDevice {
void Cancel() override;
std::string GetId() 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();
diff --git a/chromium/device/fido/fido_ble_device_unittest.cc b/chromium/device/fido/fido_ble_device_unittest.cc
index 39916f831ec..4632da30f09 100644
--- a/chromium/device/fido/fido_ble_device_unittest.cc
+++ b/chromium/device/fido/fido_ble_device_unittest.cc
@@ -7,7 +7,11 @@
#include "base/optional.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_task_environment.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/fido/fido_ble_uuids.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_parsing_utils.h"
#include "device/fido/mock_fido_ble_connection.h"
@@ -24,6 +28,9 @@ using ::testing::Test;
using TestDeviceCallbackReceiver =
test::ValueCallbackReceiver<base::Optional<std::vector<uint8_t>>>;
+using NiceMockBluetoothAdapter = ::testing::NiceMock<MockBluetoothAdapter>;
+using NiceMockBluetoothDevice = ::testing::NiceMock<MockBluetoothDevice>;
+
constexpr uint16_t kControlPointLength = 20;
constexpr uint8_t kTestData[] = {'T', 'E', 'S', 'T'};
@@ -46,6 +53,22 @@ std::vector<std::vector<uint8_t>> ToSerializedFragments(
return serialized_fragments;
}
+void SetAdvertisingDataFlags(BluetoothDevice* device,
+ base::Optional<uint8_t> flags) {
+ device->UpdateAdvertisementData(
+ 0 /* rssi */, std::move(flags), BluetoothDevice::UUIDList(),
+ base::nullopt /* tx_power */, BluetoothDevice::ServiceDataMap(),
+ BluetoothDevice::ManufacturerDataMap());
+}
+
+void SetServiceData(BluetoothDevice* device,
+ BluetoothDevice::ServiceDataMap service_data) {
+ device->UpdateAdvertisementData(
+ 0 /* rssi */, base::nullopt /* flags */, BluetoothDevice::UUIDList(),
+ base::nullopt /* tx_power */, std::move(service_data),
+ BluetoothDevice::ManufacturerDataMap());
+}
+
} // namespace
class FidoBleDeviceTest : public Test {
@@ -196,4 +219,68 @@ TEST_F(FidoBleDeviceTest, GetIdTest) {
device()->GetId());
}
+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();
+ }));
+ 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,
+ BluetoothTestBase::kTestDeviceNameU2f,
+ BluetoothTestBase::kTestDeviceAddress1, /* paired */ true,
+ /* connected */ false);
+ EXPECT_CALL(*mock_adapter, GetDevice(BluetoothTestBase::kTestDeviceAddress1))
+ .WillRepeatedly(Return(mock_bluetooth_device.get()));
+
+ EXPECT_FALSE(device()->IsInPairingMode());
+
+ // Provide advertisement flags, but set neither the Limited nor General LE
+ // Discoverable Mode bit.
+ SetAdvertisingDataFlags(mock_bluetooth_device.get(), 0);
+ EXPECT_FALSE(device()->IsInPairingMode());
+
+ // Set the limited bit, expect to be in pairing mode.
+ SetAdvertisingDataFlags(mock_bluetooth_device.get(),
+ 1 << kLeLimitedDiscoverableModeBit);
+ EXPECT_TRUE(device()->IsInPairingMode());
+
+ // Set the general bit, expect to be in pairing mode.
+ SetAdvertisingDataFlags(mock_bluetooth_device.get(),
+ 1 << kLeGeneralDiscoverableModeBit);
+ EXPECT_TRUE(device()->IsInPairingMode());
+
+ // Set both bits, expect to be NOT in pairing mode.
+ SetAdvertisingDataFlags(
+ mock_bluetooth_device.get(),
+ 1 << kLeLimitedDiscoverableModeBit | 1 << kLeGeneralDiscoverableModeBit);
+ EXPECT_FALSE(device()->IsInPairingMode());
+
+ // Remove flags, should not result in pairing mode.
+ SetAdvertisingDataFlags(mock_bluetooth_device.get(), base::nullopt);
+ EXPECT_FALSE(device()->IsInPairingMode());
+
+ // Update the advertised service data to include the corresponding pairing
+ // mode flag. This should result in the device to be considered in pairing
+ // mode.
+ SetServiceData(mock_bluetooth_device.get(),
+ {{BluetoothUUID(kFidoServiceUUID),
+ {static_cast<int>(FidoServiceDataFlags::kPairingMode)}}});
+ EXPECT_TRUE(device()->IsInPairingMode());
+
+ // Clear out the service data again, device should not be considered to be in
+ // pairing mode anymore.
+ SetServiceData(mock_bluetooth_device.get(), {});
+ EXPECT_FALSE(device()->IsInPairingMode());
+}
+
} // namespace device
diff --git a/chromium/device/fido/fido_ble_frames.cc b/chromium/device/fido/fido_ble_frames.cc
index 314519c0726..411b560d1ef 100644
--- a/chromium/device/fido/fido_ble_frames.cc
+++ b/chromium/device/fido/fido_ble_frames.cc
@@ -30,6 +30,7 @@ bool FidoBleFrame::IsValid() const {
case FidoBleDeviceCommand::kPing:
case FidoBleDeviceCommand::kMsg:
case FidoBleDeviceCommand::kCancel:
+ case FidoBleDeviceCommand::kControl:
return true;
case FidoBleDeviceCommand::kKeepAlive:
case FidoBleDeviceCommand::kError:
diff --git a/chromium/device/fido/fido_ble_frames.h b/chromium/device/fido/fido_ble_frames.h
index 194319f36b5..d326251caa7 100644
--- a/chromium/device/fido/fido_ble_frames.h
+++ b/chromium/device/fido/fido_ble_frames.h
@@ -52,7 +52,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoBleFrame {
NA_1 = 0x06, // Value reserved (HID).
NA_2 = 0x0A, // Value reserved (HID).
NA_3 = 0x0B, // Value reserved (HID).
- OTHER = 0x7F, // Other, unspecified error.
+ ENCRYPTION_FAILED = 0x0C, // Encryption failed for the request.
+ OTHER = 0x7F, // Other, unspecified error.
};
FidoBleFrame();
diff --git a/chromium/device/fido/fido_cable_device.cc b/chromium/device/fido/fido_cable_device.cc
index 02deea187df..31cf975dfb0 100644
--- a/chromium/device/fido/fido_cable_device.cc
+++ b/chromium/device/fido/fido_cable_device.cc
@@ -6,8 +6,8 @@
#include <utility>
-#include "base/command_line.h"
#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/fido_constants.h"
@@ -15,10 +15,6 @@
namespace device {
-namespace switches {
-constexpr char kEnableCableEncryption[] = "enable-cable-encryption";
-} // namespace switches
-
namespace {
// Maximum size of EncryptionData::read_sequence_num or
@@ -26,17 +22,6 @@ namespace {
// counter larger than |kMaxCounter| FidoCableDevice should error out.
constexpr size_t kMaxCounter = (1 << 24) - 1;
-base::StringPiece ConvertToStringPiece(const std::vector<uint8_t>& data) {
- return base::StringPiece(reinterpret_cast<const char*>(data.data()),
- data.size());
-}
-
-// static
-bool IsEncryptionEnabled() {
- base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- return command_line->HasSwitch(switches::kEnableCableEncryption);
-}
-
base::Optional<std::vector<uint8_t>> ConstructEncryptionNonce(
base::span<const uint8_t> nonce,
bool is_sender_client,
@@ -53,19 +38,24 @@ base::Optional<std::vector<uint8_t>> ConstructEncryptionNonce(
}
bool EncryptOutgoingMessage(
- const FidoCableDevice::EncryptionData& encryption_data,
+ const base::Optional<FidoCableDevice::EncryptionData>& encryption_data,
std::vector<uint8_t>* message_to_encrypt) {
+ if (!encryption_data)
+ return false;
+
const auto nonce = ConstructEncryptionNonce(
- encryption_data.nonce, true /* is_sender_client */,
- encryption_data.write_sequence_num);
+ encryption_data->nonce, true /* is_sender_client */,
+ encryption_data->write_sequence_num);
if (!nonce)
return false;
- DCHECK_EQ(nonce->size(), encryption_data.aes_key.NonceLength());
+ DCHECK_EQ(nonce->size(), encryption_data->aes_key.NonceLength());
std::string ciphertext;
- bool encryption_success = encryption_data.aes_key.Seal(
- ConvertToStringPiece(*message_to_encrypt), ConvertToStringPiece(*nonce),
- nullptr /* additional_data */, &ciphertext);
+ bool encryption_success = encryption_data->aes_key.Seal(
+ fido_parsing_utils::ConvertToStringPiece(*message_to_encrypt),
+ fido_parsing_utils::ConvertToStringPiece(*nonce),
+ std::string(1, base::strict_cast<uint8_t>(FidoBleDeviceCommand::kMsg)),
+ &ciphertext);
if (!encryption_success)
return false;
@@ -74,24 +64,29 @@ bool EncryptOutgoingMessage(
}
bool DecryptIncomingMessage(
- const FidoCableDevice::EncryptionData& encryption_data,
+ const base::Optional<FidoCableDevice::EncryptionData>& encryption_data,
FidoBleFrame* incoming_frame) {
+ if (!encryption_data)
+ return false;
+
const auto nonce = ConstructEncryptionNonce(
- encryption_data.nonce, false /* is_sender_client */,
- encryption_data.read_sequence_num);
+ encryption_data->nonce, false /* is_sender_client */,
+ encryption_data->read_sequence_num);
if (!nonce)
return false;
- DCHECK_EQ(nonce->size(), encryption_data.aes_key.NonceLength());
- std::string ciphertext;
+ DCHECK_EQ(nonce->size(), encryption_data->aes_key.NonceLength());
+ std::string plaintext;
- bool decryption_success = encryption_data.aes_key.Open(
- ConvertToStringPiece(incoming_frame->data()),
- ConvertToStringPiece(*nonce), nullptr /* additional_data */, &ciphertext);
+ bool decryption_success = encryption_data->aes_key.Open(
+ fido_parsing_utils::ConvertToStringPiece(incoming_frame->data()),
+ fido_parsing_utils::ConvertToStringPiece(*nonce),
+ std::string(1, base::strict_cast<uint8_t>(incoming_frame->command())),
+ &plaintext);
if (!decryption_success)
return false;
- incoming_frame->data().assign(ciphertext.begin(), ciphertext.end());
+ incoming_frame->data().assign(plaintext.begin(), plaintext.end());
return true;
}
@@ -100,11 +95,12 @@ bool DecryptIncomingMessage(
// FidoCableDevice::EncryptionData ----------------------------------------
FidoCableDevice::EncryptionData::EncryptionData(
- std::string session_key,
- const std::array<uint8_t, 8>& nonce)
- : encryption_key(std::move(session_key)), nonce(nonce) {
- DCHECK_EQ(encryption_key.size(), aes_key.KeyLength());
- aes_key.Init(&encryption_key);
+ std::string encryption_key,
+ base::span<const uint8_t, 8> nonce)
+ : session_key(std::move(encryption_key)),
+ nonce(fido_parsing_utils::Materialize(nonce)) {
+ DCHECK_EQ(session_key.size(), aes_key.KeyLength());
+ aes_key.Init(&session_key);
}
FidoCableDevice::EncryptionData::EncryptionData(EncryptionData&& data) =
@@ -117,32 +113,24 @@ FidoCableDevice::EncryptionData::~EncryptionData() = default;
// FidoCableDevice::EncryptionData ----------------------------------------
-FidoCableDevice::FidoCableDevice(std::string address,
- std::string session_key,
- const std::array<uint8_t, 8>& nonce)
- : FidoBleDevice(std::move(address)),
- encryption_data_(std::move(session_key), nonce),
- weak_factory_(this) {}
+FidoCableDevice::FidoCableDevice(std::string address)
+ : FidoBleDevice(std::move(address)), weak_factory_(this) {}
-FidoCableDevice::FidoCableDevice(std::unique_ptr<FidoBleConnection> connection,
- std::string session_key,
- const std::array<uint8_t, 8>& nonce)
- : FidoBleDevice(std::move(connection)),
- encryption_data_(std::move(session_key), nonce),
- weak_factory_(this) {}
+FidoCableDevice::FidoCableDevice(std::unique_ptr<FidoBleConnection> connection)
+ : FidoBleDevice(std::move(connection)), weak_factory_(this) {}
FidoCableDevice::~FidoCableDevice() = default;
void FidoCableDevice::DeviceTransact(std::vector<uint8_t> command,
DeviceCallback callback) {
- if (IsEncryptionEnabled()) {
- if (!EncryptOutgoingMessage(encryption_data_, &command)) {
- state_ = State::kDeviceError;
- return;
+ if (!EncryptOutgoingMessage(encryption_data_, &command)) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ 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));
@@ -154,13 +142,13 @@ void FidoCableDevice::OnResponseFrame(FrameCallback callback,
ResetTransaction();
state_ = frame ? State::kReady : State::kDeviceError;
- if (frame && IsEncryptionEnabled()) {
+ if (frame && frame->command() != FidoBleDeviceCommand::kControl) {
if (!DecryptIncomingMessage(encryption_data_, &frame.value())) {
state_ = State::kDeviceError;
frame = base::nullopt;
}
- ++encryption_data_.read_sequence_num;
+ ++encryption_data_->read_sequence_num;
}
auto self = GetWeakPtr();
@@ -175,4 +163,18 @@ base::WeakPtr<FidoDevice> FidoCableDevice::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
+void FidoCableDevice::SendHandshakeMessage(
+ std::vector<uint8_t> handshake_message,
+ DeviceCallback callback) {
+ AddToPendingFrames(FidoBleDeviceCommand::kControl,
+ std::move(handshake_message), std::move(callback));
+}
+
+void FidoCableDevice::SetEncryptionData(std::string session_key,
+ base::span<const uint8_t, 8> nonce) {
+ // Encryption data must be set at most once during Cable handshake protocol.
+ DCHECK(!encryption_data_);
+ encryption_data_.emplace(std::move(session_key), nonce);
+}
+
} // namespace device
diff --git a/chromium/device/fido/fido_cable_device.h b/chromium/device/fido/fido_cable_device.h
index e8c76fe0c6a..993df405760 100644
--- a/chromium/device/fido/fido_cable_device.h
+++ b/chromium/device/fido/fido_cable_device.h
@@ -11,6 +11,7 @@
#include <vector>
#include "base/component_export.h"
+#include "base/containers/span.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
@@ -27,14 +28,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDevice : public FidoBleDevice {
// Encapsulates state FidoCableDevice maintains to encrypt and decrypt
// data within FidoBleFrame.
struct COMPONENT_EXPORT(DEVICE_FIDO) EncryptionData {
- EncryptionData() = delete;
- EncryptionData(std::string session_key,
- const std::array<uint8_t, 8>& nonce);
+ EncryptionData(std::string session_key, base::span<const uint8_t, 8> nonce);
EncryptionData(EncryptionData&& data);
EncryptionData& operator=(EncryptionData&& other);
~EncryptionData();
- std::string encryption_key;
+ std::string session_key;
std::array<uint8_t, 8> nonce;
crypto::Aead aes_key{crypto::Aead::AES_256_GCM};
uint32_t write_sequence_num = 0;
@@ -45,13 +44,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDevice : public FidoBleDevice {
using FrameCallback = FidoBleTransaction::FrameCallback;
- FidoCableDevice(std::string address,
- std::string session_key,
- const std::array<uint8_t, 8>& nonce);
+ FidoCableDevice(std::string address);
// Constructor used for testing purposes.
- FidoCableDevice(std::unique_ptr<FidoBleConnection> connection,
- std::string session_key,
- const std::array<uint8_t, 8>& nonce);
+ FidoCableDevice(std::unique_ptr<FidoBleConnection> connection);
~FidoCableDevice() override;
// FidoBleDevice:
@@ -61,13 +56,19 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDevice : public FidoBleDevice {
base::Optional<FidoBleFrame> frame) override;
base::WeakPtr<FidoDevice> GetWeakPtr() override;
+ void SendHandshakeMessage(std::vector<uint8_t> handshake_message,
+ DeviceCallback callback);
+
+ void SetEncryptionData(std::string session_key,
+ base::span<const uint8_t, 8> nonce);
+
private:
FRIEND_TEST_ALL_PREFIXES(FidoCableDeviceTest,
TestCableDeviceSendMultipleRequests);
FRIEND_TEST_ALL_PREFIXES(FidoCableDeviceTest,
TestCableDeviceErrorOnMaxCounter);
- EncryptionData encryption_data_;
+ base::Optional<EncryptionData> encryption_data_;
base::WeakPtrFactory<FidoCableDevice> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(FidoCableDevice);
diff --git a/chromium/device/fido/fido_cable_device_unittest.cc b/chromium/device/fido/fido_cable_device_unittest.cc
index 75ad4acbb6f..ec76ea0b098 100644
--- a/chromium/device/fido/fido_cable_device_unittest.cc
+++ b/chromium/device/fido/fido_cable_device_unittest.cc
@@ -86,11 +86,10 @@ class FakeCableAuthenticator {
DCHECK(encryption_nonce.size() == aead.NonceLength());
std::string ciphertext;
- aead.Seal(message,
- base::StringPiece(
- reinterpret_cast<const char*>(encryption_nonce.data()),
- encryption_nonce.size()),
- nullptr /* additional_data */, &ciphertext);
+ aead.Seal(
+ message, fido_parsing_utils::ConvertToStringPiece(encryption_nonce),
+ std::string(1, base::strict_cast<uint8_t>(FidoBleDeviceCommand::kMsg)),
+ &ciphertext);
authenticator_counter_++;
return ciphertext;
}
@@ -108,12 +107,11 @@ class FakeCableAuthenticator {
DCHECK(encryption_nonce.size() == aead.NonceLength());
std::string ciphertext;
- aead.Open(base::StringPiece(reinterpret_cast<const char*>(message.data()),
- message.size()),
- base::StringPiece(
- reinterpret_cast<const char*>(encryption_nonce.data()),
- encryption_nonce.size()),
- nullptr /* additional_data */, &ciphertext);
+ aead.Open(
+ fido_parsing_utils::ConvertToStringPiece(message),
+ fido_parsing_utils::ConvertToStringPiece(encryption_nonce),
+ std::string(1, base::strict_cast<uint8_t>(FidoBleDeviceCommand::kMsg)),
+ &ciphertext);
expected_client_counter_++;
return ciphertext;
}
@@ -132,8 +130,8 @@ class FidoCableDeviceTest : public Test {
auto connection = std::make_unique<MockFidoBleConnection>(
BluetoothTestBase::kTestDeviceAddress1);
connection_ = connection.get();
- device_ = std::make_unique<FidoCableDevice>(
- std::move(connection), kTestSessionKey, kTestEncryptionNonce);
+ device_ = std::make_unique<FidoCableDevice>(std::move(connection));
+ device_->SetEncryptionData(kTestSessionKey, kTestEncryptionNonce);
connection_->connection_status_callback() =
device_->GetConnectionStatusCallbackForTesting();
@@ -151,11 +149,6 @@ class FidoCableDeviceTest : public Test {
device()->Connect();
}
- void SetUpEncryptionSwitch() {
- base::CommandLine::ForCurrentProcess()->AppendSwitch(
- "enable-cable-encryption");
- }
-
FidoCableDevice* device() { return device_.get(); }
MockFidoBleConnection* connection() { return connection_; }
FakeCableAuthenticator* authenticator() { return &authenticator_; }
@@ -170,7 +163,6 @@ class FidoCableDeviceTest : public Test {
};
TEST_F(FidoCableDeviceTest, TestCaBleDeviceSendData) {
- SetUpEncryptionSwitch();
ConnectWithLength(kControlPointLength);
EXPECT_CALL(*connection(), WriteControlPointPtr(_, _))
@@ -199,7 +191,6 @@ TEST_F(FidoCableDeviceTest, TestCaBleDeviceSendData) {
// Test that FidoCableDevice properly updates counters when sending/receiving
// multiple requests.
TEST_F(FidoCableDeviceTest, TestCableDeviceSendMultipleRequests) {
- SetUpEncryptionSwitch();
ConnectWithLength(kControlPointLength);
EXPECT_CALL(*connection(), WriteControlPointPtr(_, _))
.Times(2)
@@ -215,8 +206,9 @@ TEST_F(FidoCableDeviceTest, TestCableDeviceSendMultipleRequests) {
authenticator_reply)));
}));
- EXPECT_EQ(0u, device()->encryption_data_.write_sequence_num);
- EXPECT_EQ(0u, device()->encryption_data_.read_sequence_num);
+ ASSERT_TRUE(device()->encryption_data_);
+ EXPECT_EQ(0u, device()->encryption_data_->write_sequence_num);
+ EXPECT_EQ(0u, device()->encryption_data_->read_sequence_num);
TestDeviceCallbackReceiver callback_receiver1;
device()->DeviceTransact(fido_parsing_utils::Materialize(kTestData),
@@ -225,8 +217,8 @@ TEST_F(FidoCableDeviceTest, TestCableDeviceSendMultipleRequests) {
const auto& value1 = callback_receiver1.value();
ASSERT_TRUE(value1);
EXPECT_THAT(*value1, ::testing::ElementsAreArray(kTestData));
- EXPECT_EQ(1u, device()->encryption_data_.write_sequence_num);
- EXPECT_EQ(1u, device()->encryption_data_.read_sequence_num);
+ EXPECT_EQ(1u, device()->encryption_data_->write_sequence_num);
+ EXPECT_EQ(1u, device()->encryption_data_->read_sequence_num);
constexpr uint8_t kTestData2[] = {'T', 'E', 'S', 'T', '2'};
TestDeviceCallbackReceiver callback_receiver2;
@@ -236,13 +228,12 @@ TEST_F(FidoCableDeviceTest, TestCableDeviceSendMultipleRequests) {
const auto& value2 = callback_receiver2.value();
ASSERT_TRUE(value2);
EXPECT_THAT(*value2, ::testing::ElementsAreArray(kTestData2));
- EXPECT_EQ(2u, device()->encryption_data_.write_sequence_num);
- EXPECT_EQ(2u, device()->encryption_data_.read_sequence_num);
+ EXPECT_EQ(2u, device()->encryption_data_->write_sequence_num);
+ EXPECT_EQ(2u, device()->encryption_data_->read_sequence_num);
}
TEST_F(FidoCableDeviceTest, TestCableDeviceFailOnIncorrectSessionKey) {
constexpr char kIncorrectSessionKey[] = "11111111111111111111111111111111";
- SetUpEncryptionSwitch();
ConnectWithLength(kControlPointLength);
EXPECT_CALL(*connection(), WriteControlPointPtr(_, _))
@@ -272,7 +263,6 @@ TEST_F(FidoCableDeviceTest, TestCableDeviceFailOnIncorrectSessionKey) {
TEST_F(FidoCableDeviceTest, TestCableDeviceFailOnUnexpectedCounter) {
constexpr uint32_t kIncorrectAuthenticatorCounter = 1;
- SetUpEncryptionSwitch();
ConnectWithLength(kControlPointLength);
EXPECT_CALL(*connection(), WriteControlPointPtr(_, _))
@@ -308,7 +298,6 @@ TEST_F(FidoCableDeviceTest, TestCableDeviceFailOnUnexpectedCounter) {
// the expected counter value -- should return an error.
TEST_F(FidoCableDeviceTest, TestCableDeviceErrorOnMaxCounter) {
ConnectWithLength(kControlPointLength);
- SetUpEncryptionSwitch();
EXPECT_CALL(*connection(), WriteControlPointPtr(_, _))
.WillOnce(Invoke([this](const auto& data, auto* cb) {
@@ -326,7 +315,8 @@ TEST_F(FidoCableDeviceTest, TestCableDeviceErrorOnMaxCounter) {
}));
TestDeviceCallbackReceiver callback_receiver;
- device()->encryption_data_.read_sequence_num = kInvalidCounter;
+ ASSERT_TRUE(device()->encryption_data_);
+ device()->encryption_data_->read_sequence_num = kInvalidCounter;
device()->DeviceTransact(fido_parsing_utils::Materialize(kTestData),
callback_receiver.callback());
@@ -335,26 +325,4 @@ TEST_F(FidoCableDeviceTest, TestCableDeviceErrorOnMaxCounter) {
EXPECT_FALSE(value);
}
-TEST_F(FidoCableDeviceTest, TestEncryptionDisabledWithoutCommandLineSwitch) {
- ConnectWithLength(kControlPointLength);
-
- EXPECT_CALL(*connection(), WriteControlPointPtr(_, _))
- .WillOnce(Invoke([this](const auto& data, auto* cb) {
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(*cb), true));
-
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(connection()->read_callback(), data));
- }));
-
- TestDeviceCallbackReceiver callback_receiver;
- device()->DeviceTransact(fido_parsing_utils::Materialize(kTestData),
- callback_receiver.callback());
-
- callback_receiver.WaitForCallback();
- const auto& value = callback_receiver.value();
- ASSERT_TRUE(value);
- EXPECT_THAT(*value, ::testing::ElementsAreArray(kTestData));
-}
-
} // namespace device
diff --git a/chromium/device/fido/fido_cable_discovery.cc b/chromium/device/fido/fido_cable_discovery.cc
index 1468db3d37d..5911dda11f3 100644
--- a/chromium/device/fido/fido_cable_discovery.cc
+++ b/chromium/device/fido/fido_cable_discovery.cc
@@ -19,6 +19,7 @@
#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/fido_parsing_utils.h"
namespace device {
@@ -96,14 +97,17 @@ std::unique_ptr<BluetoothAdvertisement::Data> ConstructAdvertisementData(
advertisement_data->set_manufacturer_data(std::move(manufacturer_data));
#elif defined(OS_LINUX) || defined(OS_CHROMEOS)
- // Service data for ChromeOS and Linux is 1 byte corresponding to Cable
- // version number, followed by 7 empty(0x00) bytes, followed by 16 bytes
- // corresponding to client EID.
+ // 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.
auto service_data = std::make_unique<BluetoothAdvertisement::ServiceData>();
- std::vector<uint8_t> service_data_value(24, 0);
- service_data_value[0] = version_number;
+ 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[1] = version_number;
std::copy(client_eid.begin(), client_eid.end(),
- service_data_value.begin() + 8);
+ service_data_value.begin() + 2);
service_data->emplace(kCableAdvertisementUUID, std::move(service_data_value));
advertisement_data->set_service_data(std::move(service_data));
#endif
@@ -119,11 +123,11 @@ FidoCableDiscovery::CableDiscoveryData::CableDiscoveryData(
uint8_t version,
const EidArray& client_eid,
const EidArray& authenticator_eid,
- const SessionKeyArray& session_key)
+ const SessionPreKeyArray& session_pre_key)
: version(version),
client_eid(client_eid),
authenticator_eid(authenticator_eid),
- session_key(session_key) {}
+ session_pre_key(session_pre_key) {}
FidoCableDiscovery::CableDiscoveryData::CableDiscoveryData(
const CableDiscoveryData& data) = default;
@@ -145,6 +149,15 @@ FidoCableDiscovery::~FidoCableDiscovery() {
advertisement.second->Unregister(base::DoNothing(), base::DoNothing());
}
+std::unique_ptr<FidoCableHandshakeHandler>
+FidoCableDiscovery::CreateHandshakeHandler(
+ FidoCableDevice* device,
+ base::span<const uint8_t, kSessionPreKeySize> session_pre_key,
+ base::span<const uint8_t, 8> nonce) {
+ return std::make_unique<FidoCableHandshakeHandler>(device, nonce,
+ session_pre_key);
+}
+
void FidoCableDiscovery::DeviceAdded(BluetoothAdapter* adapter,
BluetoothDevice* device) {
if (!IsCableDevice(device))
@@ -252,11 +265,42 @@ void FidoCableDiscovery::CableDeviceFound(BluetoothAdapter* adapter,
if (!extract_success)
return;
- AddDevice(std::make_unique<FidoCableDevice>(
- device->GetAddress(),
- std::string(found_cable_device_data->session_key.begin(),
- found_cable_device_data->session_key.end()),
- nonce));
+ 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);
+ }
+}
+
+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) {
+ auto handshake_handler =
+ CreateHandshakeHandler(cable_device.get(), session_pre_key, nonce);
+ auto* const handshake_handler_ptr = handshake_handler.get();
+ cable_handshake_handlers_.emplace(cable_device->GetId(),
+ std::move(handshake_handler));
+
+ handshake_handler_ptr->InitiateCableHandshake(
+ base::BindOnce(&FidoCableDiscovery::ValidateAuthenticatorHandshakeMessage,
+ weak_factory_.GetWeakPtr(), std::move(cable_device),
+ handshake_handler_ptr));
+}
+
+void FidoCableDiscovery::ValidateAuthenticatorHandshakeMessage(
+ std::unique_ptr<FidoCableDevice> cable_device,
+ FidoCableHandshakeHandler* handshake_handler,
+ base::Optional<std::vector<uint8_t>> handshake_response) {
+ if (!handshake_response)
+ return;
+
+ if (!handshake_handler->ValidateAuthenticatorHandshakeMessage(
+ *handshake_response))
+ return;
+
+ AddDevice(std::move(cable_device));
}
const FidoCableDiscovery::CableDiscoveryData*
@@ -266,9 +310,14 @@ FidoCableDiscovery::GetFoundCableDiscoveryData(
device->GetServiceDataForUUID(CableAdvertisementUUID());
DCHECK(service_data);
+ // Received service data from authenticator must have a flag that signals that
+ // the service data includes Cable EID.
+ if (service_data->empty() || !(service_data->at(0) >> 5 & 1u))
+ return nullptr;
+
EidArray received_authenticator_eid;
bool extract_success = fido_parsing_utils::ExtractArray(
- *service_data, 8, &received_authenticator_eid);
+ *service_data, 2, &received_authenticator_eid);
if (!extract_success)
return nullptr;
diff --git a/chromium/device/fido/fido_cable_discovery.h b/chromium/device/fido/fido_cable_discovery.h
index a5f9f1749be..a3b9ea27678 100644
--- a/chromium/device/fido/fido_cable_discovery.h
+++ b/chromium/device/fido/fido_cable_discovery.h
@@ -9,11 +9,12 @@
#include <array>
#include <map>
-#include <set>
+#include <memory>
#include <string>
#include <vector>
#include "base/component_export.h"
+#include "base/containers/span.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
@@ -21,16 +22,18 @@
namespace device {
+class FidoCableDevice;
class BluetoothDevice;
class BluetoothAdvertisement;
+class FidoCableHandshakeHandler;
class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery
: public FidoBleDiscoveryBase {
public:
static constexpr size_t kEphemeralIdSize = 16;
- static constexpr size_t kSessionKeySize = 32;
+ static constexpr size_t kSessionPreKeySize = 32;
using EidArray = std::array<uint8_t, kEphemeralIdSize>;
- using SessionKeyArray = std::array<uint8_t, kSessionKeySize>;
+ 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
@@ -43,7 +46,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery
CableDiscoveryData(uint8_t version,
const EidArray& client_eid,
const EidArray& authenticator_eid,
- const SessionKeyArray& session_key);
+ const SessionPreKeyArray& session_pre_key);
CableDiscoveryData(const CableDiscoveryData& data);
CableDiscoveryData& operator=(const CableDiscoveryData& other);
~CableDiscoveryData();
@@ -51,12 +54,18 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery
uint8_t version;
EidArray client_eid;
EidArray authenticator_eid;
- SessionKeyArray session_key;
+ SessionPreKeyArray session_pre_key;
};
FidoCableDiscovery(std::vector<CableDiscoveryData> discovery_data);
~FidoCableDiscovery() override;
+ protected:
+ virtual std::unique_ptr<FidoCableHandshakeHandler> CreateHandshakeHandler(
+ FidoCableDevice* device,
+ base::span<const uint8_t, kSessionPreKeySize> session_pre_key,
+ base::span<const uint8_t, 8> nonce);
+
private:
FRIEND_TEST_ALL_PREFIXES(FidoCableDiscoveryTest,
TestUnregisterAdvertisementUponDestruction);
@@ -83,6 +92,15 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery
// once all advertisements has been processed.
void RecordAdvertisementResult(bool is_success);
void CableDeviceFound(BluetoothAdapter* adapter, BluetoothDevice* device);
+ void ConductEncryptionHandshake(
+ std::unique_ptr<FidoCableDevice> device,
+ base::span<const uint8_t, kSessionPreKeySize> session_pre_key,
+ base::span<const uint8_t, 8> nonce);
+ void ValidateAuthenticatorHandshakeMessage(
+ std::unique_ptr<FidoCableDevice> cable_device,
+ FidoCableHandshakeHandler* handshake_handler,
+ base::Optional<std::vector<uint8_t>> handshake_response);
+
const CableDiscoveryData* GetFoundCableDiscoveryData(
const BluetoothDevice* device) const;
@@ -90,6 +108,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery
size_t advertisement_success_counter_ = 0;
size_t advertisement_failure_counter_ = 0;
std::map<EidArray, scoped_refptr<BluetoothAdvertisement>> advertisements_;
+ std::map<std::string, std::unique_ptr<FidoCableHandshakeHandler>>
+ cable_handshake_handlers_;
base::WeakPtrFactory<FidoCableDiscovery> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(FidoCableDiscovery);
diff --git a/chromium/device/fido/fido_cable_discovery_unittest.cc b/chromium/device/fido/fido_cable_discovery_unittest.cc
index 920f7d8e289..859955de745 100644
--- a/chromium/device/fido/fido_cable_discovery_unittest.cc
+++ b/chromium/device/fido/fido_cable_discovery_unittest.cc
@@ -8,6 +8,7 @@
#include <memory>
#include <utility>
+#include "base/containers/span.h"
#include "base/stl_util.h"
#include "build/build_config.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
@@ -16,6 +17,7 @@
#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/fido_parsing_utils.h"
#include "device/fido/mock_fido_discovery_observer.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -47,7 +49,7 @@ constexpr FidoCableDiscovery::EidArray kInvalidAuthenticatorEid = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}};
-constexpr FidoCableDiscovery::SessionKeyArray kTestSessionKey = {
+constexpr FidoCableDiscovery::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}};
@@ -63,7 +65,7 @@ constexpr FidoCableDiscovery::EidArray kSecondaryAuthenticatorEid = {
{0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
0xee, 0xee, 0xee, 0xee}};
-constexpr FidoCableDiscovery::SessionKeyArray kSecondarySessionKey = {
+constexpr FidoCableDiscovery::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}};
@@ -111,9 +113,10 @@ MATCHER_P2(IsAdvertisementContent,
return false;
const auto& service_data_value = service_data_with_uuid->second;
- return service_data_value[0] == kTestCableVersionNumber &&
- service_data_value.size() == 24u &&
- base::make_span(service_data_value).subspan(8) == expected_client_eid;
+ return (service_data_value[0] >> 5 & 1) &&
+ service_data_value[1] == kTestCableVersionNumber &&
+ service_data_value.size() == 18u &&
+ base::make_span(service_data_value).subspan(2) == expected_client_eid;
#endif
@@ -149,14 +152,17 @@ class CableMockAdapter : public MockBluetoothAdapter {
authenticator_eid) {
auto mock_device = CreateTestBluetoothDevice();
- std::vector<uint8_t> service_data(8);
- fido_parsing_utils::Append(&service_data, authenticator_eid);
+ std::vector<uint8_t> service_data(18);
+ service_data[0] = 1 << 5;
+ 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));
mock_device->UpdateAdvertisementData(
- 1 /* rssi */, BluetoothDevice::UUIDList(), std::move(service_data_map),
- BluetoothDevice::ManufacturerDataMap(), nullptr /* tx_power*/);
+ 1 /* rssi */, base::nullopt /* flags */, BluetoothDevice::UUIDList(),
+ base::nullopt /* tx_power */, std::move(service_data_map),
+ BluetoothDevice::ManufacturerDataMap());
auto* mock_device_ptr = mock_device.get();
AddMockDevice(std::move(mock_device));
@@ -200,6 +206,44 @@ class CableMockAdapter : public MockBluetoothAdapter {
~CableMockAdapter() override = default;
};
+class FakeHandshakeHandler : public FidoCableHandshakeHandler {
+ public:
+ FakeHandshakeHandler(FidoCableDevice* device,
+ base::span<const uint8_t, 8> nonce,
+ base::span<const uint8_t, 32> session_pre_key)
+ : FidoCableHandshakeHandler(device, nonce, session_pre_key) {}
+ ~FakeHandshakeHandler() override = default;
+
+ void InitiateCableHandshake(FidoDevice::DeviceCallback callback) override {
+ std::move(callback).Run(std::vector<uint8_t>());
+ }
+
+ bool ValidateAuthenticatorHandshakeMessage(
+ base::span<const uint8_t> response) override {
+ return true;
+ }
+};
+
+// Fake discovery that encapsulates exactly the same behavior as
+// FidoCableDiscovery except that it uses FakeHandshakeHandler instead of
+// FidoHandshakeHandler to conduct handshake with the authenticator.
+class FakeFidoCableDiscovery : public FidoCableDiscovery {
+ public:
+ explicit FakeFidoCableDiscovery(
+ std::vector<CableDiscoveryData> discovery_data)
+ : FidoCableDiscovery(std::move(discovery_data)) {}
+ ~FakeFidoCableDiscovery() override = default;
+
+ private:
+ std::unique_ptr<FidoCableHandshakeHandler> CreateHandshakeHandler(
+ FidoCableDevice* device,
+ base::span<const uint8_t, kSessionPreKeySize> session_pre_key,
+ base::span<const uint8_t, 8> nonce) override {
+ return std::make_unique<FakeHandshakeHandler>(device, nonce,
+ session_pre_key);
+ }
+};
+
} // namespace
class FidoCableDiscoveryTest : public ::testing::Test {
@@ -207,8 +251,8 @@ class FidoCableDiscoveryTest : public ::testing::Test {
std::unique_ptr<FidoCableDiscovery> CreateDiscovery() {
std::vector<FidoCableDiscovery::CableDiscoveryData> discovery_data;
discovery_data.emplace_back(kTestCableVersionNumber, kClientEid,
- kAuthenticatorEid, kTestSessionKey);
- return std::make_unique<FidoCableDiscovery>(std::move(discovery_data));
+ kAuthenticatorEid, kTestSessionPreKey);
+ return std::make_unique<FakeFidoCableDiscovery>(std::move(discovery_data));
}
base::test::ScopedTaskEnvironment scoped_task_environment_;
@@ -270,11 +314,12 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryFindsIncorrectDevice) {
TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithMultipleEids) {
std::vector<FidoCableDiscovery::CableDiscoveryData> discovery_data;
discovery_data.emplace_back(kTestCableVersionNumber, kClientEid,
- kAuthenticatorEid, kTestSessionKey);
+ kAuthenticatorEid, kTestSessionPreKey);
discovery_data.emplace_back(kTestCableVersionNumber, kSecondaryClientEid,
- kSecondaryAuthenticatorEid, kSecondarySessionKey);
+ kSecondaryAuthenticatorEid,
+ kSecondarySessionPreKey);
auto cable_discovery =
- std::make_unique<FidoCableDiscovery>(std::move(discovery_data));
+ std::make_unique<FakeFidoCableDiscovery>(std::move(discovery_data));
NiceMock<MockFidoDiscoveryObserver> mock_observer;
EXPECT_CALL(mock_observer, DeviceAdded(_, _));
cable_discovery->set_observer(&mock_observer);
@@ -305,11 +350,12 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithMultipleEids) {
TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithPartialAdvertisementSuccess) {
std::vector<FidoCableDiscovery::CableDiscoveryData> discovery_data;
discovery_data.emplace_back(kTestCableVersionNumber, kClientEid,
- kAuthenticatorEid, kTestSessionKey);
+ kAuthenticatorEid, kTestSessionPreKey);
discovery_data.emplace_back(kTestCableVersionNumber, kSecondaryClientEid,
- kSecondaryAuthenticatorEid, kSecondarySessionKey);
+ kSecondaryAuthenticatorEid,
+ kSecondarySessionPreKey);
auto cable_discovery =
- std::make_unique<FidoCableDiscovery>(std::move(discovery_data));
+ std::make_unique<FakeFidoCableDiscovery>(std::move(discovery_data));
NiceMock<MockFidoDiscoveryObserver> mock_observer;
EXPECT_CALL(mock_observer, DeviceAdded(_, _));
cable_discovery->set_observer(&mock_observer);
@@ -338,11 +384,12 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithPartialAdvertisementSuccess) {
TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithAdvertisementFailures) {
std::vector<FidoCableDiscovery::CableDiscoveryData> discovery_data;
discovery_data.emplace_back(kTestCableVersionNumber, kClientEid,
- kAuthenticatorEid, kTestSessionKey);
+ kAuthenticatorEid, kTestSessionPreKey);
discovery_data.emplace_back(kTestCableVersionNumber, kSecondaryClientEid,
- kSecondaryAuthenticatorEid, kSecondarySessionKey);
+ kSecondaryAuthenticatorEid,
+ kSecondarySessionPreKey);
auto cable_discovery =
- std::make_unique<FidoCableDiscovery>(std::move(discovery_data));
+ std::make_unique<FakeFidoCableDiscovery>(std::move(discovery_data));
NiceMock<MockFidoDiscoveryObserver> mock_observer;
EXPECT_CALL(mock_observer, DeviceAdded(_, _)).Times(0);
diff --git a/chromium/device/fido/fido_cable_handshake_handler.cc b/chromium/device/fido/fido_cable_handshake_handler.cc
new file mode 100644
index 00000000000..a47c5d0f7cb
--- /dev/null
+++ b/chromium/device/fido/fido_cable_handshake_handler.cc
@@ -0,0 +1,173 @@
+// 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_cable_handshake_handler.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/cbor/cbor_reader.h"
+#include "components/cbor/cbor_values.h"
+#include "components/cbor/cbor_writer.h"
+#include "crypto/hkdf.h"
+#include "crypto/hmac.h"
+#include "crypto/random.h"
+#include "device/fido/fido_cable_device.h"
+#include "device/fido/fido_constants.h"
+#include "device/fido/fido_parsing_utils.h"
+
+namespace device {
+
+namespace {
+
+// Length of CBOR encoded authenticator hello message concatenated with
+// 16 byte message authentication code.
+constexpr size_t kCableAuthenticatorHandshakeMessageSize = 66;
+
+// Length of CBOR encoded client hello message concatenated with 16 byte message
+// authenticator code.
+constexpr size_t kClientHelloMessageSize = 58;
+
+constexpr size_t kCableHandshakeMacMessageSize = 16;
+
+// Derives key of size 32 bytes using HDKF algorithm.
+// See https://tools.ietf.org/html/rfc5869 for details.
+std::string GenerateKey(base::StringPiece secret,
+ base::StringPiece salt,
+ base::StringPiece info) {
+ return crypto::HkdfSha256(secret, salt, info, 32);
+}
+
+base::Optional<std::array<uint8_t, kClientHelloMessageSize>>
+ConstructHandshakeMessage(base::StringPiece handshake_key,
+ base::span<const uint8_t, 16> client_random_nonce) {
+ cbor::CBORValue::MapValue map;
+ map.emplace(0, kCableClientHelloMessage);
+ map.emplace(1, client_random_nonce);
+ auto client_hello = cbor::CBORWriter::Write(cbor::CBORValue(std::move(map)));
+ DCHECK(client_hello);
+
+ crypto::HMAC hmac(crypto::HMAC::SHA256);
+ if (!hmac.Init(handshake_key))
+ return base::nullopt;
+
+ std::array<uint8_t, 32> client_hello_mac;
+ if (!hmac.Sign(fido_parsing_utils::ConvertToStringPiece(*client_hello),
+ client_hello_mac.data(), client_hello_mac.size())) {
+ return base::nullopt;
+ }
+
+ DCHECK_EQ(kClientHelloMessageSize,
+ client_hello->size() + kCableHandshakeMacMessageSize);
+ std::array<uint8_t, kClientHelloMessageSize> handshake_message;
+ std::copy(client_hello->begin(), client_hello->end(),
+ handshake_message.begin());
+ std::copy(client_hello_mac.begin(),
+ client_hello_mac.begin() + kCableHandshakeMacMessageSize,
+ handshake_message.begin() + client_hello->size());
+
+ return handshake_message;
+}
+
+} // namespace
+
+FidoCableHandshakeHandler::FidoCableHandshakeHandler(
+ FidoCableDevice* cable_device,
+ base::span<const uint8_t, 8> nonce,
+ base::span<const uint8_t, 32> session_pre_key)
+ : cable_device_(cable_device),
+ nonce_(fido_parsing_utils::Materialize(nonce)),
+ session_pre_key_(fido_parsing_utils::Materialize(session_pre_key)),
+ handshake_key_(GenerateKey(
+ fido_parsing_utils::ConvertToStringPiece(session_pre_key_),
+ fido_parsing_utils::ConvertToStringPiece(nonce_),
+ kCableHandshakeKeyInfo)),
+ weak_factory_(this) {
+ crypto::RandBytes(client_session_random_.data(),
+ client_session_random_.size());
+}
+
+FidoCableHandshakeHandler::~FidoCableHandshakeHandler() = default;
+
+void FidoCableHandshakeHandler::InitiateCableHandshake(
+ FidoDevice::DeviceCallback callback) {
+ auto handshake_message =
+ ConstructHandshakeMessage(handshake_key_, client_session_random_);
+ if (!handshake_message) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
+ return;
+ }
+
+ cable_device_->SendHandshakeMessage(
+ fido_parsing_utils::Materialize(*handshake_message), std::move(callback));
+}
+
+bool FidoCableHandshakeHandler::ValidateAuthenticatorHandshakeMessage(
+ base::span<const uint8_t> response) {
+ crypto::HMAC hmac(crypto::HMAC::SHA256);
+ if (!hmac.Init(handshake_key_))
+ return false;
+
+ if (response.size() != kCableAuthenticatorHandshakeMessageSize) {
+ return false;
+ }
+
+ const auto authenticator_hello = response.first(
+ kCableAuthenticatorHandshakeMessageSize - kCableHandshakeMacMessageSize);
+ if (!hmac.VerifyTruncated(
+ fido_parsing_utils::ConvertToStringPiece(authenticator_hello),
+ fido_parsing_utils::ConvertToStringPiece(
+ response.subspan(authenticator_hello.size())))) {
+ return false;
+ }
+
+ const auto authenticator_hello_cbor =
+ cbor::CBORReader::Read(authenticator_hello);
+ if (!authenticator_hello_cbor || !authenticator_hello_cbor->is_map() ||
+ authenticator_hello_cbor->GetMap().size() != 2) {
+ return false;
+ }
+
+ const auto authenticator_hello_msg =
+ authenticator_hello_cbor->GetMap().find(cbor::CBORValue(0));
+ if (authenticator_hello_msg == authenticator_hello_cbor->GetMap().end() ||
+ !authenticator_hello_msg->second.is_string() ||
+ authenticator_hello_msg->second.GetString() !=
+ kCableAuthenticatorHelloMessage) {
+ return false;
+ }
+
+ const auto authenticator_random_nonce =
+ authenticator_hello_cbor->GetMap().find(cbor::CBORValue(1));
+ if (authenticator_random_nonce == authenticator_hello_cbor->GetMap().end() ||
+ !authenticator_random_nonce->second.is_bytestring() ||
+ authenticator_random_nonce->second.GetBytestring().size() != 16) {
+ return false;
+ }
+
+ cable_device_->SetEncryptionData(
+ GetEncryptionKeyAfterSuccessfulHandshake(
+ authenticator_random_nonce->second.GetBytestring()),
+ nonce_);
+
+ return true;
+}
+
+std::string FidoCableHandshakeHandler::GetEncryptionKeyAfterSuccessfulHandshake(
+ base::span<const uint8_t, 16> authenticator_random_nonce) const {
+ std::vector<uint8_t> nonce_message;
+ fido_parsing_utils::Append(&nonce_message, nonce_);
+ fido_parsing_utils::Append(&nonce_message, client_session_random_);
+ fido_parsing_utils::Append(&nonce_message, authenticator_random_nonce);
+ return GenerateKey(
+ fido_parsing_utils::ConvertToStringPiece(session_pre_key_),
+ fido_parsing_utils::ConvertToStringPiece(
+ fido_parsing_utils::CreateSHA256Hash(
+ fido_parsing_utils::ConvertToStringPiece(nonce_message))),
+ kCableDeviceEncryptionKeyInfo);
+}
+
+} // namespace device
diff --git a/chromium/device/fido/fido_cable_handshake_handler.h b/chromium/device/fido/fido_cable_handshake_handler.h
new file mode 100644
index 00000000000..fd764e53a30
--- /dev/null
+++ b/chromium/device/fido/fido_cable_handshake_handler.h
@@ -0,0 +1,62 @@
+// 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_FIDO_CABLE_HANDSHAKE_HANDLER_H_
+#define DEVICE_FIDO_FIDO_CABLE_HANDSHAKE_HANDLER_H_
+
+#include <stdint.h>
+
+#include <array>
+#include <string>
+#include <vector>
+
+#include "base/component_export.h"
+#include "base/containers/span.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "device/fido/fido_device.h"
+
+namespace device {
+
+class FidoCableDevice;
+
+// Handles exchanging handshake messages with external authenticator and
+// validating the handshake messages to derive a shared session key to be used
+// for message encryption.
+// See: fido-client-to-authenticator-protocol.html#cable-encryption-handshake of
+// the most up-to-date spec.
+class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableHandshakeHandler {
+ public:
+ FidoCableHandshakeHandler(FidoCableDevice* device,
+ base::span<const uint8_t, 8> nonce,
+ base::span<const uint8_t, 32> session_pre_key);
+ virtual ~FidoCableHandshakeHandler();
+
+ virtual void InitiateCableHandshake(FidoDevice::DeviceCallback callback);
+ virtual bool ValidateAuthenticatorHandshakeMessage(
+ base::span<const uint8_t> response);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(FidoCableHandshakeHandlerTest, HandShakeSuccess);
+ FRIEND_TEST_ALL_PREFIXES(FidoCableHandshakeHandlerTest,
+ HandshakeFailWithIncorrectAuthenticatorResponse);
+
+ std::string GetEncryptionKeyAfterSuccessfulHandshake(
+ base::span<const uint8_t, 16> authenticator_random_nonce) const;
+
+ FidoCableDevice* const cable_device_;
+ std::array<uint8_t, 8> nonce_;
+ std::array<uint8_t, 32> session_pre_key_;
+ std::array<uint8_t, 16> client_session_random_;
+ std::string handshake_key_;
+
+ base::WeakPtrFactory<FidoCableHandshakeHandler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FidoCableHandshakeHandler);
+};
+
+} // namespace device
+
+#endif // DEVICE_FIDO_FIDO_CABLE_HANDSHAKE_HANDLER_H_
diff --git a/chromium/device/fido/fido_cable_handshake_handler_fuzzer.cc b/chromium/device/fido/fido_cable_handshake_handler_fuzzer.cc
new file mode 100644
index 00000000000..64a9a7afd74
--- /dev/null
+++ b/chromium/device/fido/fido_cable_handshake_handler_fuzzer.cc
@@ -0,0 +1,38 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include <array>
+
+#include "base/containers/span.h"
+#include "device/fido/fido_cable_device.h"
+#include "device/fido/fido_cable_handshake_handler.h"
+#include "device/fido/fido_constants.h"
+
+namespace {
+
+constexpr std::array<uint8_t, 32> 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 std::array<uint8_t, 8> kTestNonce = {{
+ 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x09, 0x08,
+}};
+
+constexpr char kTestDeviceAddress[] = "Fake_Address";
+
+} // namespace
+
+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);
+ device::FidoCableHandshakeHandler handshake_handler(
+ &test_cable_device, kTestNonce, kTestSessionPreKey);
+ handshake_handler.ValidateAuthenticatorHandshakeMessage(data_span);
+ return 0;
+}
diff --git a/chromium/device/fido/fido_cable_handshake_handler_unittest.cc b/chromium/device/fido/fido_cable_handshake_handler_unittest.cc
new file mode 100644
index 00000000000..554707d1e9a
--- /dev/null
+++ b/chromium/device/fido/fido_cable_handshake_handler_unittest.cc
@@ -0,0 +1,411 @@
+// 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_cable_handshake_handler.h"
+
+#include <array>
+#include <limits>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/optional.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "components/cbor/cbor_reader.h"
+#include "components/cbor/cbor_values.h"
+#include "components/cbor/cbor_writer.h"
+#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/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"
+
+namespace device {
+
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Test;
+using TestDeviceCallbackReceiver =
+ test::ValueCallbackReceiver<base::Optional<std::vector<uint8_t>>>;
+
+// Sufficiently large test control point length as we are not interested
+// in testing fragmentations of BLE messages. All Cable messages are encrypted
+// and decrypted per request frame, not fragment.
+constexpr auto kControlPointLength = std::numeric_limits<uint16_t>::max();
+
+constexpr std::array<uint8_t, 16> kAuthenticatorSessionRandom = {{
+ 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+}};
+
+constexpr std::array<uint8_t, 32> 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 std::array<uint8_t, 32> kIncorrectSessionPreKey = {{
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
+}};
+
+constexpr std::array<uint8_t, 8> kTestNonce = {{
+ 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x09, 0x08,
+}};
+
+constexpr std::array<uint8_t, 8> kIncorrectNonce = {{
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+}};
+
+constexpr std::array<uint8_t, 50> kValidAuthenticatorHello = {{
+ // Map(2)
+ 0xA2,
+ // Key(0)
+ 0x00,
+ // Text(28)
+ 0x78, 0x1C,
+ // "caBLE v1 authenticator hello"
+ 0x63, 0x61, 0x42, 0x4C, 0x45, 0x20, 0x76, 0x31, 0x20, 0x61, 0x75, 0x74,
+ 0x68, 0x65, 0x6E, 0x74, 0x69, 0x63, 0x61, 0x74, 0x6F, 0x72, 0x20, 0x68,
+ 0x65, 0x6C, 0x6C, 0x6F,
+ // Key(1)
+ 0x01,
+ // Bytes(16)
+ 0x50,
+ // Authenticator random session
+ 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+}};
+
+constexpr std::array<uint8_t, 43> kInvalidAuthenticatorHello = {{
+ // Map(2)
+ 0xA2,
+ // Key(0)
+ 0x00,
+ // Text(21)
+ 0x75,
+ // "caBLE INVALID MESSAGE"
+ 0x63, 0x61, 0x42, 0x4C, 0x45, 0x20, 0x49, 0x4E, 0x56, 0x41, 0x4C, 0x49,
+ 0x44, 0x20, 0x4D, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45,
+ // Key(1)
+ 0x01,
+ // Bytes(16)
+ 0x50,
+ // Authenticator random session
+ 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+}};
+
+constexpr char kIncorrectHandshakeKey[] = "INCORRECT_HANDSHAKE_KEY_12345678";
+
+// Returns the expected encryption key that should be constructed given that
+// the client random nonce is |client_random_nonce| and other determining
+// factors (i.e. authenticator session random, session pre key, and nonce) are
+// |kAuthenticatorSessionRandom|, |kTestSessionPreKey|, and |kTestNonce|,
+// respectively.
+std::string GetExpectedEncryptionKey(
+ base::span<const uint8_t> client_random_nonce) {
+ std::vector<uint8_t> nonce_message =
+ fido_parsing_utils::Materialize(kTestNonce);
+ fido_parsing_utils::Append(&nonce_message, client_random_nonce);
+ fido_parsing_utils::Append(&nonce_message, kAuthenticatorSessionRandom);
+ return crypto::HkdfSha256(
+ fido_parsing_utils::ConvertToStringPiece(kTestSessionPreKey),
+ fido_parsing_utils::ConvertToStringPiece(
+ fido_parsing_utils::CreateSHA256Hash(
+ fido_parsing_utils::ConvertToStringPiece(nonce_message))),
+ kCableDeviceEncryptionKeyInfo, 32);
+}
+
+// Given a hello message and handshake key from the authenticator, construct
+// a handshake message by concatenating hello message and its mac message
+// derived from |handshake_key|.
+std::vector<uint8_t> ConstructAuthenticatorHelloReply(
+ base::span<const uint8_t> hello_msg,
+ base::StringPiece handshake_key) {
+ auto reply = fido_parsing_utils::Materialize(hello_msg);
+ crypto::HMAC hmac(crypto::HMAC::SHA256);
+ if (!hmac.Init(handshake_key))
+ return std::vector<uint8_t>();
+
+ std::array<uint8_t, 32> authenticator_hello_mac;
+ if (!hmac.Sign(fido_parsing_utils::ConvertToStringPiece(hello_msg),
+ authenticator_hello_mac.data(),
+ authenticator_hello_mac.size())) {
+ return std::vector<uint8_t>();
+ }
+
+ fido_parsing_utils::Append(
+ &reply, base::make_span(authenticator_hello_mac).first(16));
+ return reply;
+}
+
+// Constructs incoming handshake message from the authenticator into a BLE
+// control fragment.
+std::vector<uint8_t> ConstructSerializedOutgoingFragment(
+ base::span<const uint8_t> data) {
+ if (data.empty())
+ return std::vector<uint8_t>();
+
+ FidoBleFrame response_frame(FidoBleDeviceCommand::kControl,
+ fido_parsing_utils::Materialize(data));
+ const auto response_fragment =
+ std::get<0>(response_frame.ToFragments(kControlPointLength));
+
+ std::vector<uint8_t> outgoing_message;
+ response_fragment.Serialize(&outgoing_message);
+ return outgoing_message;
+}
+
+// Authenticator abstraction that handles logic related to validating handshake
+// messages from the client and sending rely handshake message back to the
+// client. Session key and nonce are assumed to be |kTestSessionPreKey| and
+// |kTestNonce| respectively.
+class FakeCableAuthenticator {
+ public:
+ FakeCableAuthenticator() {
+ handshake_key_ = crypto::HkdfSha256(
+ fido_parsing_utils::ConvertToStringPiece(kTestSessionPreKey),
+ fido_parsing_utils::ConvertToStringPiece(kTestNonce),
+ kCableHandshakeKeyInfo, 32);
+ }
+
+ // Receives handshake message from the client, check its validity and if the
+ // handshake message is valid, store |client_session_random| embedded in the
+ // handshake message.
+ bool ConfirmClientHandshakeMessage(
+ base::span<const uint8_t> handshake_message) {
+ if (handshake_message.size() <= 16)
+ return false;
+
+ crypto::HMAC hmac(crypto::HMAC::SHA256);
+ if (!hmac.Init(handshake_key_))
+ return false;
+
+ // Handshake message from client should be concatenation of client hello
+ // message (42 bytes) with message authentication code (16 bytes).
+ if (handshake_message.size() != 58)
+ return false;
+
+ const auto client_hello = handshake_message.first(42);
+ if (!hmac.VerifyTruncated(
+ fido_parsing_utils::ConvertToStringPiece(client_hello),
+ fido_parsing_utils::ConvertToStringPiece(
+ handshake_message.subspan(42)))) {
+ return false;
+ }
+
+ const auto& client_hello_cbor = cbor::CBORReader::Read(client_hello);
+ if (!client_hello_cbor)
+ return false;
+
+ const auto& message_map = client_hello_cbor->GetMap();
+ auto hello_message_it = message_map.find(cbor::CBORValue(0));
+ auto client_random_nonce_it = message_map.find(cbor::CBORValue(1));
+ if (hello_message_it == message_map.end() ||
+ client_random_nonce_it == message_map.end())
+ return false;
+
+ if (!hello_message_it->second.is_string() ||
+ hello_message_it->second.GetString() != kCableClientHelloMessage) {
+ return false;
+ }
+
+ if (!client_random_nonce_it->second.is_bytestring() ||
+ client_random_nonce_it->second.GetBytestring().size() != 16) {
+ return false;
+ }
+
+ client_session_random_ =
+ std::move(client_random_nonce_it->second.GetBytestring());
+ return true;
+ }
+
+ std::vector<uint8_t> RelyWithAuthenticatorHandShakeMessage(
+ base::span<const uint8_t> handshake_message) {
+ if (!ConfirmClientHandshakeMessage(handshake_message))
+ return std::vector<uint8_t>();
+
+ return ConstructAuthenticatorHelloReply(kValidAuthenticatorHello,
+ handshake_key_);
+ }
+
+ private:
+ std::string handshake_key_;
+ std::vector<uint8_t> client_session_random_;
+ std::vector<uint8_t> authenticator_session_random_ =
+ fido_parsing_utils::Materialize(kAuthenticatorSessionRandom);
+};
+
+} // namespace
+
+class FidoCableHandshakeHandlerTest : public Test {
+ public:
+ FidoCableHandshakeHandlerTest() {
+ auto connection = std::make_unique<MockFidoBleConnection>(
+ BluetoothTestBase::kTestDeviceAddress1);
+ connection_ = connection.get();
+ device_ = std::make_unique<FidoCableDevice>(std::move(connection));
+
+ connection_->connection_status_callback() =
+ device_->GetConnectionStatusCallbackForTesting();
+ connection_->read_callback() = device_->GetReadCallbackForTesting();
+ }
+
+ std::unique_ptr<FidoCableHandshakeHandler> CreateHandshakeHandler(
+ std::array<uint8_t, 8> nonce,
+ std::array<uint8_t, 32> session_pre_key) {
+ return std::make_unique<FidoCableHandshakeHandler>(device_.get(), nonce,
+ session_pre_key);
+ }
+
+ void ConnectWithLength(uint16_t length) {
+ EXPECT_CALL(*connection(), Connect()).WillOnce(Invoke([this] {
+ connection()->connection_status_callback().Run(true);
+ }));
+
+ EXPECT_CALL(*connection(), ReadControlPointLengthPtr(_))
+ .WillOnce(Invoke([length](auto* cb) { std::move(*cb).Run(length); }));
+
+ device()->Connect();
+ }
+
+ FidoCableDevice* device() { return device_.get(); }
+ MockFidoBleConnection* connection() { return connection_; }
+ FakeCableAuthenticator* authenticator() { return &authenticator_; }
+ TestDeviceCallbackReceiver& callback_receiver() { return callback_receiver_; }
+
+ protected:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+ private:
+ FakeCableAuthenticator authenticator_;
+ MockFidoBleConnection* connection_;
+ std::unique_ptr<FidoCableDevice> device_;
+ TestDeviceCallbackReceiver callback_receiver_;
+};
+
+// Checks that outgoing handshake message from the client is a BLE frame with
+// Control command type.
+MATCHER(IsControlFrame, "") {
+ return !arg.empty() &&
+ arg[0] == base::strict_cast<uint8_t>(FidoBleDeviceCommand::kControl);
+}
+
+TEST_F(FidoCableHandshakeHandlerTest, HandShakeSuccess) {
+ ConnectWithLength(kControlPointLength);
+
+ EXPECT_CALL(*connection(), WriteControlPointPtr(IsControlFrame(), _))
+ .WillOnce(Invoke([this](const auto& data, auto* cb) {
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(*cb), true));
+
+ const auto client_ble_handshake_message =
+ base::make_span(data).subspan(3);
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ connection()->read_callback(),
+ ConstructSerializedOutgoingFragment(
+ authenticator()->RelyWithAuthenticatorHandShakeMessage(
+ client_ble_handshake_message))));
+ }));
+
+ auto handshake_handler =
+ CreateHandshakeHandler(kTestNonce, kTestSessionPreKey);
+ handshake_handler->InitiateCableHandshake(callback_receiver().callback());
+
+ callback_receiver().WaitForCallback();
+ const auto& value = callback_receiver().value();
+ ASSERT_TRUE(value);
+ EXPECT_TRUE(handshake_handler->ValidateAuthenticatorHandshakeMessage(*value));
+ EXPECT_EQ(GetExpectedEncryptionKey(handshake_handler->client_session_random_),
+ handshake_handler->GetEncryptionKeyAfterSuccessfulHandshake(
+ kAuthenticatorSessionRandom));
+}
+
+TEST_F(FidoCableHandshakeHandlerTest, HandShakeWithIncorrectSessionPreKey) {
+ ConnectWithLength(kControlPointLength);
+
+ EXPECT_CALL(*connection(), WriteControlPointPtr(IsControlFrame(), _))
+ .WillOnce(Invoke([this](const auto& data, auto* cb) {
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(*cb), true));
+
+ const auto client_ble_handshake_message =
+ base::make_span(data).subspan(3);
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ connection()->read_callback(),
+ ConstructSerializedOutgoingFragment(
+ authenticator()->RelyWithAuthenticatorHandShakeMessage(
+ client_ble_handshake_message))));
+ }));
+
+ auto handshake_handler =
+ CreateHandshakeHandler(kTestNonce, kIncorrectSessionPreKey);
+ handshake_handler->InitiateCableHandshake(callback_receiver().callback());
+
+ callback_receiver().WaitForCallback();
+ EXPECT_FALSE(callback_receiver().value());
+}
+
+TEST_F(FidoCableHandshakeHandlerTest, HandshakeFailWithIncorrectNonce) {
+ ConnectWithLength(kControlPointLength);
+
+ EXPECT_CALL(*connection(), WriteControlPointPtr(IsControlFrame(), _))
+ .WillOnce(Invoke([this](const auto& data, auto* cb) {
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(*cb), true));
+
+ const auto client_ble_handshake_message =
+ base::make_span(data).subspan(3);
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ connection()->read_callback(),
+ ConstructSerializedOutgoingFragment(
+ authenticator()->RelyWithAuthenticatorHandShakeMessage(
+ client_ble_handshake_message))));
+ }));
+
+ auto handshake_handler =
+ CreateHandshakeHandler(kIncorrectNonce, kTestSessionPreKey);
+ handshake_handler->InitiateCableHandshake(callback_receiver().callback());
+
+ callback_receiver().WaitForCallback();
+ EXPECT_FALSE(callback_receiver().value());
+}
+
+TEST_F(FidoCableHandshakeHandlerTest,
+ HandshakeFailWithIncorrectAuthenticatorResponse) {
+ auto handshake_handler =
+ CreateHandshakeHandler(kTestNonce, kTestSessionPreKey);
+
+ EXPECT_NE(kIncorrectHandshakeKey, handshake_handler->handshake_key_);
+ const auto authenticator_reply_with_invalid_key =
+ ConstructAuthenticatorHelloReply(kValidAuthenticatorHello,
+ kIncorrectHandshakeKey);
+ EXPECT_FALSE(handshake_handler->ValidateAuthenticatorHandshakeMessage(
+ authenticator_reply_with_invalid_key));
+
+ const auto authenticator_reply_with_invalid_hello_msg =
+ ConstructAuthenticatorHelloReply(kInvalidAuthenticatorHello,
+ handshake_handler->handshake_key_);
+ EXPECT_FALSE(handshake_handler->ValidateAuthenticatorHandshakeMessage(
+ authenticator_reply_with_invalid_hello_msg));
+}
+
+} // namespace device
diff --git a/chromium/device/fido/fido_constants.cc b/chromium/device/fido/fido_constants.cc
index 4c61c7e8abb..bae7c52d45e 100644
--- a/chromium/device/fido/fido_constants.cc
+++ b/chromium/device/fido/fido_constants.cc
@@ -19,8 +19,14 @@ const std::array<uint8_t, 32> kBogusChallenge = {
const char kResidentKeyMapKey[] = "rk";
const char kUserVerificationMapKey[] = "uv";
const char kUserPresenceMapKey[] = "up";
-const char kClientPinMapKey[] = "client_pin";
+const char kClientPinMapKey[] = "clientPin";
const char kPlatformDeviceMapKey[] = "plat";
+const char kEntityIdMapKey[] = "id";
+const char kEntityNameMapKey[] = "name";
+const char kDisplayNameMapKey[] = "displayName";
+const char kIconUrlMapKey[] = "icon";
+const char kCredentialTypeMapKey[] = "type";
+const char kCredentialAlgorithmMapKey[] = "alg";
const size_t kHidPacketSize = 64;
const uint32_t kHidBroadcastChannel = 0xffffffff;
@@ -41,9 +47,9 @@ const uint8_t kP1TupRequiredConsumed = kP1TupRequired | kP1TupConsumed;
const uint8_t kP1CheckOnly = 0x07;
const uint8_t kP1IndividualAttestation = 0x80;
const size_t kMaxKeyHandleLength = 255;
-const size_t kU2fParameterLength = 32;
const base::TimeDelta kDeviceTimeout = base::TimeDelta::FromSeconds(3);
+const base::TimeDelta kU2fRetryDelay = base::TimeDelta::FromMilliseconds(200);
const base::TimeDelta kHidKeepAliveDelay =
base::TimeDelta::FromMilliseconds(100);
@@ -63,4 +69,12 @@ const char* CredentialTypeToString(CredentialType type) {
return kPublicKey;
}
+const char kCableHandshakeKeyInfo[] = "FIDO caBLE v1 handshakeKey";
+const char kCableDeviceEncryptionKeyInfo[] = "FIDO caBLE v1 sessionKey";
+const char kCableAuthenticatorHelloMessage[] = "caBLE v1 authenticator hello";
+const char kCableClientHelloMessage[] = "caBLE v1 client hello";
+
+const char kCtap2Version[] = "FIDO_2_0";
+const char kU2fVersion[] = "U2F_V2";
+
} // namespace device
diff --git a/chromium/device/fido/fido_constants.h b/chromium/device/fido/fido_constants.h
index 2d4040651f2..184ea90ae8d 100644
--- a/chromium/device/fido/fido_constants.h
+++ b/chromium/device/fido/fido_constants.h
@@ -35,6 +35,49 @@ enum class ProtocolVersion {
kUnknown,
};
+// Length of the U2F challenge parameter:
+// https://goo.gl/y75WrX#registration-request-message---u2f_register
+constexpr size_t kU2fChallengeParamLength = 32;
+
+// Length of the U2F application parameter:
+// https://goo.gl/y75WrX#registration-request-message---u2f_register
+constexpr size_t kU2fApplicationParamLength = 32;
+
+// Offset of the length of the U2F registration key handle:
+// https://goo.gl/y75WrX#registration-response-message-success
+constexpr size_t kU2fKeyHandleLengthOffset = 66;
+
+// Offset of the U2F registration key handle:
+// https://goo.gl/y75WrX#registration-response-message-success
+constexpr size_t kU2fKeyHandleOffset = 67;
+
+// Length of the SHA-256 hash of the JSON-serialized client data:
+// https://www.w3.org/TR/webauthn/#collectedclientdata-hash-of-the-serialized-client-data
+constexpr size_t kClientDataHashLength = 32;
+
+// Length of the SHA-256 hash of the RP ID asssociated with the credential:
+// https://www.w3.org/TR/webauthn/#sec-authenticator-data
+constexpr size_t kRpIdHashLength = 32;
+
+static_assert(kU2fApplicationParamLength == kRpIdHashLength,
+ "kU2fApplicationParamLength must be equal to kRpIdHashLength.");
+
+// Length of the flags:
+// https://www.w3.org/TR/webauthn/#sec-authenticator-data
+constexpr size_t kFlagsLength = 1;
+
+// Length of the signature counter, 32-bit unsigned big-endian integer:
+// https://www.w3.org/TR/webauthn/#sec-authenticator-data
+constexpr size_t kSignCounterLength = 4;
+
+// Length of the AAGUID of the authenticator:
+// https://www.w3.org/TR/webauthn/#sec-attested-credential-data
+constexpr size_t kAaguidLength = 16;
+
+// Length of the byte length L of Credential ID, 16-bit unsigned big-endian
+// integer: https://www.w3.org/TR/webauthn/#sec-attested-credential-data
+constexpr size_t kCredentialIdLengthLength = 2;
+
// CTAP protocol device response code, as specified in
// https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html#authenticator-api
enum class CtapDeviceResponseCode : uint8_t {
@@ -57,7 +100,6 @@ enum class CtapDeviceResponseCode : uint8_t {
kCtap2ErrTooManyElements = 0x17,
kCtap2ErrExtensionNotSupported = 0x18,
kCtap2ErrCredentialExcluded = 0x19,
- kCtap2ErrCredentialNotValid = 0x20,
kCtap2ErrProcesssing = 0x21,
kCtap2ErrInvalidCredential = 0x22,
kCtap2ErrUserActionPending = 0x23,
@@ -111,7 +153,6 @@ constexpr std::array<CtapDeviceResponseCode, 51> GetCtapResponseCodeList() {
CtapDeviceResponseCode::kCtap2ErrTooManyElements,
CtapDeviceResponseCode::kCtap2ErrExtensionNotSupported,
CtapDeviceResponseCode::kCtap2ErrCredentialExcluded,
- CtapDeviceResponseCode::kCtap2ErrCredentialNotValid,
CtapDeviceResponseCode::kCtap2ErrProcesssing,
CtapDeviceResponseCode::kCtap2ErrInvalidCredential,
CtapDeviceResponseCode::kCtap2ErrUserActionPending,
@@ -174,10 +215,23 @@ enum class FidoBleDeviceCommand : uint8_t {
kPing = 0x81,
kKeepAlive = 0x82,
kMsg = 0x83,
+ kControl = 0x84,
kCancel = 0xBE,
kError = 0xBF,
};
+// Relevant LE Discoverable Mode bits. Reference:
+// Bluetooth Core Specification Supplement, Part A, section 1.3
+constexpr uint8_t kLeLimitedDiscoverableModeBit = 0;
+constexpr uint8_t kLeGeneralDiscoverableModeBit = 1;
+
+// Fido Service Data Flags as specified in
+// https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#ble-pairing-authnr-considerations
+enum class FidoServiceDataFlags : uint8_t {
+ kPairingMode = 0x80,
+ kPasskeyEntry = 0x40,
+};
+
// Authenticator API commands supported by CTAP devices, as specified in
// https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html#authenticator-api
enum class CtapRequestCommand : uint8_t {
@@ -238,6 +292,12 @@ COMPONENT_EXPORT(DEVICE_FIDO) extern const char kUserVerificationMapKey[];
COMPONENT_EXPORT(DEVICE_FIDO) extern const char kUserPresenceMapKey[];
COMPONENT_EXPORT(DEVICE_FIDO) extern const char kClientPinMapKey[];
COMPONENT_EXPORT(DEVICE_FIDO) extern const char kPlatformDeviceMapKey[];
+COMPONENT_EXPORT(DEVICE_FIDO) extern const char kEntityIdMapKey[];
+COMPONENT_EXPORT(DEVICE_FIDO) extern const char kEntityNameMapKey[];
+COMPONENT_EXPORT(DEVICE_FIDO) extern const char kDisplayNameMapKey[];
+COMPONENT_EXPORT(DEVICE_FIDO) extern const char kIconUrlMapKey[];
+COMPONENT_EXPORT(DEVICE_FIDO) extern const char kCredentialTypeMapKey[];
+COMPONENT_EXPORT(DEVICE_FIDO) extern const char kCredentialAlgorithmMapKey[];
// HID transport specific constants.
COMPONENT_EXPORT(DEVICE_FIDO) extern const size_t kHidPacketSize;
@@ -272,11 +332,14 @@ COMPONENT_EXPORT(DEVICE_FIDO) extern const uint8_t kP1CheckOnly;
// return with this registration.
COMPONENT_EXPORT(DEVICE_FIDO) extern const uint8_t kP1IndividualAttestation;
COMPONENT_EXPORT(DEVICE_FIDO) extern const size_t kMaxKeyHandleLength;
-COMPONENT_EXPORT(DEVICE_FIDO) extern const size_t kU2fParameterLength;
// Maximum wait time before client error outs on device.
COMPONENT_EXPORT(DEVICE_FIDO) extern const base::TimeDelta kDeviceTimeout;
+// Wait time before polling device for U2F register/sign operation again when
+// device times out waiting for user presence.
+COMPONENT_EXPORT(DEVICE_FIDO) extern const base::TimeDelta kU2fRetryDelay;
+
// Interval wait time before retrying reading on HID connection when
// CTAPHID_KEEPALIVE message has been received.
// https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html#ctaphid_keepalive-0x3b
@@ -296,6 +359,18 @@ extern const char kPublicKey[];
const char* CredentialTypeToString(CredentialType type);
+// Values used to construct/validate handshake messages for Cable handshake
+// protocol.
+COMPONENT_EXPORT(DEVICE_FIDO) extern const char kCableHandshakeKeyInfo[];
+COMPONENT_EXPORT(DEVICE_FIDO) extern const char kCableDeviceEncryptionKeyInfo[];
+COMPONENT_EXPORT(DEVICE_FIDO)
+extern const char kCableAuthenticatorHelloMessage[];
+COMPONENT_EXPORT(DEVICE_FIDO) extern const char kCableClientHelloMessage[];
+
+// TODO(hongjunchoi): Add url to the official spec once it's standardized.
+COMPONENT_EXPORT(DEVICE_FIDO) extern const char kCtap2Version[];
+COMPONENT_EXPORT(DEVICE_FIDO) extern const char kU2fVersion[];
+
} // namespace device
#endif // DEVICE_FIDO_FIDO_CONSTANTS_H_
diff --git a/chromium/device/fido/fido_device.cc b/chromium/device/fido/fido_device.cc
index 1646e652c05..e3f448c052b 100644
--- a/chromium/device/fido/fido_device.cc
+++ b/chromium/device/fido/fido_device.cc
@@ -6,11 +6,56 @@
#include <utility>
+#include "base/bind.h"
+#include "base/stl_util.h"
+#include "device/base/features.h"
+#include "device/fido/ctap_empty_authenticator_request.h"
+#include "device/fido/device_response_converter.h"
+#include "device/fido/fido_constants.h"
+
namespace device {
FidoDevice::FidoDevice() = default;
FidoDevice::~FidoDevice() = default;
+void FidoDevice::DiscoverSupportedProtocolAndDeviceInfo(
+ base::OnceClosure done) {
+ if (base::FeatureList::IsEnabled(kNewCtap2Device)) {
+ // Set the protocol version to CTAP2 for the purpose of sending the GetInfo
+ // request. The correct value will be set in the callback based on the
+ // device response.
+ supported_protocol_ = ProtocolVersion::kCtap;
+ DeviceTransact(AuthenticatorGetInfoRequest().Serialize(),
+ base::BindOnce(&FidoDevice::OnDeviceInfoReceived,
+ GetWeakPtr(), std::move(done)));
+ } else {
+ supported_protocol_ = ProtocolVersion::kU2f;
+ std::move(done).Run();
+ }
+}
+
+bool FidoDevice::SupportedProtocolIsInitialized() {
+ return (supported_protocol_ == ProtocolVersion::kU2f && !device_info_) ||
+ (supported_protocol_ == ProtocolVersion::kCtap && device_info_);
+}
+
+void FidoDevice::OnDeviceInfoReceived(
+ base::OnceClosure done,
+ base::Optional<std::vector<uint8_t>> response) {
+ state_ = FidoDevice::State::kReady;
+
+ base::Optional<AuthenticatorGetInfoResponse> get_info_response =
+ response ? ReadCTAPGetInfoResponse(*response) : base::nullopt;
+ if (!get_info_response || !base::ContainsKey(get_info_response->versions(),
+ ProtocolVersion::kCtap)) {
+ supported_protocol_ = ProtocolVersion::kU2f;
+ } else {
+ supported_protocol_ = ProtocolVersion::kCtap;
+ device_info_ = std::move(*get_info_response);
+ }
+ std::move(done).Run();
+}
+
void FidoDevice::SetDeviceInfo(AuthenticatorGetInfoResponse device_info) {
device_info_ = std::move(device_info);
}
diff --git a/chromium/device/fido/fido_device.h b/chromium/device/fido/fido_device.h
index 28ffef7f692..defeec56cea 100644
--- a/chromium/device/fido/fido_device.h
+++ b/chromium/device/fido/fido_device.h
@@ -21,6 +21,11 @@
namespace device {
// Device abstraction for an individual CTAP1.0/CTAP2.0 device.
+//
+// Devices are instantiated with an unknown protocol version. Users should call
+// |DiscoverSupportedProtocolAndDeviceInfo| to determine a device's
+// capabilities and initialize the instance accordingly. Instances returned by
+// |FidoDiscovery| are already fully initialized.
class COMPONENT_EXPORT(DEVICE_FIDO) FidoDevice {
public:
using WinkCallback = base::OnceClosure;
@@ -41,11 +46,18 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDevice {
virtual void Cancel() = 0;
virtual std::string GetId() const = 0;
- void SetDeviceInfo(AuthenticatorGetInfoResponse device_info);
+ // Sends a speculative AuthenticatorGetInfo request to determine whether the
+ // device supports the CTAP2 protocol, and initializes supported_protocol_
+ // and device_info_ according to the result (unless the
+ // device::kNewCtap2Device feature is off, in which case U2F is assumed).
+ void DiscoverSupportedProtocolAndDeviceInfo(base::OnceClosure done);
+ // Returns whether supported_protocol has been correctly initialized (usually
+ // by calling DiscoverSupportedProtocolAndDeviceInfo).
+ bool SupportedProtocolIsInitialized();
+ // TODO(martinkr): Rename to "SetSupportedProtocolForTesting".
void set_supported_protocol(ProtocolVersion supported_protocol) {
supported_protocol_ = supported_protocol;
}
- void set_state(State state) { state_ = state; }
ProtocolVersion supported_protocol() const { return supported_protocol_; }
const base::Optional<AuthenticatorGetInfoResponse>& device_info() const {
@@ -56,6 +68,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDevice {
protected:
virtual base::WeakPtr<FidoDevice> GetWeakPtr() = 0;
+ void OnDeviceInfoReceived(base::OnceClosure done,
+ base::Optional<std::vector<uint8_t>> response);
+ void SetDeviceInfo(AuthenticatorGetInfoResponse device_info);
+
State state_ = State::kInit;
ProtocolVersion supported_protocol_ = ProtocolVersion::kUnknown;
base::Optional<AuthenticatorGetInfoResponse> device_info_;
diff --git a/chromium/device/fido/fido_device_authenticator.cc b/chromium/device/fido/fido_device_authenticator.cc
index c8aa469918e..93071cefe4e 100644
--- a/chromium/device/fido/fido_device_authenticator.cc
+++ b/chromium/device/fido/fido_device_authenticator.cc
@@ -6,7 +6,8 @@
#include <utility>
-#include "device/fido/authenticator_selection_criteria.h"
+#include "base/logging.h"
+#include "device/fido/authenticator_supported_options.h"
#include "device/fido/ctap_get_assertion_request.h"
#include "device/fido/ctap_make_credential_request.h"
#include "device/fido/fido_device.h"
@@ -16,19 +17,18 @@
namespace device {
FidoDeviceAuthenticator::FidoDeviceAuthenticator(FidoDevice* device)
- : device_(device) {}
+ : device_(device) {
+ DCHECK(device_->SupportedProtocolIsInitialized());
+}
FidoDeviceAuthenticator::~FidoDeviceAuthenticator() = default;
-void FidoDeviceAuthenticator::MakeCredential(
- AuthenticatorSelectionCriteria authenticator_selection_criteria,
- CtapMakeCredentialRequest request,
- MakeCredentialCallback callback) {
+void FidoDeviceAuthenticator::MakeCredential(CtapMakeCredentialRequest request,
+ MakeCredentialCallback callback) {
DCHECK(!task_);
// 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), std::move(authenticator_selection_criteria),
- std::move(callback));
+ task_ = std::make_unique<MakeCredentialTask>(device_, std::move(request),
+ std::move(callback));
}
void FidoDeviceAuthenticator::GetAssertion(CtapGetAssertionRequest request,
@@ -48,6 +48,21 @@ std::string FidoDeviceAuthenticator::GetId() const {
return device_->GetId();
}
+const AuthenticatorSupportedOptions& FidoDeviceAuthenticator::Options() const {
+ static const AuthenticatorSupportedOptions default_options;
+ switch (device_->supported_protocol()) {
+ case ProtocolVersion::kU2f:
+ return default_options;
+ case ProtocolVersion::kCtap:
+ DCHECK(device_->device_info()) << "uninitialized device";
+ return device_->device_info()->options();
+ case ProtocolVersion::kUnknown:
+ NOTREACHED() << "uninitialized device";
+ }
+ NOTREACHED();
+ return default_options;
+}
+
void FidoDeviceAuthenticator::SetTaskForTesting(
std::unique_ptr<FidoTask> task) {
task_ = std::move(task);
diff --git a/chromium/device/fido/fido_device_authenticator.h b/chromium/device/fido/fido_device_authenticator.h
index 29db9d814f5..ec62fefdd9c 100644
--- a/chromium/device/fido/fido_device_authenticator.h
+++ b/chromium/device/fido/fido_device_authenticator.h
@@ -15,7 +15,6 @@
namespace device {
-class AuthenticatorSelectionCriteria;
class CtapGetAssertionRequest;
class CtapMakeCredentialRequest;
class FidoDevice;
@@ -32,13 +31,13 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDeviceAuthenticator
// FidoAuthenticator:
void MakeCredential(
- AuthenticatorSelectionCriteria authenticator_selection_criteria,
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;
protected:
void OnCtapMakeCredentialResponseReceived(
diff --git a/chromium/device/fido/fido_discovery.cc b/chromium/device/fido/fido_discovery.cc
index 299bbf058c7..98d4f678947 100644
--- a/chromium/device/fido/fido_discovery.cc
+++ b/chromium/device/fido/fido_discovery.cc
@@ -6,6 +6,7 @@
#include <utility>
+#include "base/bind.h"
#include "build/build_config.h"
#include "device/fido/fido_ble_discovery.h"
#include "device/fido/fido_device.h"
@@ -67,7 +68,7 @@ std::unique_ptr<FidoDiscovery> FidoDiscovery::Create(
}
FidoDiscovery::FidoDiscovery(FidoTransportProtocol transport)
- : transport_(transport) {}
+ : transport_(transport), weak_factory_(this) {}
FidoDiscovery::~FidoDiscovery() = default;
@@ -132,9 +133,15 @@ const FidoDevice* FidoDiscovery::GetDevice(base::StringPiece device_id) const {
bool FidoDiscovery::AddDevice(std::unique_ptr<FidoDevice> device) {
std::string device_id = device->GetId();
const auto result = devices_.emplace(std::move(device_id), std::move(device));
- if (result.second)
- NotifyDeviceAdded(result.first->second.get());
- return result.second;
+ 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));
+ return true;
}
bool FidoDiscovery::RemoveDevice(base::StringPiece device_id) {
diff --git a/chromium/device/fido/fido_discovery.h b/chromium/device/fido/fido_discovery.h
index a3c31b5af94..68aadcc9a9f 100644
--- a/chromium/device/fido/fido_discovery.h
+++ b/chromium/device/fido/fido_discovery.h
@@ -49,6 +49,11 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDiscovery {
// before the client of FidoDiscovery calls FidoDiscovery::Start(). However,
// for devices already known to the system at that point, DeviceAdded()
// might already be called to reported already known devices.
+ //
+ // The supplied FidoDevice instance is guaranteed to have its protocol
+ // version initialized. I.e., FidoDiscovery calls
+ // FidoDevice::DiscoverSupportedProtocolAndDeviceInfo() before notifying
+ // the Observer.
virtual void DeviceAdded(FidoDiscovery* discovery, FidoDevice* device) = 0;
virtual void DeviceRemoved(FidoDiscovery* discovery,
FidoDevice* device) = 0;
@@ -112,6 +117,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDiscovery {
const FidoTransportProtocol transport_;
State state_ = State::kIdle;
+ base::WeakPtrFactory<FidoDiscovery> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(FidoDiscovery);
};
diff --git a/chromium/device/fido/fido_discovery_unittest.cc b/chromium/device/fido/fido_discovery_unittest.cc
index 51b873e63c4..922c559c080 100644
--- a/chromium/device/fido/fido_discovery_unittest.cc
+++ b/chromium/device/fido/fido_discovery_unittest.cc
@@ -7,6 +7,9 @@
#include <utility>
#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "device/fido/fido_test_data.h"
#include "device/fido/mock_fido_device.h"
#include "device/fido/mock_fido_discovery_observer.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -90,6 +93,7 @@ TEST(FidoDiscoveryTest, TestNotificationsOnFailedStart) {
}
TEST(FidoDiscoveryTest, TestAddRemoveDevices) {
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
ConcreteFidoDiscovery discovery(FidoTransportProtocol::kBluetoothLowEnergy);
MockFidoDiscoveryObserver observer;
discovery.set_observer(&observer);
@@ -98,18 +102,37 @@ TEST(FidoDiscoveryTest, TestAddRemoveDevices) {
// Expect successful insertion.
auto device0 = std::make_unique<MockFidoDevice>();
auto* device0_raw = device0.get();
- EXPECT_CALL(observer, DeviceAdded(&discovery, device0_raw));
+ device0->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo,
+ test_data::kTestAuthenticatorGetInfoResponse);
+ base::RunLoop device0_done;
+ EXPECT_CALL(observer, DeviceAdded(&discovery, device0_raw))
+ .WillOnce(testing::InvokeWithoutArgs(
+ [&device0_done]() { device0_done.Quit(); }));
EXPECT_CALL(*device0, GetId()).WillOnce(Return("device0"));
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.
+ // Expect successful insertion.
+ base::RunLoop device1_done;
auto device1 = std::make_unique<MockFidoDevice>();
auto* device1_raw = device1.get();
- EXPECT_CALL(observer, DeviceAdded(&discovery, device1_raw));
+ device1->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
+ EXPECT_CALL(observer, DeviceAdded(&discovery, device1_raw))
+ .WillOnce(testing::InvokeWithoutArgs(
+ [&device1_done]() { device1_done.Quit(); }));
EXPECT_CALL(*device1, GetId()).WillOnce(Return("device1"));
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_hid_device.cc b/chromium/device/fido/fido_hid_device.cc
index d2104f564b9..5dee08aacc9 100644
--- a/chromium/device/fido/fido_hid_device.cc
+++ b/chromium/device/fido/fido_hid_device.cc
@@ -15,10 +15,6 @@
namespace device {
-namespace switches {
-static constexpr char kEnableU2fHidTest[] = "enable-u2f-hid-tests";
-} // namespace switches
-
namespace {
// U2F devices only provide a single report so specify a report ID of 0 here.
static constexpr uint8_t kReportId = 0x00;
@@ -43,7 +39,12 @@ void FidoHidDevice::Cancel() {
if (state_ != State::kBusy && state_ != State::kReady)
return;
- Transition(std::vector<uint8_t>(), base::DoNothing());
+ // Delete any remaining pending requests on this Channel ID.
+ pending_transactions_ = {};
+ WriteMessage(
+ FidoHidMessage::Create(channel_id_, FidoHidDeviceCommand::kCancel,
+ std::vector<uint8_t>()),
+ false /* response_expected */, base::DoNothing());
}
void FidoHidDevice::Transition(std::vector<uint8_t> command,
@@ -69,18 +70,6 @@ void FidoHidDevice::Transition(std::vector<uint8_t> command,
state_ = State::kBusy;
ArmTimeout(repeating_callback);
- // If cancel command has been received, send HID_CANCEL with no-op
- // callback.
- // TODO(hongjunchoi): Re-factor cancel logic and consolidate it with
- // FidoBleDevice::Cancel().
- if (command.empty()) {
- WriteMessage(
- FidoHidMessage::Create(channel_id_, FidoHidDeviceCommand::kCancel,
- std::move(command)),
- false, base::DoNothing());
- return;
- }
-
// Write message to the device.
const auto command_type = supported_protocol() == ProtocolVersion::kCtap
? FidoHidDeviceCommand::kCbor
@@ -394,12 +383,6 @@ std::string FidoHidDevice::GetIdForDevice(
return "hid:" + device_info.guid;
}
-// static
-bool FidoHidDevice::IsTestEnabled() {
- base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- return command_line->HasSwitch(switches::kEnableU2fHidTest);
-}
-
base::WeakPtr<FidoDevice> FidoHidDevice::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
diff --git a/chromium/device/fido/fido_hid_device.h b/chromium/device/fido/fido_hid_device.h
index f1e4865b50e..10f34023244 100644
--- a/chromium/device/fido/fido_hid_device.h
+++ b/chromium/device/fido/fido_hid_device.h
@@ -5,7 +5,6 @@
#ifndef DEVICE_FIDO_FIDO_HID_DEVICE_H_
#define DEVICE_FIDO_FIDO_HID_DEVICE_H_
-#include <queue>
#include <string>
#include <utility>
#include <vector>
@@ -13,6 +12,7 @@
#include "base/callback.h"
#include "base/cancelable_callback.h"
#include "base/component_export.h"
+#include "base/containers/queue.h"
#include "base/macros.h"
#include "base/optional.h"
#include "components/apdu/apdu_command.h"
@@ -45,13 +45,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoHidDevice : public FidoDevice {
// Get a string identifier for a given device info.
static std::string GetIdForDevice(
const device::mojom::HidDeviceInfo& device_info);
- // Command line flag to enable tests on actual HID hardware.
- static bool IsTestEnabled();
private:
FRIEND_TEST_ALL_PREFIXES(FidoHidDeviceTest, TestConnectionFailure);
FRIEND_TEST_ALL_PREFIXES(FidoHidDeviceTest, TestDeviceError);
FRIEND_TEST_ALL_PREFIXES(FidoHidDeviceTest, TestRetryChannelAllocation);
+ FRIEND_TEST_ALL_PREFIXES(FidoHidDeviceTest, TestCancel);
static constexpr uint8_t kWinkCapability = 0x01;
static constexpr uint8_t kLockCapability = 0x02;
@@ -105,7 +104,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoHidDevice : public FidoDevice {
uint8_t capabilities_ = 0;
base::CancelableOnceClosure timeout_callback_;
- std::queue<std::pair<std::vector<uint8_t>, DeviceCallback>>
+ base::queue<std::pair<std::vector<uint8_t>, DeviceCallback>>
pending_transactions_;
// All the FidoHidDevice instances are owned by U2fRequest. So it is safe to
diff --git a/chromium/device/fido/fido_hid_device_unittest.cc b/chromium/device/fido/fido_hid_device_unittest.cc
index e52e1b5382a..88c86732eb7 100644
--- a/chromium/device/fido/fido_hid_device_unittest.cc
+++ b/chromium/device/fido/fido_hid_device_unittest.cc
@@ -492,7 +492,11 @@ TEST_F(FidoHidDeviceTest, TestCancel) {
device->set_supported_protocol(ProtocolVersion::kCtap);
TestDeviceCallbackReceiver cb;
device->DeviceTransact(GetMockDeviceRequest(), cb.callback());
- device->Cancel();
+ auto delay_before_cancel = base::TimeDelta::FromSeconds(1);
+ auto cancel_callback = base::BindOnce(&FidoHidDevice::Cancel,
+ device->weak_factory_.GetWeakPtr());
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, std::move(cancel_callback), delay_before_cancel);
scoped_task_environment_.FastForwardUntilNoTasksRemain();
}
diff --git a/chromium/device/fido/fido_parsing_utils.cc b/chromium/device/fido/fido_parsing_utils.cc
index 54daf0940e4..a8448dce45c 100644
--- a/chromium/device/fido/fido_parsing_utils.cc
+++ b/chromium/device/fido/fido_parsing_utils.cc
@@ -5,7 +5,6 @@
#include "device/fido/fido_parsing_utils.h"
#include "base/logging.h"
-#include "crypto/sha2.h"
namespace device {
namespace fido_parsing_utils {
@@ -20,8 +19,6 @@ constexpr bool AreSpansDisjoint(base::span<const uint8_t> lhs,
} // namespace
-const uint32_t kU2fResponseKeyHandleLengthPos = 66u;
-const uint32_t kU2fResponseKeyHandleStartPos = 67u;
const char kEs256[] = "ES256";
std::vector<uint8_t> Materialize(base::span<const uint8_t> span) {
@@ -80,11 +77,16 @@ std::vector<base::span<const uint8_t>> SplitSpan(base::span<const uint8_t> span,
return chunks;
}
-std::vector<uint8_t> CreateSHA256Hash(base::StringPiece data) {
- std::vector<uint8_t> hashed_data(crypto::kSHA256Length);
+std::array<uint8_t, crypto::kSHA256Length> CreateSHA256Hash(
+ base::StringPiece data) {
+ std::array<uint8_t, crypto::kSHA256Length> hashed_data;
crypto::SHA256HashString(data, hashed_data.data(), hashed_data.size());
return hashed_data;
}
+base::StringPiece ConvertToStringPiece(base::span<const uint8_t> data) {
+ return {reinterpret_cast<const char*>(data.data()), data.size()};
+}
+
} // 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 ace50c5cd7f..278207baa44 100644
--- a/chromium/device/fido/fido_parsing_utils.h
+++ b/chromium/device/fido/fido_parsing_utils.h
@@ -17,6 +17,7 @@
#include "base/containers/span.h"
#include "base/optional.h"
#include "base/strings/string_piece.h"
+#include "crypto/sha2.h"
namespace device {
namespace fido_parsing_utils {
@@ -50,6 +51,15 @@ COMPONENT_EXPORT(DEVICE_FIDO)
base::Optional<std::vector<uint8_t>> MaterializeOrNull(
base::Optional<base::span<const uint8_t>> span);
+// Returns a materialized copy of the static |span|, that is, an array with the
+// same elements.
+template <size_t N>
+std::array<uint8_t, N> Materialize(base::span<const uint8_t, N> span) {
+ std::array<uint8_t, N> array;
+ std::copy(span.begin(), span.end(), array.begin());
+ return array;
+}
+
// Appends |in_values| to the end of |target|. The underlying container for
// |in_values| should *not* be |target|.
COMPONENT_EXPORT(DEVICE_FIDO)
@@ -98,7 +108,11 @@ std::vector<base::span<const uint8_t>> SplitSpan(base::span<const uint8_t> span,
size_t max_chunk_size);
COMPONENT_EXPORT(DEVICE_FIDO)
-std::vector<uint8_t> CreateSHA256Hash(base::StringPiece data);
+std::array<uint8_t, crypto::kSHA256Length> CreateSHA256Hash(
+ base::StringPiece data);
+
+COMPONENT_EXPORT(DEVICE_FIDO)
+base::StringPiece ConvertToStringPiece(base::span<const uint8_t> data);
} // namespace fido_parsing_utils
} // namespace device
diff --git a/chromium/device/fido/fido_parsing_utils_unittest.cc b/chromium/device/fido/fido_parsing_utils_unittest.cc
index b84cb6d9265..41ff123f595 100644
--- a/chromium/device/fido/fido_parsing_utils_unittest.cc
+++ b/chromium/device/fido/fido_parsing_utils_unittest.cc
@@ -85,9 +85,32 @@ TEST(U2fParsingUtils, Materialize) {
EXPECT_THAT(Materialize(empty), ::testing::IsEmpty());
EXPECT_THAT(Materialize(base::span<const uint8_t>()), ::testing::IsEmpty());
- EXPECT_THAT(Materialize(kOne), ::testing::ElementsAreArray(kOne));
- EXPECT_THAT(Materialize(kOneTwoThree),
+ EXPECT_THAT(Materialize(base::span<const uint8_t>(kOne)),
+ ::testing::ElementsAreArray(kOne));
+ EXPECT_THAT(Materialize(base::span<const uint8_t>(kOneTwoThree)),
::testing::ElementsAreArray(kOneTwoThree));
+
+ static_assert(std::is_same<std::vector<uint8_t>,
+ decltype(Materialize(
+ base::span<const uint8_t>(kOne)))>::value,
+ "Materialize with a dynamic span should yield a std::vector.");
+}
+
+TEST(U2fParsingUtils, StaticMaterialize) {
+ std::array<uint8_t, 0> empty;
+ EXPECT_THAT(Materialize(empty), ::testing::IsEmpty());
+ EXPECT_THAT(Materialize(base::span<const uint8_t, 0>()),
+ ::testing::IsEmpty());
+
+ EXPECT_THAT(Materialize(base::make_span(kOne)),
+ ::testing::ElementsAreArray(kOne));
+ EXPECT_THAT(Materialize(base::make_span(kOneTwoThree)),
+ ::testing::ElementsAreArray(kOneTwoThree));
+
+ static_assert(
+ std::is_same<std::array<uint8_t, 1>,
+ decltype(Materialize(base::make_span(kOne)))>::value,
+ "Materialize with a static span should yield a std::array.");
}
TEST(U2fParsingUtils, MaterializeOrNull) {
@@ -254,5 +277,10 @@ TEST(U2fParsingUtils, CreateSHA256Hash) {
::testing::ElementsAreArray(test_data::kApplicationParameter));
}
+TEST(U2fParsingUtils, ConvertSpanToStringPiece) {
+ constexpr uint8_t kTestAsciiAbcd[] = {'a', 'b', 'c', 'd'};
+ EXPECT_EQ("abcd", ConvertToStringPiece(kTestAsciiAbcd));
+}
+
} // 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 a81327cdd2f..fa2e842ca8d 100644
--- a/chromium/device/fido/fido_request_handler.h
+++ b/chromium/device/fido/fido_request_handler.h
@@ -32,7 +32,18 @@ class FidoRequestHandler : public FidoRequestHandlerBase {
FidoRequestHandler(service_manager::Connector* connector,
const base::flat_set<FidoTransportProtocol>& transports,
CompletionCallback completion_callback)
- : FidoRequestHandlerBase(connector, transports),
+ : FidoRequestHandler(connector,
+ transports,
+ std::move(completion_callback),
+ AddPlatformAuthenticatorCallback()) {}
+ 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)),
completion_callback_(std::move(completion_callback)) {}
~FidoRequestHandler() override {
if (!is_complete())
@@ -87,6 +98,18 @@ class FidoRequestHandler : public FidoRequestHandlerBase {
return FidoReturnCode::kUserConsentButCredentialExcluded;
case CtapDeviceResponseCode::kCtap2ErrNoCredentials:
return FidoReturnCode::kUserConsentButCredentialNotRecognized;
+
+ // 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
+ // possibly asking the user for their PIN) for requests it knows
+ // beforehand it cannot handle.
+ //
+ // Ignore this error to avoid canceling the request without user
+ // interaction.
+ case CtapDeviceResponseCode::kCtap2ErrInvalidCredential:
+ return base::nullopt;
+
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 f3b5b7bf93b..7adbfddd277 100644
--- a/chromium/device/fido/fido_request_handler_base.cc
+++ b/chromium/device/fido/fido_request_handler_base.cc
@@ -6,21 +6,27 @@
#include <utility>
+#include "base/logging.h"
#include "base/strings/string_piece.h"
#include "build/build_config.h"
#include "device/fido/fido_device.h"
#include "device/fido/fido_task.h"
#include "services/service_manager/public/cpp/connector.h"
-#if defined(OS_MACOSX)
-#include "device/fido/mac/authenticator.h"
-#endif
-
namespace device {
FidoRequestHandlerBase::FidoRequestHandlerBase(
service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& transports) {
+ const base::flat_set<FidoTransportProtocol>& transports)
+ : FidoRequestHandlerBase(connector,
+ transports,
+ AddPlatformAuthenticatorCallback()) {}
+
+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) {
// Construction of CaBleDiscovery is handled by the implementing class as it
// requires an extension passed on from the relying party.
@@ -28,7 +34,9 @@ FidoRequestHandlerBase::FidoRequestHandlerBase(
continue;
if (transport == FidoTransportProtocol::kInternal) {
- use_platform_authenticator_ = true;
+ // Internal authenticators are injected through
+ // AddPlatformAuthenticatorCallback.
+ NOTREACHED();
continue;
}
@@ -64,21 +72,18 @@ void FidoRequestHandlerBase::Start() {
for (const auto& discovery : discoveries_) {
discovery->Start();
}
- if (use_platform_authenticator_) {
- MaybeAddPlatformAuthenticator();
- }
+ MaybeAddPlatformAuthenticator();
}
void FidoRequestHandlerBase::MaybeAddPlatformAuthenticator() {
-#if defined(OS_MACOSX)
- if (__builtin_available(macOS 10.12.2, *)) {
- auto authenticator = fido::mac::TouchIdAuthenticator::CreateIfAvailable();
- if (!authenticator) {
- return;
- }
- AddAuthenticator(std::move(authenticator));
+ if (!add_platform_authenticator_) {
+ return;
+ }
+ auto authenticator = std::move(add_platform_authenticator_).Run();
+ if (!authenticator) {
+ return;
}
-#endif
+ AddAuthenticator(std::move(authenticator));
}
void FidoRequestHandlerBase::DiscoveryStarted(FidoDiscovery* discovery,
@@ -87,10 +92,6 @@ void FidoRequestHandlerBase::DiscoveryStarted(FidoDiscovery* discovery,
void FidoRequestHandlerBase::DeviceAdded(FidoDiscovery* discovery,
FidoDevice* device) {
DCHECK(!base::ContainsKey(active_authenticators(), device->GetId()));
- // All devices are initially assumed to support CTAP protocol and thus
- // AuthenticatorGetInfo command is sent to all connected devices. If device
- // errors out, then it is assumed to support U2F protocol.
- device->set_supported_protocol(ProtocolVersion::kCtap);
AddAuthenticator(CreateAuthenticatorFromDevice(device));
}
diff --git a/chromium/device/fido/fido_request_handler_base.h b/chromium/device/fido/fido_request_handler_base.h
index 65e494a7225..6b9b7e3dc49 100644
--- a/chromium/device/fido/fido_request_handler_base.h
+++ b/chromium/device/fido/fido_request_handler_base.h
@@ -11,6 +11,7 @@
#include <string>
#include <vector>
+#include "base/callback.h"
#include "base/component_export.h"
#include "base/containers/flat_set.h"
#include "base/macros.h"
@@ -39,10 +40,18 @@ 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>()>;
+ // TODO(https://crbug.com/769631): Remove the dependency on Connector once
+ // device/fido is servicified.
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);
~FidoRequestHandlerBase() override;
// Triggers cancellation of all per-device FidoTasks, except for the device
@@ -87,11 +96,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase
AuthenticatorMap active_authenticators_;
std::vector<std::unique_ptr<FidoDiscovery>> discoveries_;
- // If set to true at any point before calling Start(), the request handler
- // will try to create a platform authenticator to handle the request
- // (currently only TouchIdAuthenticator on macOS).
- bool use_platform_authenticator_ = false;
-
+ AddPlatformAuthenticatorCallback add_platform_authenticator_;
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 2d5dce3822b..c497d0a7f2c 100644
--- a/chromium/device/fido/fido_request_handler_unittest.cc
+++ b/chromium/device/fido/fido_request_handler_unittest.cc
@@ -96,11 +96,15 @@ class FakeFidoAuthenticator : public FidoDeviceAuthenticator {
class FakeFidoRequestHandler : public FidoRequestHandler<std::vector<uint8_t>> {
public:
- FakeFidoRequestHandler(const base::flat_set<FidoTransportProtocol>& protocols,
- FakeHandlerCallback callback)
+ FakeFidoRequestHandler(
+ const base::flat_set<FidoTransportProtocol>& protocols,
+ FakeHandlerCallback callback,
+ AddPlatformAuthenticatorCallback add_platform_authenticator =
+ AddPlatformAuthenticatorCallback())
: FidoRequestHandler(nullptr /* connector */,
protocols,
- std::move(callback)),
+ std::move(callback),
+ std::move(add_platform_authenticator)),
weak_factory_(this) {
Start();
}
@@ -151,6 +155,15 @@ class FidoRequestHandlerTest : public ::testing::Test {
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));
+ }
+
test::FakeFidoDiscovery* discovery() const { return discovery_; }
FakeHandlerCallbackReceiver& callback() { return cb_; }
@@ -167,6 +180,8 @@ TEST_F(FidoRequestHandlerTest, TestSingleDeviceSuccess) {
discovery()->WaitForCallToStartAndSimulateSuccess();
auto device = std::make_unique<MockFidoDevice>();
+ device->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
// Device returns success response.
device->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
@@ -187,12 +202,16 @@ TEST_F(FidoRequestHandlerTest, TestAuthenticatorHandlerReset) {
discovery()->WaitForCallToStartAndSimulateSuccess();
auto device0 = std::make_unique<MockFidoDevice>();
- device0->set_supported_protocol(ProtocolVersion::kCtap);
+ device0->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo,
+ test_data::kTestAuthenticatorGetInfoResponse);
EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0"));
device0->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
EXPECT_CALL(*device0, Cancel());
auto device1 = std::make_unique<MockFidoDevice>();
- device1->set_supported_protocol(ProtocolVersion::kCtap);
+ device1->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo,
+ test_data::kTestAuthenticatorGetInfoResponse);
EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1"));
device1->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
EXPECT_CALL(*device1, Cancel());
@@ -211,7 +230,9 @@ TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleDevices) {
// Represents a connected device that hangs without a response.
auto device0 = std::make_unique<MockFidoDevice>();
- device0->set_supported_protocol(ProtocolVersion::kCtap);
+ device0->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo,
+ test_data::kTestAuthenticatorGetInfoResponse);
EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0"));
// Device is unresponsive and cancel command is invoked afterwards.
device0->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
@@ -219,7 +240,9 @@ TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleDevices) {
// Represents a connected device that response successfully.
auto device1 = std::make_unique<MockFidoDevice>();
- device1->set_supported_protocol(ProtocolVersion::kCtap);
+ device1->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo,
+ test_data::kTestAuthenticatorGetInfoResponse);
EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1"));
device1->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
CreateFakeSuccessDeviceResponse());
@@ -242,7 +265,9 @@ TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleSuccessResponses) {
// Represents a connected device that responds successfully after small time
// delay.
auto device0 = std::make_unique<MockFidoDevice>();
- device0->set_supported_protocol(ProtocolVersion::kCtap);
+ device0->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo,
+ test_data::kTestAuthenticatorGetInfoResponse);
EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0"));
device0->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
CreateFakeSuccessDeviceResponse(),
@@ -251,7 +276,9 @@ TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleSuccessResponses) {
// Represents a device that returns a success response after a longer time
// delay.
auto device1 = std::make_unique<MockFidoDevice>();
- device1->set_supported_protocol(ProtocolVersion::kCtap);
+ device1->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo,
+ test_data::kTestAuthenticatorGetInfoResponse);
EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1"));
device1->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
CreateFakeSuccessDeviceResponse(),
@@ -281,6 +308,9 @@ TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleFailureResponses) {
// Represents a connected device that immediately responds with a processing
// error.
auto device0 = std::make_unique<MockFidoDevice>();
+ device0->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo,
+ test_data::kTestAuthenticatorGetInfoResponse);
EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0"));
device0->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
CreateFakeDeviceProcesssingError());
@@ -288,6 +318,9 @@ TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleFailureResponses) {
// Represents a device that returns an UP verified failure response after a
// small time delay.
auto device1 = std::make_unique<MockFidoDevice>();
+ device1->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo,
+ test_data::kTestAuthenticatorGetInfoResponse);
EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1"));
device1->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
CreateFakeUserPresenceVerifiedError(),
@@ -296,7 +329,9 @@ TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleFailureResponses) {
// Represents a device that returns an UP verified failure response after a
// big time delay.
auto device2 = std::make_unique<MockFidoDevice>();
- device2->set_supported_protocol(ProtocolVersion::kCtap);
+ device2->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo,
+ test_data::kTestAuthenticatorGetInfoResponse);
EXPECT_CALL(*device2, GetId()).WillRepeatedly(testing::Return("device2"));
device2->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
CreateFakeDeviceProcesssingError(),
@@ -314,4 +349,31 @@ 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) {
+ // 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->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
+ CreateFakeSuccessDeviceResponse());
+
+ 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));
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ callback().WaitForCallback();
+ EXPECT_TRUE(request_handler->is_complete());
+ EXPECT_EQ(FidoReturnCode::kSuccess, callback().status());
+}
+
} // namespace device
diff --git a/chromium/device/fido/fido_task.cc b/chromium/device/fido/fido_task.cc
index 4c3138d970e..80ba6505b22 100644
--- a/chromium/device/fido/fido_task.cc
+++ b/chromium/device/fido/fido_task.cc
@@ -7,15 +7,15 @@
#include <utility>
#include "base/bind.h"
+#include "base/stl_util.h"
#include "base/threading/sequenced_task_runner_handle.h"
-#include "device/fido/ctap_empty_authenticator_request.h"
-#include "device/fido/device_response_converter.h"
#include "device/fido/fido_constants.h"
namespace device {
FidoTask::FidoTask(FidoDevice* device) : device_(device), weak_factory_(this) {
DCHECK(device_);
+ DCHECK(device_->SupportedProtocolIsInitialized());
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&FidoTask::StartTask, weak_factory_.GetWeakPtr()));
@@ -30,31 +30,4 @@ void FidoTask::CancelTask() {
device()->Cancel();
}
-void FidoTask::GetAuthenticatorInfo(base::OnceClosure ctap_callback,
- base::OnceClosure u2f_callback) {
- device()->DeviceTransact(
- AuthenticatorGetInfoRequest().Serialize(),
- base::BindOnce(&FidoTask::OnAuthenticatorInfoReceived,
- weak_factory_.GetWeakPtr(), std::move(ctap_callback),
- std::move(u2f_callback)));
-}
-
-void FidoTask::OnAuthenticatorInfoReceived(
- base::OnceClosure ctap_callback,
- base::OnceClosure u2f_callback,
- base::Optional<std::vector<uint8_t>> response) {
- device()->set_state(FidoDevice::State::kReady);
-
- base::Optional<AuthenticatorGetInfoResponse> get_info_response;
- if (!response || !(get_info_response = ReadCTAPGetInfoResponse(*response))) {
- device()->set_supported_protocol(ProtocolVersion::kU2f);
- std::move(u2f_callback).Run();
- return;
- }
-
- device()->SetDeviceInfo(std::move(*get_info_response));
- device()->set_supported_protocol(ProtocolVersion::kCtap);
- std::move(ctap_callback).Run();
-}
-
} // namespace device
diff --git a/chromium/device/fido/fido_task.h b/chromium/device/fido/fido_task.h
index 773b145b780..b2ca94b87bf 100644
--- a/chromium/device/fido/fido_task.h
+++ b/chromium/device/fido/fido_task.h
@@ -13,7 +13,6 @@
#include "base/component_export.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
-#include "base/optional.h"
#include "device/fido/fido_device.h"
namespace device {
@@ -42,27 +41,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoTask {
// Asynchronously initiates CTAP request operation for a single device.
virtual void StartTask() = 0;
- // Invokes the AuthenticatorGetInfo method on |device_|. If successful and a
- // well formed response is received, then |device_| is deemed to support CTAP
- // protocol and |ctap_callback| is invoked, which sends CBOR encoded command
- // to the authenticator. For all failure cases, |device_| is assumed to
- // support the U2F protocol as FidoDiscovery selects only devices that support
- // either the U2F or CTAP protocols during discovery. Therefore |u2f_callback|
- // is invoked, which sends APDU encoded request to authenticator.
- void GetAuthenticatorInfo(base::OnceClosure ctap_callback,
- base::OnceClosure u2f_callback);
-
FidoDevice* device() const {
DCHECK(device_);
return device_;
}
private:
- void OnAuthenticatorInfoReceived(
- base::OnceClosure ctap_callback,
- base::OnceClosure u2f_callback,
- base::Optional<std::vector<uint8_t>> response);
-
FidoDevice* const device_;
base::WeakPtrFactory<FidoTask> weak_factory_;
diff --git a/chromium/device/fido/fido_test_data.h b/chromium/device/fido/fido_test_data.h
index ba23b8ed792..e36d0da259f 100644
--- a/chromium/device/fido/fido_test_data.h
+++ b/chromium/device/fido/fido_test_data.h
@@ -14,6 +14,8 @@ namespace device {
namespace test_data {
+// U2F request parameters ------------------------------------------------------
+
// Sample U2F register request parameters used in example 6 of the CTAP spec.
// https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html#using-the-ctap2-authenticatormakecredential-command-with-ctap1-u2f-authenticators
constexpr uint8_t kChallengeParameter[] = {
@@ -28,6 +30,12 @@ constexpr uint8_t kApplicationParameter[] = {
0x7B, 0xCF, 0x01, 0x3D, 0xE9, 0x6D, 0x4E, 0xFB, 0x17, 0xDE,
};
+constexpr uint8_t kAlternativeApplicationParameter[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03,
+ 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02,
+ 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04,
+};
+
constexpr uint8_t kClientDataHash[] = {
0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xec, 0x17, 0x20, 0x2e, 0x42,
0x50, 0x5f, 0x8e, 0xd2, 0xb1, 0x6a, 0xe2, 0x2f, 0x16, 0xbb, 0x05,
@@ -35,7 +43,7 @@ constexpr uint8_t kClientDataHash[] = {
constexpr uint8_t kUserId[] = {0x10, 0x98, 0x23, 0x72, 0x35, 0x40, 0x98, 0x72};
-constexpr char kRelyingPartyId[] = "example.com";
+constexpr char kRelyingPartyId[] = "acme.com";
constexpr uint8_t kU2fRegisterCommandApduWithIndividualAttestation[] = {
// CLA, INS, P1, P2 APDU instructions
@@ -275,6 +283,59 @@ constexpr uint8_t kU2fCheckOnlySignCommandApdu[] = {
0x00, 0x00,
};
+constexpr uint8_t kU2fSignCommandApduWithAlternativeApplicationParameter[] = {
+ // CLA, INS, P1, P2 APDU instruction parameters
+ 0x00, 0x02, 0x03, 0x00,
+ // Data Length (3 bytes in big endian order)
+ 0x00, 0x00, 0x81,
+ // Challenge parameter
+ 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,
+ // Alternative application parameter
+ 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04,
+ // Key handle length
+ 0x40,
+ // Key handle
+ 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,
+ // Max response length
+ 0x00, 0x00,
+};
+
+constexpr uint8_t
+ kU2fCheckOnlySignCommandApduWithAlternativeApplicationParameter[] = {
+ // CLA, INS, P1, P2 APDU instruction parameters
+ 0x00, 0x02, 0x07, 0x00,
+ // Data Length (3 bytes in big endian order).
+ 0x00, 0x00, 0x81,
+ // Challenge parameter
+ 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,
+ // Alternative application parameter
+ 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04,
+ // Key handle length
+ 0x40,
+ // Key handle
+ 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,
+ // Max response length
+ 0x00, 0x00,
+};
+
// Encoded U2fSign command excluding key handle length and key handle. Precise
// key handle length and key handle shall be added based on different testing
// scenarios.
@@ -310,6 +371,109 @@ constexpr uint8_t kU2fFakeRegisterCommand[] = {
0x00, 0x00,
};
+// U2F responses ---------------------------------------------------------------
+
+constexpr uint8_t kU2fConditionNotSatisfiedApduResponse[] = {0x69, 0x85};
+
+constexpr uint8_t kU2fWrongDataApduResponse[] = {0x6A, 0x80};
+
+constexpr uint8_t kApduEncodedNoErrorRegisterResponse[] = {
+ // Reserved byte
+ 0x05,
+ // User public key
+ 0x04, 0xE8, 0x76, 0x25, 0x89, 0x6E, 0xE4, 0xE4, 0x6D, 0xC0, 0x32, 0x76,
+ 0x6E, 0x80, 0x87, 0x96, 0x2F, 0x36, 0xDF, 0x9D, 0xFE, 0x8B, 0x56, 0x7F,
+ 0x37, 0x63, 0x01, 0x5B, 0x19, 0x90, 0xA6, 0x0E, 0x14, 0x27, 0xDE, 0x61,
+ 0x2D, 0x66, 0x41, 0x8B, 0xDA, 0x19, 0x50, 0x58, 0x1E, 0xBC, 0x5C, 0x8C,
+ 0x1D, 0xAD, 0x71, 0x0C, 0xB1, 0x4C, 0x22, 0xF8, 0xC9, 0x70, 0x45, 0xF4,
+ 0x61, 0x2F, 0xB2, 0x0C, 0x91,
+ // Key handle length
+ 0x40,
+ // Key handle
+ 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,
+ // X.509 Certificate
+ 0x00, 0x38, 0x30, 0x82, 0x02, 0x4A, 0x30, 0x82, 0x01, 0x32, 0xA0, 0x03,
+ 0x02, 0x01, 0x02, 0x02, 0x04, 0x04, 0x6C, 0x88, 0x22, 0x30, 0x0D, 0x06,
+ 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00,
+ 0x30, 0x2E, 0x31, 0x2C, 0x30, 0x2A, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x23, 0x59, 0x75, 0x62, 0x69, 0x63, 0x6F, 0x20, 0x55, 0x32, 0x46, 0x20,
+ 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x43, 0x41, 0x20, 0x53, 0x65, 0x72, 0x69,
+ 0x61, 0x6C, 0x20, 0x34, 0x35, 0x37, 0x32, 0x30, 0x30, 0x36, 0x33, 0x31,
+ 0x30, 0x20, 0x17, 0x0D, 0x31, 0x34, 0x30, 0x38, 0x30, 0x31, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x5A, 0x18, 0x0F, 0x32, 0x30, 0x35, 0x30, 0x30,
+ 0x39, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x30, 0x2C,
+ 0x31, 0x2A, 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x21, 0x59,
+ 0x75, 0x62, 0x69, 0x63, 0x6F, 0x20, 0x55, 0x32, 0x46, 0x20, 0x45, 0x45,
+ 0x20, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6C, 0x20, 0x32, 0x34, 0x39, 0x31,
+ 0x38, 0x32, 0x33, 0x32, 0x34, 0x37, 0x37, 0x30, 0x30, 0x59, 0x30, 0x13,
+ 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A,
+ 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x3C,
+ 0xCA, 0xB9, 0x2C, 0xCB, 0x97, 0x28, 0x7E, 0xE8, 0xE6, 0x39, 0x43, 0x7E,
+ 0x21, 0xFC, 0xD6, 0xB6, 0xF1, 0x65, 0xB2, 0xD5, 0xA3, 0xF3, 0xDB, 0x13,
+ 0x1D, 0x31, 0xC1, 0x6B, 0x74, 0x2B, 0xB4, 0x76, 0xD8, 0xD1, 0xE9, 0x90,
+ 0x80, 0xEB, 0x54, 0x6C, 0x9B, 0xBD, 0xF5, 0x56, 0xE6, 0x21, 0x0F, 0xD4,
+ 0x27, 0x85, 0x89, 0x9E, 0x78, 0xCC, 0x58, 0x9E, 0xBE, 0x31, 0x0F, 0x6C,
+ 0xDB, 0x9F, 0xF4, 0xA3, 0x3B, 0x30, 0x39, 0x30, 0x22, 0x06, 0x09, 0x2B,
+ 0x06, 0x01, 0x04, 0x01, 0x82, 0xC4, 0x0A, 0x02, 0x04, 0x15, 0x31, 0x2E,
+ 0x33, 0x2E, 0x36, 0x2E, 0x31, 0x2E, 0x34, 0x2E, 0x31, 0x2E, 0x34, 0x31,
+ 0x34, 0x38, 0x32, 0x2E, 0x31, 0x2E, 0x32, 0x30, 0x13, 0x06, 0x0B, 0x2B,
+ 0x06, 0x01, 0x04, 0x01, 0x82, 0xE5, 0x1C, 0x02, 0x01, 0x01, 0x04, 0x04,
+ 0x03, 0x02, 0x04, 0x30, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
+ 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+ 0x9F, 0x9B, 0x05, 0x22, 0x48, 0xBC, 0x4C, 0xF4, 0x2C, 0xC5, 0x99, 0x1F,
+ 0xCA, 0xAB, 0xAC, 0x9B, 0x65, 0x1B, 0xBE, 0x5B, 0xDC, 0xDC, 0x8E, 0xF0,
+ 0xAD, 0x2C, 0x1C, 0x1F, 0xFB, 0x36, 0xD1, 0x87, 0x15, 0xD4, 0x2E, 0x78,
+ 0xB2, 0x49, 0x22, 0x4F, 0x92, 0xC7, 0xE6, 0xE7, 0xA0, 0x5C, 0x49, 0xF0,
+ 0xE7, 0xE4, 0xC8, 0x81, 0xBF, 0x2E, 0x94, 0xF4, 0x5E, 0x4A, 0x21, 0x83,
+ 0x3D, 0x74, 0x56, 0x85, 0x1D, 0x0F, 0x6C, 0x14, 0x5A, 0x29, 0x54, 0x0C,
+ 0x87, 0x4F, 0x30, 0x92, 0xC9, 0x34, 0xB4, 0x3D, 0x22, 0x2B, 0x89, 0x62,
+ 0xC0, 0xF4, 0x10, 0xCE, 0xF1, 0xDB, 0x75, 0x89, 0x2A, 0xF1, 0x16, 0xB4,
+ 0x4A, 0x96, 0xF5, 0xD3, 0x5A, 0xDE, 0xA3, 0x82, 0x2F, 0xC7, 0x14, 0x6F,
+ 0x60, 0x04, 0x38, 0x5B, 0xCB, 0x69, 0xB6, 0x5C, 0x99, 0xE7, 0xEB, 0x69,
+ 0x19, 0x78, 0x67, 0x03, 0xC0, 0xD8, 0xCD, 0x41, 0xE8, 0xF7, 0x5C, 0xCA,
+ 0x44, 0xAA, 0x8A, 0xB7, 0x25, 0xAD, 0x8E, 0x79, 0x9F, 0xF3, 0xA8, 0x69,
+ 0x6A, 0x6F, 0x1B, 0x26, 0x56, 0xE6, 0x31, 0xB1, 0xE4, 0x01, 0x83, 0xC0,
+ 0x8F, 0xDA, 0x53, 0xFA, 0x4A, 0x8F, 0x85, 0xA0, 0x56, 0x93, 0x94, 0x4A,
+ 0xE1, 0x79, 0xA1, 0x33, 0x9D, 0x00, 0x2D, 0x15, 0xCA, 0xBD, 0x81, 0x00,
+ 0x90, 0xEC, 0x72, 0x2E, 0xF5, 0xDE, 0xF9, 0x96, 0x5A, 0x37, 0x1D, 0x41,
+ 0x5D, 0x62, 0x4B, 0x68, 0xA2, 0x70, 0x7C, 0xAD, 0x97, 0xBC, 0xDD, 0x17,
+ 0x85, 0xAF, 0x97, 0xE2, 0x58, 0xF3, 0x3D, 0xF5, 0x6A, 0x03, 0x1A, 0xA0,
+ 0x35, 0x6D, 0x8E, 0x8D, 0x5E, 0xBC, 0xAD, 0xC7, 0x4E, 0x07, 0x16, 0x36,
+ 0xC6, 0xB1, 0x10, 0xAC, 0xE5, 0xCC, 0x9B, 0x90, 0xDF, 0xEA, 0xCA, 0xE6,
+ 0x40, 0xFF, 0x1B, 0xB0, 0xF1, 0xFE, 0x5D, 0xB4, 0xEF, 0xF7, 0xA9, 0x5F,
+ 0x06, 0x07, 0x33, 0xF5,
+ // Signature
+ 0x30, 0x45, 0x02, 0x20, 0x32, 0x47, 0x79, 0xC6, 0x8F, 0x33, 0x80, 0x28,
+ 0x8A, 0x11, 0x97, 0xB6, 0x09, 0x5F, 0x7A, 0x6E, 0xB9, 0xB1, 0xB1, 0xC1,
+ 0x27, 0xF6, 0x6A, 0xE1, 0x2A, 0x99, 0xFE, 0x85, 0x32, 0xEC, 0x23, 0xB9,
+ 0x02, 0x21, 0x00, 0xE3, 0x95, 0x16, 0xAC, 0x4D, 0x61, 0xEE, 0x64, 0x04,
+ 0x4D, 0x50, 0xB4, 0x15, 0xA6, 0xA4, 0xD4, 0xD8, 0x4B, 0xA6, 0xD8, 0x95,
+ 0xCB, 0x5A, 0xB7, 0xA1, 0xAA, 0x7D, 0x08, 0x1D, 0xE3, 0x41, 0xFA,
+ // APDU Status bytes
+ 0x90, 0x00,
+};
+
+constexpr uint8_t kApduEncodedNoErrorSignResponse[] = {
+ // User presence
+ 0x01,
+ // Sign count(4 bytes)
+ 0x00, 0x00, 0x00, 0x3B,
+ // Signature
+ 0x30, 0x44, 0x02, 0x20, 0x7B, 0xDE, 0x0A, 0x52, 0xAC, 0x1F, 0x4C, 0x8B,
+ 0x27, 0xE0, 0x03, 0xA3, 0x70, 0xCD, 0x66, 0xA4, 0xC7, 0x11, 0x8D, 0xD2,
+ 0x2D, 0x54, 0x47, 0x83, 0x5F, 0x45, 0xB9, 0x9C, 0x68, 0x42, 0x3F, 0xF7,
+ 0x02, 0x20, 0x3C, 0x51, 0x7B, 0x47, 0x87, 0x7F, 0x85, 0x78, 0x2D, 0xE1,
+ 0x00, 0x86, 0xA7, 0x83, 0xD1, 0xE7, 0xDF, 0x4E, 0x36, 0x39, 0xE7, 0x71,
+ 0xF5, 0xF6, 0xAF, 0xA3, 0x5A, 0xAD, 0x53, 0x73, 0x85, 0x8E,
+ // APDU Status bytes
+ 0x90, 0x00,
+};
+
// U2F response blob produced by a U2F registration request used in example 6
// of the CTAP spec.
// https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html
@@ -520,6 +684,115 @@ constexpr uint8_t kU2fAttestationStatementCBOR[] = {
0x33, 0xF5,
};
+// Like kU2fAttestationStatementCBOR but in 'packed' format.
+constexpr uint8_t kPackedAttestationStatementCBOR[] = {
+ // Map(3)
+ 0xA3,
+ // Text(3)
+ 0x63,
+ // "alg"
+ 0x61, 0x6C, 0x67,
+ // COSEAlgorithmIdentifier "ES256" (-7)
+ 0x26,
+ // Text(3)
+ 0x63,
+ // "sig"
+ 0x73, 0x69, 0x67,
+ // Bytes(71)
+ 0x58, 0x47,
+ // Byte array content
+ 0x30, 0x45, 0x02, 0x20, 0x32, 0x47, 0x79, 0xC6, 0x8F, 0x33, 0x80, 0x28,
+ 0x8A, 0x11, 0x97, 0xB6, 0x09, 0x5F, 0x7A, 0x6E, 0xB9, 0xB1, 0xB1, 0xC1,
+ 0x27, 0xF6, 0x6A, 0xE1, 0x2A, 0x99, 0xFE, 0x85, 0x32, 0xEC, 0x23, 0xB9,
+ 0x02, 0x21, 0x00, 0xE3, 0x95, 0x16, 0xAC, 0x4D, 0x61, 0xEE, 0x64, 0x04,
+ 0x4D, 0x50, 0xB4, 0x15, 0xA6, 0xA4, 0xD4, 0xD8, 0x4B, 0xA6, 0xD8, 0x95,
+ 0xCB, 0x5A, 0xB7, 0xA1, 0xAA, 0x7D, 0x08, 0x1D, 0xE3, 0x41, 0xFA,
+ // Text(3)
+ 0x63,
+ // "x5c"
+ 0x78, 0x35, 0x63,
+ // Array(1)
+ 0x81,
+ // Bytes(590)
+ 0x59, 0x02, 0x4E,
+ // Byte array content
+ 0x30, 0x82, 0x02, 0x4A, 0x30, 0x82, 0x01, 0x32, 0xA0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x04, 0x6C, 0x88, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A,
+ 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x30, 0x2E,
+ 0x31, 0x2C, 0x30, 0x2A, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x23, 0x59,
+ 0x75, 0x62, 0x69, 0x63, 0x6F, 0x20, 0x55, 0x32, 0x46, 0x20, 0x52, 0x6F,
+ 0x6F, 0x74, 0x20, 0x43, 0x41, 0x20, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6C,
+ 0x20, 0x34, 0x35, 0x37, 0x32, 0x30, 0x30, 0x36, 0x33, 0x31, 0x30, 0x20,
+ 0x17, 0x0D, 0x31, 0x34, 0x30, 0x38, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5A, 0x18, 0x0F, 0x32, 0x30, 0x35, 0x30, 0x30, 0x39, 0x30,
+ 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x30, 0x2C, 0x31, 0x2A,
+ 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x21, 0x59, 0x75, 0x62,
+ 0x69, 0x63, 0x6F, 0x20, 0x55, 0x32, 0x46, 0x20, 0x45, 0x45, 0x20, 0x53,
+ 0x65, 0x72, 0x69, 0x61, 0x6C, 0x20, 0x32, 0x34, 0x39, 0x31, 0x38, 0x32,
+ 0x33, 0x32, 0x34, 0x37, 0x37, 0x30, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
+ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48,
+ 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x3C, 0xCA, 0xB9,
+ 0x2C, 0xCB, 0x97, 0x28, 0x7E, 0xE8, 0xE6, 0x39, 0x43, 0x7E, 0x21, 0xFC,
+ 0xD6, 0xB6, 0xF1, 0x65, 0xB2, 0xD5, 0xA3, 0xF3, 0xDB, 0x13, 0x1D, 0x31,
+ 0xC1, 0x6B, 0x74, 0x2B, 0xB4, 0x76, 0xD8, 0xD1, 0xE9, 0x90, 0x80, 0xEB,
+ 0x54, 0x6C, 0x9B, 0xBD, 0xF5, 0x56, 0xE6, 0x21, 0x0F, 0xD4, 0x27, 0x85,
+ 0x89, 0x9E, 0x78, 0xCC, 0x58, 0x9E, 0xBE, 0x31, 0x0F, 0x6C, 0xDB, 0x9F,
+ 0xF4, 0xA3, 0x3B, 0x30, 0x39, 0x30, 0x22, 0x06, 0x09, 0x2B, 0x06, 0x01,
+ 0x04, 0x01, 0x82, 0xC4, 0x0A, 0x02, 0x04, 0x15, 0x31, 0x2E, 0x33, 0x2E,
+ 0x36, 0x2E, 0x31, 0x2E, 0x34, 0x2E, 0x31, 0x2E, 0x34, 0x31, 0x34, 0x38,
+ 0x32, 0x2E, 0x31, 0x2E, 0x32, 0x30, 0x13, 0x06, 0x0B, 0x2B, 0x06, 0x01,
+ 0x04, 0x01, 0x82, 0xE5, 0x1C, 0x02, 0x01, 0x01, 0x04, 0x04, 0x03, 0x02,
+ 0x04, 0x30, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D,
+ 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x9F, 0x9B,
+ 0x05, 0x22, 0x48, 0xBC, 0x4C, 0xF4, 0x2C, 0xC5, 0x99, 0x1F, 0xCA, 0xAB,
+ 0xAC, 0x9B, 0x65, 0x1B, 0xBE, 0x5B, 0xDC, 0xDC, 0x8E, 0xF0, 0xAD, 0x2C,
+ 0x1C, 0x1F, 0xFB, 0x36, 0xD1, 0x87, 0x15, 0xD4, 0x2E, 0x78, 0xB2, 0x49,
+ 0x22, 0x4F, 0x92, 0xC7, 0xE6, 0xE7, 0xA0, 0x5C, 0x49, 0xF0, 0xE7, 0xE4,
+ 0xC8, 0x81, 0xBF, 0x2E, 0x94, 0xF4, 0x5E, 0x4A, 0x21, 0x83, 0x3D, 0x74,
+ 0x56, 0x85, 0x1D, 0x0F, 0x6C, 0x14, 0x5A, 0x29, 0x54, 0x0C, 0x87, 0x4F,
+ 0x30, 0x92, 0xC9, 0x34, 0xB4, 0x3D, 0x22, 0x2B, 0x89, 0x62, 0xC0, 0xF4,
+ 0x10, 0xCE, 0xF1, 0xDB, 0x75, 0x89, 0x2A, 0xF1, 0x16, 0xB4, 0x4A, 0x96,
+ 0xF5, 0xD3, 0x5A, 0xDE, 0xA3, 0x82, 0x2F, 0xC7, 0x14, 0x6F, 0x60, 0x04,
+ 0x38, 0x5B, 0xCB, 0x69, 0xB6, 0x5C, 0x99, 0xE7, 0xEB, 0x69, 0x19, 0x78,
+ 0x67, 0x03, 0xC0, 0xD8, 0xCD, 0x41, 0xE8, 0xF7, 0x5C, 0xCA, 0x44, 0xAA,
+ 0x8A, 0xB7, 0x25, 0xAD, 0x8E, 0x79, 0x9F, 0xF3, 0xA8, 0x69, 0x6A, 0x6F,
+ 0x1B, 0x26, 0x56, 0xE6, 0x31, 0xB1, 0xE4, 0x01, 0x83, 0xC0, 0x8F, 0xDA,
+ 0x53, 0xFA, 0x4A, 0x8F, 0x85, 0xA0, 0x56, 0x93, 0x94, 0x4A, 0xE1, 0x79,
+ 0xA1, 0x33, 0x9D, 0x00, 0x2D, 0x15, 0xCA, 0xBD, 0x81, 0x00, 0x90, 0xEC,
+ 0x72, 0x2E, 0xF5, 0xDE, 0xF9, 0x96, 0x5A, 0x37, 0x1D, 0x41, 0x5D, 0x62,
+ 0x4B, 0x68, 0xA2, 0x70, 0x7C, 0xAD, 0x97, 0xBC, 0xDD, 0x17, 0x85, 0xAF,
+ 0x97, 0xE2, 0x58, 0xF3, 0x3D, 0xF5, 0x6A, 0x03, 0x1A, 0xA0, 0x35, 0x6D,
+ 0x8E, 0x8D, 0x5E, 0xBC, 0xAD, 0xC7, 0x4E, 0x07, 0x16, 0x36, 0xC6, 0xB1,
+ 0x10, 0xAC, 0xE5, 0xCC, 0x9B, 0x90, 0xDF, 0xEA, 0xCA, 0xE6, 0x40, 0xFF,
+ 0x1B, 0xB0, 0xF1, 0xFE, 0x5D, 0xB4, 0xEF, 0xF7, 0xA9, 0x5F, 0x06, 0x07,
+ 0x33, 0xF5,
+};
+
+// Like kPackedAttestationStatementCBOR but certs are omitted.
+constexpr uint8_t kPackedAttestationStatementCBORNoCerts[] = {
+ // Map(2)
+ 0xA2,
+ // Text(3)
+ 0x63,
+ // "alg"
+ 0x61, 0x6C, 0x67,
+ // COSEAlgorithmIdentifier "ES256" (-7)
+ 0x26,
+ // Text(3)
+ 0x63,
+ // "sig"
+ 0x73, 0x69, 0x67,
+ // Bytes(71)
+ 0x58, 0x47,
+ // Byte array content
+ 0x30, 0x45, 0x02, 0x20, 0x32, 0x47, 0x79, 0xC6, 0x8F, 0x33, 0x80, 0x28,
+ 0x8A, 0x11, 0x97, 0xB6, 0x09, 0x5F, 0x7A, 0x6E, 0xB9, 0xB1, 0xB1, 0xC1,
+ 0x27, 0xF6, 0x6A, 0xE1, 0x2A, 0x99, 0xFE, 0x85, 0x32, 0xEC, 0x23, 0xB9,
+ 0x02, 0x21, 0x00, 0xE3, 0x95, 0x16, 0xAC, 0x4D, 0x61, 0xEE, 0x64, 0x04,
+ 0x4D, 0x50, 0xB4, 0x15, 0xA6, 0xA4, 0xD4, 0xD8, 0x4B, 0xA6, 0xD8, 0x95,
+ 0xCB, 0x5A, 0xB7, 0xA1, 0xAA, 0x7D, 0x08, 0x1D, 0xE3, 0x41, 0xFA,
+};
+
// U2F response blob produced by a U2F sign request used in example 7 of the
// CTAP spec.
// https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html#using-the-ctap2-authenticatorgetassertion-command-with-ctap1-u2f-authenticators
@@ -554,8 +827,159 @@ constexpr uint8_t kTestSignAuthenticatorData[] = {
0x00, 0x00, 0x00, 0x3B,
};
-// A sample corrupted response to a U2F sign request.
-constexpr uint8_t kTestCorruptedU2fSignResponse[] = {0x01, 0x00, 0x00, 0x00};
+// A sample APDU encoded response to a U2F sign request that contains NO_ERROR
+// status but has a corrupted data.
+constexpr uint8_t kTestCorruptedU2fSignResponse[] = {0x01, 0x00, 0x00,
+ 0x00, 0x90, 0x00};
+
+// CTAP requests ---------------------------------------------------------------
+constexpr uint8_t kCtapMakeCredentialRequest[] = {
+ // authenticatorMakeCredential command
+ 0x01,
+ // map(5)
+ 0xa5,
+ // key(1) - clientDataHash
+ 0x01,
+ // 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(2) - rp
+ 0x02,
+ // map(2)
+ 0xa2,
+ // key - "id"
+ 0x62, 0x69, 0x64,
+ // value - "acme.com"
+ 0x68, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ // key - "name"
+ 0x64, 0x6e, 0x61, 0x6d, 0x65,
+ // value - "Acme"
+ 0x64, 0x41, 0x63, 0x6d, 0x65,
+ // key(3) - user
+ 0x03,
+ // map(4)
+ 0xa4,
+ // key - "id"
+ 0x62, 0x69, 0x64,
+ // value - user id
+ 0x48, 0x10, 0x98, 0x23, 0x72, 0x35, 0x40, 0x98, 0x72,
+ // key - "icon"
+ 0x64, 0x69, 0x63, 0x6f, 0x6e,
+ // value - "https://pics.acme.com/00/p/aBjjjpqPb.png"
+ 0x78, 0x28, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x70, 0x69,
+ 0x63, 0x73, 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x30, 0x30, 0x2f, 0x70, 0x2f, 0x61, 0x42, 0x6a, 0x6a, 0x6a, 0x70, 0x71,
+ 0x50, 0x62, 0x2e, 0x70, 0x6e, 0x67,
+ // key - "name"
+ 0x64, 0x6e, 0x61, 0x6d, 0x65,
+ // value - "johnpsmith@example.com"
+ 0x76, 0x6a, 0x6f, 0x68, 0x6e, 0x70, 0x73, 0x6d, 0x69, 0x74, 0x68, 0x40,
+ 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ // key - "displayName"
+ 0x6b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65,
+ // value - "John P. Smith"
+ 0x6d, 0x4a, 0x6f, 0x68, 0x6e, 0x20, 0x50, 0x2e, 0x20, 0x53, 0x6d, 0x69,
+ 0x74, 0x68,
+ // key(4) - pubKeyCredParams
+ 0x04,
+ // array(2)
+ 0x82,
+ // map(2)
+ 0xa2,
+ // key - "alg"
+ 0x63, 0x61, 0x6c, 0x67,
+ // value - 7
+ 0x07,
+ // key - "type"
+ 0x64, 0x74, 0x79, 0x70, 0x65,
+ // value - "public-key"
+ 0x6a, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79,
+ // map(2)
+ 0xa2,
+ // key - "alg"
+ 0x63, 0x61, 0x6c, 0x67,
+ // value - 257
+ 0x19, 0x01, 0x01,
+ // key - "type"
+ 0x64, 0x74, 0x79, 0x70, 0x65, // "type"
+ // value - "public-key"
+ 0x6a, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79,
+ // key(7) - options
+ 0x07,
+ // map(2)
+ 0xa2,
+ // key - "rk"
+ 0x62, 0x72, 0x6b,
+ // True(21)
+ 0xf5,
+ // key - "uv"
+ 0x62, 0x75, 0x76,
+ // True(21)
+ 0xf5};
+
+constexpr uint8_t kCtapGetAssertionRequest[] = {
+ // 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(2)
+ 0x82,
+ // map(2)
+ 0xa2,
+ // key - "id"
+ 0x62, 0x69, 0x64,
+ // value - credential ID
+ 0x58, 0x40, 0xf2, 0x20, 0x06, 0xde, 0x4f, 0x90, 0x5a, 0xf6, 0x8a, 0x43,
+ 0x94, 0x2f, 0x02, 0x4f, 0x2a, 0x5e, 0xce, 0x60, 0x3d, 0x9c, 0x6d, 0x4b,
+ 0x3d, 0xf8, 0xbe, 0x08, 0xed, 0x01, 0xfc, 0x44, 0x26, 0x46, 0xd0, 0x34,
+ 0x85, 0x8a, 0xc7, 0x5b, 0xed, 0x3f, 0xd5, 0x80, 0xbf, 0x98, 0x08, 0xd9,
+ 0x4f, 0xcb, 0xee, 0x82, 0xb9, 0xb2, 0xef, 0x66, 0x77, 0xaf, 0x0a, 0xdc,
+ 0xc3, 0x58, 0x52, 0xea, 0x6b, 0x9e,
+ // key - "type"
+ 0x64, 0x74, 0x79, 0x70, 0x65,
+ // value - "public-key"
+ 0x6a, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79,
+ // map(2)
+ 0xa2,
+ // key - "id"
+ 0x62, 0x69, 0x64,
+ // value - credential ID
+ 0x58, 0x32, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03,
+ // 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(2)
+ 0xa2,
+ // key -"up"
+ 0x62, 0x75, 0x70,
+ // value - False(20)
+ 0xf4,
+ // key - "uv"
+ 0x62, 0x75, 0x76,
+ // value - True(21)
+ 0xf5};
+
+// CTAP responses --------------------------------------------------------------
// A sample well formed response to CTAP AuthenticatorGetInfo request. Supports
// platform device, resident key, and user verification.
@@ -621,22 +1045,51 @@ constexpr uint8_t kTestGetInfoResponseCrossPlatformDevice[] = {
0x19, 0x04, 0xB0, 0x06, 0x81, 0x01,
};
+// AuthenticatorGetInfo request with all configurations equal to that of
+// kTestAuthenticatorGetInfoResponse except clientPin option is set to true.
+constexpr uint8_t kTestGetInfoResponseWithClientPinSet[] = {
+ 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,
+ 0x6C, 0x61, 0x74, 0xF5,
+ // clientPin : true
+ 0x69, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x75,
+ // End of clientPin setting.
+ 0x50, 0x69, 0x6E, 0xF4, 0x05, 0x19, 0x04, 0xB0, 0x06, 0x81, 0x01,
+};
+
// A Sample well formed response to CTAP MakeCredential request.
constexpr uint8_t kTestMakeCredentialResponse[] = {
- 0x00, 0xa3, 0x01, 0x66, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x02, 0x58,
- 0x9a, 0x11, 0x94, 0x22, 0x8d, 0xa8, 0xfd, 0xbd, 0xee, 0xfd, 0x26, 0x1b,
- 0xd7, 0xb6, 0x59, 0x5c, 0xfd, 0x70, 0xa5, 0x0d, 0x70, 0xc6, 0x40, 0x7b,
- 0xcf, 0x01, 0x3d, 0xe9, 0x6d, 0x4e, 0xfb, 0x17, 0xde, 0x41, 0x00, 0x00,
- 0x00, 0x0b, 0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06,
- 0x17, 0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x00, 0x10, 0x89, 0x59, 0xce, 0xad,
- 0x5b, 0x5c, 0x48, 0x16, 0x4e, 0x8a, 0xbc, 0xd6, 0xd9, 0x43, 0x5c, 0x6f,
- 0xa3, 0x63, 0x61, 0x6c, 0x67, 0x65, 0x45, 0x53, 0x32, 0x35, 0x36, 0x61,
- 0x78, 0x58, 0x20, 0xf7, 0xc4, 0xf4, 0xa6, 0xf1, 0xd7, 0x95, 0x38, 0xdf,
- 0xa4, 0xc9, 0xac, 0x50, 0x84, 0x8d, 0xf7, 0x08, 0xbc, 0x1c, 0x99, 0xf5,
- 0xe6, 0x0e, 0x51, 0xb4, 0x2a, 0x52, 0x1b, 0x35, 0xd3, 0xb6, 0x9a, 0x61,
- 0x79, 0x58, 0x20, 0xde, 0x7b, 0x7d, 0x6c, 0xa5, 0x64, 0xe7, 0x0e, 0xa3,
- 0x21, 0xa4, 0xd5, 0xd9, 0x6e, 0xa0, 0x0e, 0xf0, 0xe2, 0xdb, 0x89, 0xdd,
- 0x61, 0xd4, 0x89, 0x4c, 0x15, 0xac, 0x58, 0x5b, 0xd2, 0x36, 0x84, 0x03,
+ // Success status byte
+ 0x00,
+ // Map(03)
+ 0xa3,
+ // key(01) - Format
+ 0x01,
+ // "packed"
+ 0x66, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64,
+ // key(02) - Authenticator Data
+ 0x02,
+ // Byte(154)
+ 0x58, 0x9a, 0x11, 0x94, 0x22, 0x8d, 0xa8, 0xfd, 0xbd, 0xee, 0xfd, 0x26,
+ 0x1b, 0xd7, 0xb6, 0x59, 0x5c, 0xfd, 0x70, 0xa5, 0x0d, 0x70, 0xc6, 0x40,
+ 0x7b, 0xcf, 0x01, 0x3d, 0xe9, 0x6d, 0x4e, 0xfb, 0x17, 0xde, 0x41, 0x00,
+ 0x00, 0x00, 0x0b, 0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80,
+ 0x06, 0x17, 0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x00, 0x10, 0x89, 0x59, 0xce,
+ 0xad, 0x5b, 0x5c, 0x48, 0x16, 0x4e, 0x8a, 0xbc, 0xd6, 0xd9, 0x43, 0x5c,
+ 0x6f, 0xa3, 0x63, 0x61, 0x6c, 0x67, 0x65, 0x45, 0x53, 0x32, 0x35, 0x36,
+ 0x61, 0x78, 0x58, 0x20, 0xf7, 0xc4, 0xf4, 0xa6, 0xf1, 0xd7, 0x95, 0x38,
+ 0xdf, 0xa4, 0xc9, 0xac, 0x50, 0x84, 0x8d, 0xf7, 0x08, 0xbc, 0x1c, 0x99,
+ 0xf5, 0xe6, 0x0e, 0x51, 0xb4, 0x2a, 0x52, 0x1b, 0x35, 0xd3, 0xb6, 0x9a,
+ 0x61, 0x79, 0x58, 0x20, 0xde, 0x7b, 0x7d, 0x6c, 0xa5, 0x64, 0xe7, 0x0e,
+ 0xa3, 0x21, 0xa4, 0xd5, 0xd9, 0x6e, 0xa0, 0x0e, 0xf0, 0xe2, 0xdb, 0x89,
+ 0xdd, 0x61, 0xd4, 0x89, 0x4c, 0x15, 0xac, 0x58, 0x5b, 0xd2, 0x36, 0x84,
+ // Key(03) - Attestation object
+ 0x03,
+ // Map - Attestation object
0xa3, 0x63, 0x61, 0x6c, 0x67, 0x26, 0x63, 0x73, 0x69, 0x67, 0x58, 0x47,
0x30, 0x45, 0x02, 0x20, 0x13, 0xf7, 0x3c, 0x5d, 0x9d, 0x53, 0x0e, 0x8c,
0xc1, 0x5c, 0xc9, 0xbd, 0x96, 0xad, 0x58, 0x6d, 0x39, 0x36, 0x64, 0xe4,
@@ -837,9 +1290,9 @@ constexpr uint8_t kCtap2MakeCredentialCertificate[] = {
0xc5, 0xd3, 0x43, 0xcb, 0x2f, 0x11, 0x3d, 0xa2, 0x37, 0x23, 0xf3};
constexpr uint8_t kCtap2MakeCredentialAuthData[] = {
- 0xc2, 0x89, 0xc5, 0xca, 0x9b, 0x04, 0x60, 0xf9, 0x34, 0x6a, 0xb4, 0xe4,
- 0x2d, 0x84, 0x27, 0x43, 0x40, 0x4d, 0x31, 0xf4, 0x84, 0x68, 0x25, 0xa6,
- 0xd0, 0x65, 0xbe, 0x59, 0x7a, 0x87, 0x05, 0x1d, 0x41, 0x00, 0x00, 0x00,
+ 0x11, 0x94, 0x22, 0x8D, 0xA8, 0xFD, 0xBD, 0xEE, 0xFD, 0x26, 0x1B, 0xD7,
+ 0xB6, 0x59, 0x5C, 0xFD, 0x70, 0xA5, 0x0D, 0x70, 0xC6, 0x40, 0x7B, 0xCF,
+ 0x01, 0x3D, 0xE9, 0x6D, 0x4E, 0xFB, 0x17, 0xDE, 0x41, 0x00, 0x00, 0x00,
0x0b, 0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17,
0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x00, 0x10, 0x89, 0x59, 0xce, 0xad, 0x5b,
0x5c, 0x48, 0x16, 0x4e, 0x8a, 0xbc, 0xd6, 0xd9, 0x43, 0x5c, 0x6f, 0xa3,
@@ -888,9 +1341,9 @@ constexpr uint8_t kNoneAttestationResponse[] = {
// bytes(154)
0x58, 0x9a,
// byte data
- 0xc2, 0x89, 0xc5, 0xca, 0x9b, 0x04, 0x60, 0xf9, 0x34, 0x6a, 0xb4, 0xe4,
- 0x2d, 0x84, 0x27, 0x43, 0x40, 0x4d, 0x31, 0xf4, 0x84, 0x68, 0x25, 0xa6,
- 0xd0, 0x65, 0xbe, 0x59, 0x7a, 0x87, 0x05, 0x1d, 0x41, 0x00, 0x00, 0x00,
+ 0x11, 0x94, 0x22, 0x8D, 0xA8, 0xFD, 0xBD, 0xEE, 0xFD, 0x26, 0x1B, 0xD7,
+ 0xB6, 0x59, 0x5C, 0xFD, 0x70, 0xA5, 0x0D, 0x70, 0xC6, 0x40, 0x7B, 0xCF,
+ 0x01, 0x3D, 0xE9, 0x6D, 0x4E, 0xFB, 0x17, 0xDE, 0x41, 0x00, 0x00, 0x00,
0x0b,
// Replaced device AAGUID
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -907,102 +1360,6 @@ constexpr uint8_t kNoneAttestationResponse[] = {
0x58, 0x5b, 0xd2, 0x36, 0x84,
};
-const uint8_t kDeviceMakeCredentialResponse[] = {
- // Success response code
- 0x00,
- // map(3)
- 0xa3,
- // unsigned(1)
- 0x01,
- // text(6)
- 0x66,
- // "packed"
- 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64,
- // unsigned(2)
- 0x02,
- // bytes(154)
- 0x58, 0x9a,
- // auth data
- 0xc2, 0x89, 0xc5, 0xca, 0x9b, 0x04, 0x60, 0xf9, 0x34, 0x6a, 0xb4, 0xe4,
- 0x2d, 0x84, 0x27, 0x43, 0x40, 0x4d, 0x31, 0xf4, 0x84, 0x68, 0x25, 0xa6,
- 0xd0, 0x65, 0xbe, 0x59, 0x7a, 0x87, 0x05, 0x1d, 0x41, 0x00, 0x00, 0x00,
- 0x0b, 0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17,
- 0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x00, 0x10, 0x89, 0x59, 0xce, 0xad, 0x5b,
- 0x5c, 0x48, 0x16, 0x4e, 0x8a, 0xbc, 0xd6, 0xd9, 0x43, 0x5c, 0x6f, 0xa3,
- 0x63, 0x61, 0x6c, 0x67, 0x65, 0x45, 0x53, 0x32, 0x35, 0x36, 0x61, 0x78,
- 0x58, 0x20, 0xf7, 0xc4, 0xf4, 0xa6, 0xf1, 0xd7, 0x95, 0x38, 0xdf, 0xa4,
- 0xc9, 0xac, 0x50, 0x84, 0x8d, 0xf7, 0x08, 0xbc, 0x1c, 0x99, 0xf5, 0xe6,
- 0x0e, 0x51, 0xb4, 0x2a, 0x52, 0x1b, 0x35, 0xd3, 0xb6, 0x9a, 0x61, 0x79,
- 0x58, 0x20, 0xde, 0x7b, 0x7d, 0x6c, 0xa5, 0x64, 0xe7, 0x0e, 0xa3, 0x21,
- 0xa4, 0xd5, 0xd9, 0x6e, 0xa0, 0x0e, 0xf0, 0xe2, 0xdb, 0x89, 0xdd, 0x61,
- 0xd4, 0x89, 0x4c, 0x15, 0xac, 0x58, 0x5b, 0xd2, 0x36, 0x84,
- // unsigned(3)
- 0x03,
- // map(3)
- 0xa3,
- // text(3)
- 0x63,
- // "alg"
- 0x61, 0x6c, 0x67,
- // 7
- 0x07,
- // text(3)
- 0x63,
- // "sig"
- 0x73, 0x69, 0x67,
- // bytes(71)
- 0x58, 0x47,
- // signature
- 0x30, 0x45, 0x02, 0x20, 0x13, 0xf7, 0x3c, 0x5d, 0x9d, 0x53, 0x0e, 0x8c,
- 0xc1, 0x5c, 0xc9, 0xbd, 0x96, 0xad, 0x58, 0x6d, 0x39, 0x36, 0x64, 0xe4,
- 0x62, 0xd5, 0xf0, 0x56, 0x12, 0x35, 0xe6, 0x35, 0x0f, 0x2b, 0x72, 0x89,
- 0x02, 0x21, 0x00, 0x90, 0x35, 0x7f, 0xf9, 0x10, 0xcc, 0xb5, 0x6a, 0xc5,
- 0xb5, 0x96, 0x51, 0x19, 0x48, 0x58, 0x1c, 0x8f, 0xdd, 0xb4, 0xa2, 0xb7,
- 0x99, 0x59, 0x94, 0x80, 0x78, 0xb0, 0x9f, 0x4b, 0xdc, 0x62, 0x29,
- // text(3)
- 0x63,
- // "x5c"
- 0x78, 0x35, 0x63,
- // array(1)
- 0x81,
- // bytes(407)
- 0x59, 0x01, 0x97,
- // certificate
- 0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02, 0x01,
- 0x02, 0x02, 0x09, 0x00, 0x85, 0x9b, 0x72, 0x6c, 0xb2, 0x4b, 0x4c, 0x29,
- 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02,
- 0x30, 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
- 0x02, 0x55, 0x53, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a,
- 0x0c, 0x0b, 0x59, 0x75, 0x62, 0x69, 0x63, 0x6f, 0x20, 0x54, 0x65, 0x73,
- 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x19,
- 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x6f,
- 0x72, 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f,
- 0x6e, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x32, 0x30, 0x34, 0x31,
- 0x31, 0x35, 0x35, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x36, 0x31, 0x32,
- 0x30, 0x32, 0x31, 0x31, 0x35, 0x35, 0x30, 0x30, 0x5a, 0x30, 0x47, 0x31,
- 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
- 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b, 0x59,
- 0x75, 0x62, 0x69, 0x63, 0x6f, 0x20, 0x54, 0x65, 0x73, 0x74, 0x31, 0x22,
- 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x19, 0x41, 0x75, 0x74,
- 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x41,
- 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x59,
- 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06,
- 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00,
- 0x04, 0xad, 0x11, 0xeb, 0x0e, 0x88, 0x52, 0xe5, 0x3a, 0xd5, 0xdf, 0xed,
- 0x86, 0xb4, 0x1e, 0x61, 0x34, 0xa1, 0x8e, 0xc4, 0xe1, 0xaf, 0x8f, 0x22,
- 0x1a, 0x3c, 0x7d, 0x6e, 0x63, 0x6c, 0x80, 0xea, 0x13, 0xc3, 0xd5, 0x04,
- 0xff, 0x2e, 0x76, 0x21, 0x1b, 0xb4, 0x45, 0x25, 0xb1, 0x96, 0xc4, 0x4c,
- 0xb4, 0x84, 0x99, 0x79, 0xcf, 0x6f, 0x89, 0x6e, 0xcd, 0x2b, 0xb8, 0x60,
- 0xde, 0x1b, 0xf4, 0x37, 0x6b, 0xa3, 0x0d, 0x30, 0x0b, 0x30, 0x09, 0x06,
- 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0a, 0x06, 0x08,
- 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x49, 0x00, 0x30,
- 0x46, 0x02, 0x21, 0x00, 0xe9, 0xa3, 0x9f, 0x1b, 0x03, 0x19, 0x75, 0x25,
- 0xf7, 0x37, 0x3e, 0x10, 0xce, 0x77, 0xe7, 0x80, 0x21, 0x73, 0x1b, 0x94,
- 0xd0, 0xc0, 0x3f, 0x3f, 0xda, 0x1f, 0xd2, 0x2d, 0xb3, 0xd0, 0x30, 0xe7,
- 0x02, 0x21, 0x00, 0xc4, 0xfa, 0xec, 0x34, 0x45, 0xa8, 0x20, 0xcf, 0x43,
- 0x12, 0x9c, 0xdb, 0x00, 0xaa, 0xbe, 0xfd, 0x9a, 0xe2, 0xd8, 0x74, 0xf9,
- 0xc5, 0xd3, 0x43, 0xcb, 0x2f, 0x11, 0x3d, 0xa2, 0x37, 0x23, 0xf3};
-
constexpr uint8_t kCtap2GetAssertionAuthData[] = {
0x62, 0x5d, 0xda, 0xdf, 0x74, 0x3f, 0x57, 0x27, 0xe6, 0x6b,
0xba, 0x8c, 0x2e, 0x38, 0x79, 0x22, 0xd1, 0xaf, 0x43, 0xc5,
@@ -1117,6 +1474,18 @@ constexpr uint8_t kDeviceGetAssertionResponse[] = {
0x01,
};
+constexpr uint8_t kX962UncompressedPublicKey[] = {
+ 0x04,
+ // X-coordinate
+ 0xE8, 0x76, 0x25, 0x89, 0x6E, 0xE4, 0xE4, 0x6D, 0xC0, 0x32, 0x76, 0x6E,
+ 0x80, 0x87, 0x96, 0x2F, 0x36, 0xDF, 0x9D, 0xFE, 0x8B, 0x56, 0x7F, 0x37,
+ 0x63, 0x01, 0x5B, 0x19, 0x90, 0xA6, 0x0E, 0x14,
+ // Y-coordinate
+ 0x27, 0xDE, 0x61, 0x2D, 0x66, 0x41, 0x8B, 0xDA, 0x19, 0x50, 0x58, 0x1E,
+ 0xBC, 0x5C, 0x8C, 0x1D, 0xAD, 0x71, 0x0C, 0xB1, 0x4C, 0x22, 0xF8, 0xC9,
+ 0x70, 0x45, 0xF4, 0x61, 0x2F, 0xB2, 0x0C, 0x91,
+};
+
} // namespace test_data
} // namespace device
diff --git a/chromium/device/fido/get_assertion_handler_unittest.cc b/chromium/device/fido/get_assertion_handler_unittest.cc
index 7dba2d677be..ed8267e85f3 100644
--- a/chromium/device/fido/get_assertion_handler_unittest.cc
+++ b/chromium/device/fido/get_assertion_handler_unittest.cc
@@ -5,7 +5,9 @@
#include <memory>
#include <utility>
+#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
+#include "device/base/features.h"
#include "device/fido/authenticator_get_assertion_response.h"
#include "device/fido/ctap_get_assertion_request.h"
#include "device/fido/fake_fido_discovery.h"
@@ -23,9 +25,6 @@ namespace device {
namespace {
-constexpr uint8_t kClientDataHash[] = {0x01, 0x02, 0x03};
-constexpr char kRpId[] = "acme.com";
-
using TestGetAssertionRequestCallback = test::StatusAndValueCallbackReceiver<
FidoReturnCode,
base::Optional<AuthenticatorGetAssertionResponse>>;
@@ -39,24 +38,31 @@ class FidoGetAssertionHandlerTest : public ::testing::Test {
}
std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandler() {
- ForgeNextHidDiscovery();
-
- CtapGetAssertionRequest request_param(
- kRpId, fido_parsing_utils::Materialize(kClientDataHash));
- request_param.SetAllowList(
+ CtapGetAssertionRequest request(test_data::kRelyingPartyId,
+ test_data::kClientDataHash);
+ request.SetAllowList(
{{CredentialType::kPublicKey,
- fido_parsing_utils::Materialize(
- test_data::kTestGetAssertionCredentialId)}});
+ fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
+
+ return CreateGetAssertionHandlerWithRequest(std::move(request));
+ }
+
+ std::unique_ptr<GetAssertionRequestHandler>
+ CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest request) {
+ ForgeNextHidDiscovery();
return std::make_unique<GetAssertionRequestHandler>(
nullptr /* connector */,
base::flat_set<FidoTransportProtocol>(
{FidoTransportProtocol::kUsbHumanInterfaceDevice}),
- std::move(request_param), get_assertion_cb_.callback());
+ std::move(request), get_assertion_cb_.callback());
}
- test::FakeFidoDiscovery* discovery() const { return discovery_; }
+ void InitFeatureListAndDisableCtapFlag() {
+ scoped_feature_list_.InitAndDisableFeature(kNewCtap2Device);
+ }
+ test::FakeFidoDiscovery* discovery() const { return discovery_; }
TestGetAssertionRequestCallback& get_assertion_callback() {
return get_assertion_cb_;
}
@@ -64,6 +70,7 @@ class FidoGetAssertionHandlerTest : public ::testing::Test {
protected:
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_;
TestGetAssertionRequestCallback get_assertion_cb_;
@@ -90,20 +97,93 @@ TEST_F(FidoGetAssertionHandlerTest, TestGetAssertionRequestOnSingleDevice) {
EXPECT_TRUE(request_handler->is_complete());
}
-// Test a scenario where the connected authenticator is a U2F device. Request
-// be silently dropped and request should remain in incomplete state.
-TEST_F(FidoGetAssertionHandlerTest, TestGetAssertionIncorrectGetInfoResponse) {
+// Test a scenario where the connected authenticator is a U2F device.
+TEST_F(FidoGetAssertionHandlerTest, TestU2fSign) {
+ auto request_handler = CreateGetAssertionHandler();
+ discovery()->WaitForCallToStartAndSimulateSuccess();
+
+ auto device = std::make_unique<MockFidoDevice>();
+ EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
+ device->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fCheckOnlySignCommandApdu,
+ test_data::kApduEncodedNoErrorSignResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fSignCommandApdu,
+ test_data::kApduEncodedNoErrorSignResponse);
+
+ 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(request_handler->is_complete());
+}
+
+// Test a scenario where the connected authenticator is a U2F device and
+// "WebAuthenticationCtap2" flag is not enabled.
+TEST_F(FidoGetAssertionHandlerTest, TestU2fSignWithoutCtapFlag) {
+ InitFeatureListAndDisableCtapFlag();
auto request_handler = CreateGetAssertionHandler();
discovery()->WaitForCallToStartAndSimulateSuccess();
auto device = std::make_unique<MockFidoDevice>();
EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fCheckOnlySignCommandApdu,
+ test_data::kApduEncodedNoErrorSignResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fSignCommandApdu,
+ test_data::kApduEncodedNoErrorSignResponse);
+
+ 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(request_handler->is_complete());
+}
+
+TEST_F(FidoGetAssertionHandlerTest, TestIncompatibleUserVerificationSetting) {
+ auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
+ test_data::kClientDataHash);
+ request.SetUserVerification(UserVerificationRequirement::kRequired);
+ auto request_handler =
+ CreateGetAssertionHandlerWithRequest(std::move(request));
+ 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));
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(get_assertion_callback().was_called());
+}
+
+TEST_F(FidoGetAssertionHandlerTest,
+ TestU2fSignRequestWithUserVerificationRequired) {
+ auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
+ test_data::kClientDataHash);
+ request.SetAllowList(
+ {{CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
+ request.SetUserVerification(UserVerificationRequirement::kRequired);
+ auto request_handler =
+ CreateGetAssertionHandlerWithRequest(std::move(request));
+ discovery()->WaitForCallToStartAndSimulateSuccess();
+
+ auto device = std::make_unique<MockFidoDevice>();
+ EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
discovery()->AddDevice(std::move(device));
+
scoped_task_environment_.FastForwardUntilNoTasksRemain();
- EXPECT_FALSE(request_handler->is_complete());
+ EXPECT_FALSE(get_assertion_callback().was_called());
}
} // namespace device
diff --git a/chromium/device/fido/get_assertion_request_handler.cc b/chromium/device/fido/get_assertion_request_handler.cc
index 722deacae64..dcf5aa04b6b 100644
--- a/chromium/device/fido/get_assertion_request_handler.cc
+++ b/chromium/device/fido/get_assertion_request_handler.cc
@@ -19,7 +19,22 @@ GetAssertionRequestHandler::GetAssertionRequestHandler(
const base::flat_set<FidoTransportProtocol>& protocols,
CtapGetAssertionRequest request,
SignResponseCallback completion_callback)
- : FidoRequestHandler(connector, protocols, std::move(completion_callback)),
+ : GetAssertionRequestHandler(connector,
+ protocols,
+ std::move(request),
+ std::move(completion_callback),
+ AddPlatformAuthenticatorCallback()) {}
+
+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(
@@ -36,10 +51,54 @@ GetAssertionRequestHandler::GetAssertionRequestHandler(
GetAssertionRequestHandler::~GetAssertionRequestHandler() = default;
+namespace {
+
+// Checks UserVerificationRequirement enum passed from the relying party is
+// compatible with the authenticator, and updates the request to the
+// "effective" user verification requirement.
+// https://w3c.github.io/webauthn/#effective-user-verification-requirement-for-assertion
+bool CheckUserVerificationCompatible(FidoAuthenticator* authenticator,
+ CtapGetAssertionRequest* request) {
+ const auto uv_availability =
+ authenticator->Options().user_verification_availability();
+
+ switch (request->user_verification()) {
+ case UserVerificationRequirement::kRequired:
+ return uv_availability ==
+ AuthenticatorSupportedOptions::UserVerificationAvailability::
+ kSupportedAndConfigured;
+
+ case UserVerificationRequirement::kDiscouraged:
+ return true;
+
+ case UserVerificationRequirement::kPreferred:
+ if (uv_availability ==
+ AuthenticatorSupportedOptions::UserVerificationAvailability::
+ kSupportedAndConfigured) {
+ request->SetUserVerification(UserVerificationRequirement::kRequired);
+ } else {
+ request->SetUserVerification(UserVerificationRequirement::kDiscouraged);
+ }
+ return true;
+ }
+
+ NOTREACHED();
+ return false;
+}
+
+} // namespace
+
void GetAssertionRequestHandler::DispatchRequest(
FidoAuthenticator* authenticator) {
+ // The user verification field of the request may be adjusted to the
+ // authenticator, so we need to make a copy.
+ CtapGetAssertionRequest request_copy = request_;
+ if (!CheckUserVerificationCompatible(authenticator, &request_copy)) {
+ return;
+ }
+
authenticator->GetAssertion(
- request_,
+ std::move(request_copy),
base::BindOnce(&GetAssertionRequestHandler::OnAuthenticatorResponse,
weak_factory_.GetWeakPtr(), authenticator));
}
diff --git a/chromium/device/fido/get_assertion_request_handler.h b/chromium/device/fido/get_assertion_request_handler.h
index 4f318334895..594a89c1657 100644
--- a/chromium/device/fido/get_assertion_request_handler.h
+++ b/chromium/device/fido/get_assertion_request_handler.h
@@ -37,6 +37,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) GetAssertionRequestHandler
const base::flat_set<FidoTransportProtocol>& protocols,
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:
diff --git a/chromium/device/fido/get_assertion_task.cc b/chromium/device/fido/get_assertion_task.cc
index c6ae9b880c6..e381b64aa27 100644
--- a/chromium/device/fido/get_assertion_task.cc
+++ b/chromium/device/fido/get_assertion_task.cc
@@ -8,9 +8,13 @@
#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 {
@@ -39,32 +43,34 @@ GetAssertionTask::GetAssertionTask(FidoDevice* device,
GetAssertionTask::~GetAssertionTask() = default;
void GetAssertionTask::StartTask() {
- GetAuthenticatorInfo(
- base::BindOnce(&GetAssertionTask::GetAssertion,
- weak_factory_.GetWeakPtr()),
- base::BindOnce(&GetAssertionTask::U2fSign, weak_factory_.GetWeakPtr()));
+ if (base::FeatureList::IsEnabled(kNewCtap2Device) &&
+ device()->supported_protocol() == ProtocolVersion::kCtap) {
+ GetAssertion();
+ } else {
+ U2fSign();
+ }
}
void GetAssertionTask::GetAssertion() {
- if (!CheckUserVerificationCompatible()) {
- std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
- base::nullopt);
- return;
- }
-
- device()->DeviceTransact(
- request_.EncodeAsCBOR(),
- base::BindOnce(&GetAssertionTask::OnCtapGetAssertionResponseReceived,
- weak_factory_.GetWeakPtr()));
+ sign_operation_ =
+ std::make_unique<Ctap2DeviceOperation<CtapGetAssertionRequest,
+ AuthenticatorGetAssertionResponse>>(
+ device(), request_,
+ base::BindOnce(&GetAssertionTask::OnCtapGetAssertionResponseReceived,
+ weak_factory_.GetWeakPtr()),
+ base::BindOnce(&ReadCTAPGetAssertionResponse));
+ sign_operation_->Start();
}
void GetAssertionTask::U2fSign() {
- // TODO(hongjunchoi): Implement U2F sign request logic to support
- // interoperability with U2F protocol. Currently all requests for U2F devices
- // are silently dropped.
- // See: https://www.crbug.com/798573
- std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
- base::nullopt);
+ 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_->Start();
}
bool GetAssertionTask::CheckRequirementsOnReturnedUserEntities(
@@ -107,58 +113,24 @@ bool GetAssertionTask::CheckRequirementsOnReturnedCredentialId(
}
void GetAssertionTask::OnCtapGetAssertionResponseReceived(
- base::Optional<std::vector<uint8_t>> device_response) {
- if (!device_response) {
- std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
- base::nullopt);
- return;
- }
-
- auto response_code = GetResponseCode(*device_response);
+ CtapDeviceResponseCode response_code,
+ base::Optional<AuthenticatorGetAssertionResponse> device_response) {
if (response_code != CtapDeviceResponseCode::kSuccess) {
std::move(callback_).Run(response_code, base::nullopt);
return;
}
- auto parsed_response = ReadCTAPGetAssertionResponse(*device_response);
- if (!parsed_response || !parsed_response->CheckRpIdHash(request_.rp_id()) ||
- !CheckRequirementsOnReturnedCredentialId(*parsed_response) ||
- !CheckRequirementsOnReturnedUserEntities(*parsed_response)) {
+ // TODO(martinkr): CheckRpIdHash invocation needs to move into the Request
+ // handler. See https://crbug.com/863988.
+ if (!device_response || !device_response->CheckRpIdHash(request_.rp_id()) ||
+ !CheckRequirementsOnReturnedCredentialId(*device_response) ||
+ !CheckRequirementsOnReturnedUserEntities(*device_response)) {
std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
base::nullopt);
return;
}
- std::move(callback_).Run(response_code, std::move(parsed_response));
-}
-
-bool GetAssertionTask::CheckUserVerificationCompatible() {
- DCHECK(device()->device_info());
- const auto uv_availability =
- device()->device_info()->options().user_verification_availability();
-
- switch (request_.user_verification()) {
- case UserVerificationRequirement::kRequired:
- return uv_availability ==
- AuthenticatorSupportedOptions::UserVerificationAvailability::
- kSupportedAndConfigured;
-
- case UserVerificationRequirement::kDiscouraged:
- return true;
-
- case UserVerificationRequirement::kPreferred:
- if (uv_availability ==
- AuthenticatorSupportedOptions::UserVerificationAvailability::
- kSupportedAndConfigured) {
- request_.SetUserVerification(UserVerificationRequirement::kRequired);
- } else {
- request_.SetUserVerification(UserVerificationRequirement::kDiscouraged);
- }
- return true;
- }
-
- NOTREACHED();
- return false;
+ 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 3f90a21b8e7..b993d84c3f6 100644
--- a/chromium/device/fido/get_assertion_task.h
+++ b/chromium/device/fido/get_assertion_task.h
@@ -7,6 +7,7 @@
#include <stdint.h>
+#include <memory>
#include <vector>
#include "base/callback.h"
@@ -15,6 +16,7 @@
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "device/fido/ctap_get_assertion_request.h"
+#include "device/fido/device_operation.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_task.h"
@@ -29,6 +31,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) GetAssertionTask : public FidoTask {
using GetAssertionTaskCallback = base::OnceCallback<void(
CtapDeviceResponseCode,
base::Optional<AuthenticatorGetAssertionResponse>)>;
+ using SignOperation = DeviceOperation<CtapGetAssertionRequest,
+ AuthenticatorGetAssertionResponse>;
GetAssertionTask(FidoDevice* device,
CtapGetAssertionRequest request,
@@ -63,21 +67,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) GetAssertionTask : public FidoTask {
bool CheckRequirementsOnReturnedCredentialId(
const AuthenticatorGetAssertionResponse& response);
- // Checks UserVerificationRequirement enum passed from the relying party
- // is compatible with the authenticator using the following logic:
- // - If UserVerificationRequirement is set to kRequired, user verification
- // option parameter should be set to true.
- // - If UserVerificationRequirement is set to kPreferred, user verification
- // option is set to true only if the authenticator supports UV.
- // - If UserVerificationRequirement is set to kDiscouraged, user verification
- // is set to false.
- // https://w3c.github.io/webauthn/#enumdef-userverificationrequirement
- bool CheckUserVerificationCompatible();
-
void OnCtapGetAssertionResponseReceived(
- base::Optional<std::vector<uint8_t>> device_response);
+ CtapDeviceResponseCode response_code,
+ base::Optional<AuthenticatorGetAssertionResponse> device_response);
CtapGetAssertionRequest request_;
+ std::unique_ptr<SignOperation> sign_operation_;
GetAssertionTaskCallback callback_;
base::WeakPtrFactory<GetAssertionTask> weak_factory_;
diff --git a/chromium/device/fido/get_assertion_task_unittest.cc b/chromium/device/fido/get_assertion_task_unittest.cc
index d126d035b7c..db9183c8e7b 100644
--- a/chromium/device/fido/get_assertion_task_unittest.cc
+++ b/chromium/device/fido/get_assertion_task_unittest.cc
@@ -6,59 +6,64 @@
#include <iterator>
#include <memory>
+#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
+#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
+#include "crypto/ec_private_key.h"
+#include "device/base/features.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/fido_constants.h"
#include "device/fido/fido_parsing_utils.h"
#include "device/fido/fido_test_data.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"
using ::testing::_;
namespace device {
-
namespace {
-constexpr uint8_t kClientDataHash[] = {0x01, 0x02, 0x03};
-constexpr char kRpId[] = "acme.com";
-
using TestGetAssertionTaskCallbackReceiver =
::device::test::StatusAndValueCallbackReceiver<
CtapDeviceResponseCode,
base::Optional<AuthenticatorGetAssertionResponse>>;
-} // namespace
-
class FidoGetAssertionTaskTest : public testing::Test {
public:
+ FidoGetAssertionTaskTest() { scoped_feature_list_.emplace(); }
+
TestGetAssertionTaskCallbackReceiver& get_assertion_callback_receiver() {
return cb_;
}
+ void RemoveCtapFlag() {
+ scoped_feature_list_.emplace();
+ scoped_feature_list_->InitAndDisableFeature(kNewCtap2Device);
+ }
+
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
+ base::Optional<base::test::ScopedFeatureList> scoped_feature_list_;
TestGetAssertionTaskCallbackReceiver cb_;
};
TEST_F(FidoGetAssertionTaskTest, TestGetAssertionSuccess) {
- auto device = std::make_unique<MockFidoDevice>();
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
+ auto device = MockFidoDevice::MakeCtap();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetAssertion,
test_data::kTestGetAssertionResponse);
- CtapGetAssertionRequest request_param(
- kRpId, fido_parsing_utils::Materialize(kClientDataHash));
+ CtapGetAssertionRequest request_param(test_data::kRelyingPartyId,
+ test_data::kClientDataHash);
request_param.SetAllowList({{CredentialType::kPublicKey,
fido_parsing_utils::Materialize(
test_data::kTestGetAssertionCredentialId)}});
@@ -71,129 +76,200 @@ TEST_F(FidoGetAssertionTaskTest, TestGetAssertionSuccess) {
EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
get_assertion_callback_receiver().status());
EXPECT_TRUE(get_assertion_callback_receiver().value());
- EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kCtap);
- EXPECT_TRUE(device->device_info());
+}
+
+TEST_F(FidoGetAssertionTaskTest, TestU2fSignSuccess) {
+ auto device = MockFidoDevice::MakeU2f();
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fCheckOnlySignCommandApdu,
+ test_data::kApduEncodedNoErrorSignResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fSignCommandApdu,
+ test_data::kApduEncodedNoErrorSignResponse);
+
+ CtapGetAssertionRequest request_param(test_data::kRelyingPartyId,
+ test_data::kClientDataHash);
+ request_param.SetAllowList(
+ {{CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
+
+ auto task = std::make_unique<GetAssertionTask>(
+ device.get(), std::move(request_param),
+ get_assertion_callback_receiver().callback());
+
+ get_assertion_callback_receiver().WaitForCallback();
+ EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
+ get_assertion_callback_receiver().status());
+ EXPECT_TRUE(get_assertion_callback_receiver().value());
+}
+
+TEST_F(FidoGetAssertionTaskTest, TestSignSuccessWithFake) {
+ auto private_key = crypto::ECPrivateKey::Create();
+ std::string public_key;
+ private_key->ExportRawPublicKey(&public_key);
+ auto hash = fido_parsing_utils::CreateSHA256Hash(public_key);
+ std::vector<uint8_t> key_handle(hash.begin(), hash.end());
+ CtapGetAssertionRequest request_param(test_data::kRelyingPartyId,
+ test_data::kClientDataHash);
+ request_param.SetAllowList({{CredentialType::kPublicKey, key_handle}});
+
+ auto device = std::make_unique<VirtualCtap2Device>();
+ device->mutable_state()->registrations.emplace(
+ key_handle,
+ VirtualFidoDevice::RegistrationData(
+ std::move(private_key),
+ fido_parsing_utils::CreateSHA256Hash(test_data::kRelyingPartyId),
+ 42 /* counter */));
+ test::TestCallbackReceiver<> done;
+ device->DiscoverSupportedProtocolAndDeviceInfo(done.callback());
+ done.WaitForCallback();
+
+ auto task = std::make_unique<GetAssertionTask>(
+ device.get(), std::move(request_param),
+ get_assertion_callback_receiver().callback());
+
+ get_assertion_callback_receiver().WaitForCallback();
+ EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
+ get_assertion_callback_receiver().status());
+
+ // Just a sanity check, we don't verify the actual signature.
+ ASSERT_GE(32u + 1u + 4u + 8u, // Minimal ECDSA signature is 8 bytes
+ get_assertion_callback_receiver()
+ .value()
+ ->auth_data()
+ .SerializeToByteArray()
+ .size());
+ EXPECT_EQ(0x01,
+ get_assertion_callback_receiver()
+ .value()
+ ->auth_data()
+ .SerializeToByteArray()[32]); // UP flag
+ // Counter is incremented for every sign request.
+ EXPECT_EQ(43, get_assertion_callback_receiver()
+ .value()
+ ->auth_data()
+ .SerializeToByteArray()[36]); // counter
+}
+
+TEST_F(FidoGetAssertionTaskTest, TestU2fSignWithoutFlag) {
+ RemoveCtapFlag();
+ auto device = MockFidoDevice::MakeU2f();
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fCheckOnlySignCommandApdu,
+ test_data::kApduEncodedNoErrorSignResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fSignCommandApdu,
+ test_data::kApduEncodedNoErrorSignResponse);
+
+ CtapGetAssertionRequest request_param(test_data::kRelyingPartyId,
+ test_data::kClientDataHash);
+ request_param.SetAllowList(
+ {{CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
+
+ auto task = std::make_unique<GetAssertionTask>(
+ device.get(), std::move(request_param),
+ get_assertion_callback_receiver().callback());
+
+ get_assertion_callback_receiver().WaitForCallback();
+ EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
+ get_assertion_callback_receiver().status());
+ 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) {
- auto device = std::make_unique<MockFidoDevice>();
-
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
+ auto device = MockFidoDevice::MakeCtap();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetAssertion,
test_data::kTestGetAssertionResponse);
auto task = std::make_unique<GetAssertionTask>(
device.get(),
- CtapGetAssertionRequest(kRpId,
- fido_parsing_utils::Materialize(kClientDataHash)),
+ CtapGetAssertionRequest(test_data::kRelyingPartyId,
+ test_data::kClientDataHash),
get_assertion_callback_receiver().callback());
get_assertion_callback_receiver().WaitForCallback();
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
get_assertion_callback_receiver().status());
EXPECT_FALSE(get_assertion_callback_receiver().value());
- EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kCtap);
- EXPECT_TRUE(device->device_info());
}
// 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 = std::make_unique<MockFidoDevice>();
-
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
+ auto device = MockFidoDevice::MakeCtap();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetAssertion,
test_data::kTestGetAssertionResponse);
auto task = std::make_unique<GetAssertionTask>(
device.get(),
- CtapGetAssertionRequest(kRpId,
- fido_parsing_utils::Materialize(kClientDataHash)),
+ CtapGetAssertionRequest(test_data::kRelyingPartyId,
+ test_data::kClientDataHash),
get_assertion_callback_receiver().callback());
get_assertion_callback_receiver().WaitForCallback();
- EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kCtap);
- EXPECT_TRUE(device->device_info());
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
get_assertion_callback_receiver().status());
EXPECT_FALSE(get_assertion_callback_receiver().value());
}
TEST_F(FidoGetAssertionTaskTest, TestGetAsserionIncorrectRpIdHash) {
- auto device = std::make_unique<MockFidoDevice>();
-
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
+ auto device = MockFidoDevice::MakeCtap();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetAssertion,
test_data::kTestGetAssertionResponseWithIncorrectRpIdHash);
auto task = std::make_unique<GetAssertionTask>(
device.get(),
- CtapGetAssertionRequest(kRpId,
- fido_parsing_utils::Materialize(kClientDataHash)),
+ CtapGetAssertionRequest(test_data::kRelyingPartyId,
+ test_data::kClientDataHash),
get_assertion_callback_receiver().callback());
get_assertion_callback_receiver().WaitForCallback();
- EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kCtap);
- EXPECT_TRUE(device->device_info());
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
get_assertion_callback_receiver().status());
EXPECT_FALSE(get_assertion_callback_receiver().value());
}
TEST_F(FidoGetAssertionTaskTest, TestIncorrectGetAssertionResponse) {
- auto device = std::make_unique<MockFidoDevice>();
-
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
+ auto device = MockFidoDevice::MakeCtap();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetAssertion, base::nullopt);
auto task = std::make_unique<GetAssertionTask>(
device.get(),
- CtapGetAssertionRequest(kRpId,
- fido_parsing_utils::Materialize(kClientDataHash)),
+ CtapGetAssertionRequest(test_data::kRelyingPartyId,
+ test_data::kClientDataHash),
get_assertion_callback_receiver().callback());
get_assertion_callback_receiver().WaitForCallback();
- EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kCtap);
- EXPECT_TRUE(device->device_info());
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
get_assertion_callback_receiver().status());
EXPECT_FALSE(get_assertion_callback_receiver().value());
}
-TEST_F(FidoGetAssertionTaskTest, TestIncompatibleUserVerificationSetting) {
- auto device = std::make_unique<MockFidoDevice>();
-
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestGetInfoResponseWithoutUvSupport);
+TEST_F(FidoGetAssertionTaskTest, TestU2fSignRequestWithEmptyAllowedList) {
+ auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
+ test_data::kClientDataHash);
- auto request = CtapGetAssertionRequest(
- kRpId, fido_parsing_utils::Materialize(kClientDataHash));
- request.SetUserVerification(UserVerificationRequirement::kRequired);
+ auto device = MockFidoDevice::MakeU2f();
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fFakeRegisterCommand,
+ test_data::kApduEncodedNoErrorSignResponse);
auto task = std::make_unique<GetAssertionTask>(
device.get(), std::move(request),
get_assertion_callback_receiver().callback());
get_assertion_callback_receiver().WaitForCallback();
- EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kCtap);
- EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
+ EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrNoCredentials,
get_assertion_callback_receiver().status());
EXPECT_FALSE(get_assertion_callback_receiver().value());
}
+} // namespace
} // namespace device
diff --git a/chromium/device/fido/mac/authenticator.h b/chromium/device/fido/mac/authenticator.h
index bedc1e08373..5030f64508a 100644
--- a/chromium/device/fido/mac/authenticator.h
+++ b/chromium/device/fido/mac/authenticator.h
@@ -5,6 +5,7 @@
#ifndef DEVICE_FIDO_MAC_AUTHENTICATOR_H_
#define DEVICE_FIDO_MAC_AUTHENTICATOR_H_
+#include "base/component_export.h"
#include "base/mac/availability.h"
#include "base/macros.h"
#include "base/strings/string_piece_forward.h"
@@ -15,45 +16,48 @@ namespace device {
namespace fido {
namespace mac {
-class API_AVAILABLE(macosx(10.12.2)) TouchIdAuthenticator
+class COMPONENT_EXPORT(DEVICE_FIDO) TouchIdAuthenticator
: public FidoAuthenticator {
public:
- // IsAvailable returns true iff Touch ID is enabled and enrolled on the
+ // IsAvailable returns whether Touch ID is available and enrolled on the
// current device.
static bool IsAvailable();
// CreateIfAvailable returns a TouchIdAuthenticator if IsAvailable() returns
// true and nullptr otherwise.
- static std::unique_ptr<TouchIdAuthenticator> CreateIfAvailable();
+ static std::unique_ptr<TouchIdAuthenticator> CreateIfAvailable(
+ std::string keychain_access_group,
+ std::string metadata_secret);
+
+ static std::unique_ptr<TouchIdAuthenticator> CreateForTesting(
+ std::string keychain_access_group,
+ std::string metadata_secret);
~TouchIdAuthenticator() override;
- // TouchIdAuthenticator
+ // FidoAuthenticator
void MakeCredential(
- AuthenticatorSelectionCriteria authenticator_selection_criteria,
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;
private:
- TouchIdAuthenticator();
+ TouchIdAuthenticator(std::string keychain_access_group,
+ std::string metadata_secret);
- // The profile ID identifies the user profile from which the request
- // originates. It is used to scope credentials to the profile under which they
- // were created.
- base::StringPiece GetOrInitializeProfileId();
-
- // The keychain access group is a string value related to the Apple developer
- // ID under which the binary gets signed that the Keychain Services API use
- // for access control. See
+ // The keychain access group under which credentials are stored in the macOS
+ // keychain for access control. The set of all access groups that the
+ // application belongs to is stored in the entitlements file that gets
+ // embedded into the application during code signing. For more information
+ // see
// https://developer.apple.com/documentation/security/ksecattraccessgroup?language=objc.
- base::StringPiece keychain_access_group() {
- return "EQHXZ8M8AV.com.google.chrome.webauthn";
- }
+ std::string keychain_access_group_;
+
+ std::string metadata_secret_;
std::unique_ptr<Operation> operation_;
diff --git a/chromium/device/fido/mac/authenticator.mm b/chromium/device/fido/mac/authenticator.mm
index 351e3e94c49..76f9d482d58 100644
--- a/chromium/device/fido/mac/authenticator.mm
+++ b/chromium/device/fido/mac/authenticator.mm
@@ -6,12 +6,16 @@
#import <LocalAuthentication/LocalAuthentication.h>
+#include "base/feature_list.h"
+#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/optional.h"
#include "base/strings/string_piece.h"
-#include "device/fido/authenticator_selection_criteria.h"
+#include "device/base/features.h"
+#include "device/fido/authenticator_supported_options.h"
#include "device/fido/ctap_get_assertion_request.h"
#include "device/fido/ctap_make_credential_request.h"
+#include "device/fido/fido_constants.h"
#include "device/fido/mac/get_assertion_operation.h"
#include "device/fido/mac/make_credential_operation.h"
#include "device/fido/mac/util.h"
@@ -21,43 +25,63 @@ namespace fido {
namespace mac {
// static
-// NOTE(martinkr): This is currently only called from |CreateIfAvailable| but
-// will also be needed for the implementation of
-// IsUserVerifyingPlatformAuthenticatorAvailable() (see
-// https://www.w3.org/TR/webauthn/#isUserVerifyingPlatformAuthenticatorAvailable).
bool TouchIdAuthenticator::IsAvailable() {
- base::scoped_nsobject<LAContext> context([[LAContext alloc] init]);
- return
- [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
- error:nil];
+ 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 false;
}
// static
-std::unique_ptr<TouchIdAuthenticator>
-TouchIdAuthenticator::CreateIfAvailable() {
- return IsAvailable() ? base::WrapUnique(new TouchIdAuthenticator()) : nullptr;
+std::unique_ptr<TouchIdAuthenticator> TouchIdAuthenticator::CreateIfAvailable(
+ std::string keychain_access_group,
+ std::string metadata_secret) {
+ // N.B. IsAvailable also checks for the feature flag being set.
+ return IsAvailable() ? base::WrapUnique(new TouchIdAuthenticator(
+ std::move(keychain_access_group),
+ std::move(metadata_secret)))
+ : nullptr;
+}
+
+// static
+std::unique_ptr<TouchIdAuthenticator> TouchIdAuthenticator::CreateForTesting(
+ std::string keychain_access_group,
+ std::string metadata_secret) {
+ return base::WrapUnique(new TouchIdAuthenticator(
+ std::move(keychain_access_group), std::move(metadata_secret)));
}
TouchIdAuthenticator::~TouchIdAuthenticator() = default;
-void TouchIdAuthenticator::MakeCredential(
- AuthenticatorSelectionCriteria authenticator_selection_criteria,
- CtapMakeCredentialRequest request,
- MakeCredentialCallback callback) {
- DCHECK(!operation_);
- operation_ = std::make_unique<MakeCredentialOperation>(
- std::move(request), GetOrInitializeProfileId().as_string(),
- keychain_access_group().as_string(), std::move(callback));
- operation_->Run();
+void TouchIdAuthenticator::MakeCredential(CtapMakeCredentialRequest request,
+ MakeCredentialCallback callback) {
+ if (__builtin_available(macOS 10.12.2, *)) {
+ DCHECK(!operation_);
+ operation_ = std::make_unique<MakeCredentialOperation>(
+ std::move(request), metadata_secret_, keychain_access_group_,
+ std::move(callback));
+ operation_->Run();
+ return;
+ }
+ NOTREACHED();
}
void TouchIdAuthenticator::GetAssertion(CtapGetAssertionRequest request,
GetAssertionCallback callback) {
- DCHECK(!operation_);
- operation_ = std::make_unique<GetAssertionOperation>(
- std::move(request), GetOrInitializeProfileId().as_string(),
- keychain_access_group().as_string(), std::move(callback));
- operation_->Run();
+ if (__builtin_available(macOS 10.12.2, *)) {
+ DCHECK(!operation_);
+ operation_ = std::make_unique<GetAssertionOperation>(
+ std::move(request), metadata_secret_, keychain_access_group_,
+ std::move(callback));
+ operation_->Run();
+ return;
+ }
+ NOTREACHED();
}
void TouchIdAuthenticator::Cancel() {
@@ -72,13 +96,32 @@ std::string TouchIdAuthenticator::GetId() const {
return "TouchIdAuthenticator";
}
-TouchIdAuthenticator::TouchIdAuthenticator() = default;
+namespace {
-base::StringPiece TouchIdAuthenticator::GetOrInitializeProfileId() {
- // TODO(martinkr): Implement.
- return "TODO";
+AuthenticatorSupportedOptions TouchIdAuthenticatorOptions() {
+ AuthenticatorSupportedOptions options;
+ options.SetIsPlatformDevice(true);
+ options.SetSupportsResidentKey(true);
+ options.SetUserVerificationAvailability(
+ AuthenticatorSupportedOptions::UserVerificationAvailability::
+ kSupportedAndConfigured);
+ options.SetUserPresenceRequired(true);
+ return options;
}
+} // namespace
+
+const AuthenticatorSupportedOptions& TouchIdAuthenticator::Options() const {
+ static const AuthenticatorSupportedOptions options =
+ TouchIdAuthenticatorOptions();
+ return options;
+}
+
+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)) {}
+
} // namespace mac
} // namespace fido
} // namespace device
diff --git a/chromium/device/fido/mac/browsing_data_deletion.h b/chromium/device/fido/mac/browsing_data_deletion.h
new file mode 100644
index 00000000000..1ab37d7527d
--- /dev/null
+++ b/chromium/device/fido/mac/browsing_data_deletion.h
@@ -0,0 +1,40 @@
+// 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_BROWSING_DATA_DELETION_H_
+#define DEVICE_FIDO_MAC_BROWSING_DATA_DELETION_H_
+
+#include <string>
+
+#include "base/component_export.h"
+#include "base/time/time.h"
+
+namespace device {
+namespace fido {
+namespace mac {
+
+// DeleteWebAuthnCredentiuals deletes Touch ID authenticator credentials from
+// the macOS keychain that were created within the time interval `[begin, end)`
+// and with the given metadata secret (which is tied to a browser profile).
+// The |keychain_access_group| parameter is an identifier tied to Chrome's code
+// signing identity that identifies the set of all keychain items associated
+// with the Touch ID WebAuthentication authenticator.
+//
+// Returns false if any attempt to delete a credential failed (but others may
+// still have succeeded), and true otherwise.
+//
+// On platforms where Touch ID is not supported, or when the Touch ID WebAuthn
+// authenticator feature flag is disabled, this method does nothing and returns
+// true.
+bool COMPONENT_EXPORT(DEVICE_FIDO)
+ DeleteWebAuthnCredentials(const std::string& keychain_access_group,
+ const std::string& profile_metadata_secret,
+ base::Time begin,
+ base::Time end);
+
+} // namespace mac
+} // namespace fido
+} // namespace device
+
+#endif // DEVICE_FIDO_MAC_BROWSING_DATA_DELETION_H_
diff --git a/chromium/device/fido/mac/browsing_data_deletion.mm b/chromium/device/fido/mac/browsing_data_deletion.mm
new file mode 100644
index 00000000000..56eefc45309
--- /dev/null
+++ b/chromium/device/fido/mac/browsing_data_deletion.mm
@@ -0,0 +1,162 @@
+// 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/browsing_data_deletion.h"
+
+#include <string>
+
+#import <Foundation/Foundation.h>
+#import <Security/Security.h>
+
+#include "base/feature_list.h"
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/mac_logging.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/optional.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "device/base/features.h"
+#include "device/fido/mac/credential_metadata.h"
+#include "device/fido/mac/keychain.h"
+
+namespace device {
+namespace fido {
+namespace mac {
+
+namespace {
+
+bool DoDeleteWebAuthnCredentials(const std::string& keychain_access_group,
+ const std::string& profile_metadata_secret,
+ base::Time begin,
+ base::Time end)
+ API_AVAILABLE(macosx(10.12.2)) {
+ // Query the keychain for all items tagged with the given access group, which
+ // should in theory yield all WebAuthentication credentials (for all
+ // profiles). Sadly, the kSecAttrAccessGroup filter doesn't quite work, and
+ // so we also get results from the legacy keychain that are tagged with no
+ // keychain access group.
+ base::ScopedCFTypeRef<CFMutableDictionaryRef> query(
+ CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr));
+ CFDictionarySetValue(query, kSecClass, kSecClassKey);
+ CFDictionarySetValue(query, kSecAttrAccessGroup,
+ base::SysUTF8ToNSString(keychain_access_group));
+ CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
+ // Return the key reference and its attributes.
+ CFDictionarySetValue(query, kSecReturnRef, @YES);
+ CFDictionarySetValue(query, kSecReturnAttributes, @YES);
+
+ base::ScopedCFTypeRef<CFArrayRef> keychain_items;
+ {
+ OSStatus status = Keychain::GetInstance().ItemCopyMatching(
+ query, reinterpret_cast<CFTypeRef*>(keychain_items.InitializeInto()));
+ if (status == errSecItemNotFound) {
+ DVLOG(1) << "no credentials found";
+ return true;
+ }
+ if (status != errSecSuccess) {
+ OSSTATUS_DLOG(ERROR, status) << "SecItemCopyMatching failed";
+ return false;
+ }
+ }
+
+ bool result = true;
+ // Determine which items from the result need to be deleted.
+ for (CFIndex i = 0; i < CFArrayGetCount(keychain_items); ++i) {
+ CFDictionaryRef attributes = base::mac::CFCast<CFDictionaryRef>(
+ CFArrayGetValueAtIndex(keychain_items, i));
+ // Skip items that don't belong to the correct keychain access group
+ // because the kSecAttrAccessGroup filter is broken.
+ CFStringRef attr_access_group =
+ base::mac::GetValueFromDictionary<CFStringRef>(attributes,
+ kSecAttrAccessGroup);
+ if (!attr_access_group || base::SysCFStringRefToUTF8(attr_access_group) !=
+ keychain_access_group) {
+ DVLOG(1) << "missing/invalid access group";
+ continue;
+ }
+ // If the RP ID, stored encrypted in the item's label, cannot be decrypted
+ // with the given metadata secret, then the credential belongs to a
+ // different profile and must be ignored.
+ CFStringRef sec_attr_label = base::mac::GetValueFromDictionary<CFStringRef>(
+ attributes, kSecAttrLabel);
+ if (!sec_attr_label) {
+ DLOG(ERROR) << "missing label";
+ continue;
+ }
+ base::Optional<std::string> opt_rp_id = CredentialMetadata::DecodeRpId(
+ profile_metadata_secret, base::SysCFStringRefToUTF8(sec_attr_label));
+ if (!opt_rp_id) {
+ DVLOG(1) << "key doesn't belong to this profile";
+ continue;
+ }
+
+ // If the creation date is out of range, the credential must be ignored.
+ // If the creation date is missing for some obscure reason, we delete.
+ CFDateRef creation_date_cf = base::mac::GetValueFromDictionary<CFDateRef>(
+ attributes, kSecAttrCreationDate);
+ if (!creation_date_cf) {
+ DVLOG(1) << "missing creation date; deleting anyway";
+ } else {
+ base::Time creation_date = base::Time::FromCFAbsoluteTime(
+ CFDateGetAbsoluteTime(creation_date_cf));
+ // Browsing data deletion API uses [begin, end) intervals.
+ if (creation_date < begin || creation_date >= end) {
+ DVLOG(1) << "creation date out of range";
+ continue;
+ }
+ }
+
+ // The sane way to delete this item would be to build a query that has the
+ // kSecMatchItemList field set to a list of SecKeyRef objects that need
+ // deleting. Sadly, on macOS that appears to work only if you also set
+ // kSecAttrNoLegacy (which is an internal symbol); otherwise it appears to
+ // only search the "legacy" keychain and return errSecItemNotFound. What
+ // does work however, is lookup by the (unique) kSecAttrApplicationLabel
+ // (which stores the credential id). So we clumsily do this for each item
+ // instead.
+ CFDataRef sec_attr_app_label = base::mac::GetValueFromDictionary<CFDataRef>(
+ attributes, kSecAttrApplicationLabel);
+ if (!sec_attr_app_label) {
+ DLOG(ERROR) << "missing application label";
+ continue;
+ }
+ base::ScopedCFTypeRef<CFMutableDictionaryRef> delete_query(
+ CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr));
+ CFDictionarySetValue(delete_query, kSecClass, kSecClassKey);
+ CFDictionarySetValue(delete_query, kSecAttrApplicationLabel,
+ sec_attr_app_label);
+ OSStatus status = Keychain::GetInstance().ItemDelete(delete_query);
+ if (status != errSecSuccess) {
+ OSSTATUS_DLOG(ERROR, status) << "SecItemDelete failed";
+ result = false;
+ continue;
+ }
+ }
+ return result;
+}
+
+} // namespace
+
+bool DeleteWebAuthnCredentials(const std::string& keychain_access_group,
+ const std::string& profile_metadata_secret,
+ base::Time begin,
+ base::Time end) {
+#if defined(OS_MACOSX)
+ if (base::FeatureList::IsEnabled(device::kWebAuthTouchId)) {
+ // Touch ID uses macOS APIs available in 10.12.2 or newer. No need to check
+ // for credentials in lower OS versions.
+ if (__builtin_available(macos 10.12.2, *)) {
+ return DoDeleteWebAuthnCredentials(keychain_access_group,
+ profile_metadata_secret, begin, end);
+ }
+ }
+#endif // defined(OS_MACOSX)
+ return true;
+}
+
+} // namespace mac
+} // namespace fido
+} // namespace device
diff --git a/chromium/device/fido/mac/browsing_data_deletion_unittest.mm b/chromium/device/fido/mac/browsing_data_deletion_unittest.mm
new file mode 100644
index 00000000000..b6acba5bed7
--- /dev/null
+++ b/chromium/device/fido/mac/browsing_data_deletion_unittest.mm
@@ -0,0 +1,210 @@
+// 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/browsing_data_deletion.h"
+
+#include <Foundation/Foundation.h>
+#include <Security/Security.h>
+
+#include "base/mac/foundation_util.h"
+#include "base/mac/mac_logging.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/scoped_task_environment.h"
+#include "device/base/features.h"
+#include "device/fido/ctap_make_credential_request.h"
+#include "device/fido/fido_constants.h"
+#include "device/fido/mac/authenticator.h"
+#include "device/fido/mac/keychain.h"
+#include "device/fido/test_callback_receiver.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+extern "C" {
+// This is a private Security Framework symbol. It indicates that a query must
+// be run on the "syncable" macOS keychain, which is where Secure Enclave keys
+// are stored. This test needs it because it tries to erase all credentials
+// belonging to the (test-only) keychain access group, and the corresponding
+// filter label (kSecAttrAccessGroup) appears to be ineffective *unless*
+// kSecAttrNoLegacy is `@YES`. Marked as weak import because the symbol is only
+// available in 10.11 or greater.
+extern const CFStringRef kSecAttrNoLegacy __attribute__((weak_import));
+}
+
+namespace device {
+
+using test::TestCallbackReceiver;
+
+namespace fido {
+namespace mac {
+namespace {
+
+constexpr char kKeychainAccessGroup[] =
+ "EQHXZ8M8AV.com.google.chrome.webauthn.test";
+constexpr char kMetadataSecret[] = "supersecret";
+
+constexpr std::array<uint8_t, kClientDataHashLength> kClientDataHash = {};
+constexpr char kRpId[] = "rp.example.com";
+const std::vector<uint8_t> kUserId = {10, 11, 12, 13, 14, 15};
+
+// Returns a query to use with Keychain instance methods that returns all
+// credentials in the non-legacy keychain that are tagged with the keychain
+// access group used in this test.
+base::ScopedCFTypeRef<CFMutableDictionaryRef> BaseQuery() {
+ base::ScopedCFTypeRef<CFMutableDictionaryRef> query(
+ CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr));
+ CFDictionarySetValue(query, kSecClass, kSecClassKey);
+ base::ScopedCFTypeRef<CFStringRef> access_group_ref(
+ base::SysUTF8ToCFStringRef(kKeychainAccessGroup));
+ CFDictionarySetValue(query, kSecAttrAccessGroup, access_group_ref.release());
+ CFDictionarySetValue(query, kSecAttrNoLegacy, @YES);
+ CFDictionarySetValue(query, kSecReturnAttributes, @YES);
+ CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
+ return query;
+}
+
+// Returns all WebAuthn credentials stored in the keychain, regardless of which
+// profile they are associated with. May return a null reference if an error
+// occurred.
+base::ScopedCFTypeRef<CFArrayRef> QueryAllCredentials() {
+ if (__builtin_available(macOS 10.12.2, *)) {
+ base::ScopedCFTypeRef<CFArrayRef> items;
+ OSStatus status = Keychain::GetInstance().ItemCopyMatching(
+ BaseQuery(), reinterpret_cast<CFTypeRef*>(items.InitializeInto()));
+ if (status == errSecItemNotFound) {
+ // The API returns null, but we should return an empty array instead to
+ // distinguish from real errors.
+ items = base::ScopedCFTypeRef<CFArrayRef>(
+ CFArrayCreate(nullptr, nullptr, 0, nullptr));
+ } else if (status != errSecSuccess) {
+ OSSTATUS_DLOG(ERROR, status);
+ }
+ return items;
+ }
+ NOTREACHED();
+ return base::ScopedCFTypeRef<CFArrayRef>(nullptr);
+}
+
+// Returns the number of WebAuthn credentials in the keychain (for all
+// profiles), or -1 if an error occurs.
+ssize_t CredentialCount() {
+ base::ScopedCFTypeRef<CFArrayRef> items = QueryAllCredentials();
+ return items ? CFArrayGetCount(items) : -1;
+}
+
+bool ResetKeychain() {
+ if (__builtin_available(macOS 10.12.2, *)) {
+ OSStatus status = Keychain::GetInstance().ItemDelete(BaseQuery());
+ if (status != errSecSuccess && status != errSecItemNotFound) {
+ OSSTATUS_DLOG(ERROR, status);
+ return false;
+ }
+ return true;
+ }
+ NOTREACHED();
+ return false;
+}
+
+class BrowsingDataDeletionTest : public testing::Test {
+ public:
+ void SetUp() override {
+ scoped_feature_list_.InitAndEnableFeature(device::kWebAuthTouchId);
+ authenticator_ = MakeAuthenticator(kMetadataSecret);
+ CHECK(authenticator_);
+ CHECK(ResetKeychain());
+ }
+
+ void TearDown() override { ResetKeychain(); }
+
+ protected:
+ CtapMakeCredentialRequest MakeRequest() {
+ return CtapMakeCredentialRequest(
+ kClientDataHash, PublicKeyCredentialRpEntity(kRpId),
+ PublicKeyCredentialUserEntity(kUserId),
+ PublicKeyCredentialParams(
+ {{PublicKeyCredentialParams::
+ CredentialInfo() /* defaults to ES-256 */}}));
+ }
+
+ std::unique_ptr<TouchIdAuthenticator> MakeAuthenticator(
+ std::string profile_metadata_secret) {
+ return TouchIdAuthenticator::CreateForTesting(
+ kKeychainAccessGroup, std::move(profile_metadata_secret));
+ }
+
+ bool MakeCredential() { return MakeCredential(authenticator_.get()); }
+
+ bool MakeCredential(TouchIdAuthenticator* authenticator) {
+ TestCallbackReceiver<CtapDeviceResponseCode,
+ base::Optional<AuthenticatorMakeCredentialResponse>>
+ callback_receiver;
+ authenticator->MakeCredential(MakeRequest(), callback_receiver.callback());
+ callback_receiver.WaitForCallback();
+ auto result = callback_receiver.TakeResult();
+ return std::get<0>(result) == CtapDeviceResponseCode::kSuccess;
+ }
+
+ bool DeleteCredentials() { return DeleteCredentials(kMetadataSecret); }
+
+ bool DeleteCredentials(const std::string& metadata_secret) {
+ return DeleteWebAuthnCredentials(kKeychainAccessGroup, metadata_secret,
+ base::Time(), base::Time::Max());
+ }
+
+ base::test::ScopedFeatureList scoped_feature_list_;
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ std::unique_ptr<TouchIdAuthenticator> authenticator_;
+};
+
+// All tests are disabled because they need to be codesigned with the
+// keychain-access-group entitlement, executed on a Macbook Pro with Touch ID
+// running macOS 10.12.2 or later, and require user input (Touch ID).
+
+TEST_F(BrowsingDataDeletionTest, DISABLED_Basic) {
+ ASSERT_EQ(0, CredentialCount());
+ ASSERT_TRUE(MakeCredential());
+ ASSERT_EQ(1, CredentialCount());
+
+ EXPECT_TRUE(DeleteCredentials());
+ EXPECT_EQ(0, CredentialCount());
+}
+
+TEST_F(BrowsingDataDeletionTest, DISABLED_DifferentProfiles) {
+ // Create credentials in two different profiles.
+ EXPECT_EQ(0, CredentialCount());
+ ASSERT_TRUE(MakeCredential());
+ std::string other_metadata_secret = "reallynotsosecret";
+ auto other_authenticator = MakeAuthenticator(other_metadata_secret);
+ ASSERT_TRUE(MakeCredential(other_authenticator.get()));
+ ASSERT_EQ(2, CredentialCount());
+
+ // Delete credential from the first profile.
+ EXPECT_TRUE(DeleteCredentials());
+ EXPECT_EQ(1, CredentialCount());
+ // Only providing the correct secret removes the second credential.
+ EXPECT_TRUE(DeleteCredentials());
+ EXPECT_EQ(1, CredentialCount());
+ EXPECT_TRUE(DeleteCredentials(other_metadata_secret));
+ EXPECT_EQ(0, CredentialCount());
+}
+
+TEST_F(BrowsingDataDeletionTest, DISABLED_FeatureFlag) {
+ // Remove the feature flag override provided by the fixture.
+ base::FeatureList::ClearInstanceForTesting();
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndDisableFeature(device::kWebAuthTouchId);
+
+ ASSERT_EQ(0, CredentialCount());
+ ASSERT_TRUE(MakeCredential());
+
+ // DeleteCredentials() has no effect with the feature flag flipped off.
+ EXPECT_TRUE(DeleteCredentials());
+ EXPECT_EQ(1, CredentialCount());
+}
+
+} // namespace
+} // namespace mac
+} // namespace fido
+} // namespace device
diff --git a/chromium/device/fido/mac/credential_metadata.cc b/chromium/device/fido/mac/credential_metadata.cc
new file mode 100644
index 00000000000..60d9feafd77
--- /dev/null
+++ b/chromium/device/fido/mac/credential_metadata.cc
@@ -0,0 +1,303 @@
+// 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/credential_metadata.h"
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "components/cbor/cbor_reader.h"
+#include "components/cbor/cbor_values.h"
+#include "components/cbor/cbor_writer.h"
+#include "device/fido/public_key_credential_user_entity.h"
+#include "third_party/boringssl/src/include/openssl/digest.h"
+#include "third_party/boringssl/src/include/openssl/hkdf.h"
+#include "third_party/boringssl/src/include/openssl/rand.h"
+
+namespace device {
+namespace fido {
+namespace mac {
+
+using cbor::CBORWriter;
+using cbor::CBORReader;
+using cbor::CBORValue;
+
+// static
+std::string CredentialMetadata::GenerateRandomSecret() {
+ static constexpr size_t kSecretSize = 32u;
+ std::string secret;
+ RAND_bytes(
+ reinterpret_cast<uint8_t*>(base::WriteInto(&secret, kSecretSize + 1)),
+ kSecretSize);
+ return secret;
+}
+
+CredentialMetadata::CredentialMetadata(const std::string& secret)
+ : secret_(secret) {}
+CredentialMetadata::~CredentialMetadata() = default;
+
+// static
+CredentialMetadata::UserEntity
+CredentialMetadata::UserEntity::FromPublicKeyCredentialUserEntity(
+ const PublicKeyCredentialUserEntity& user) {
+ return CredentialMetadata::UserEntity(user.user_id(),
+ user.user_name().value_or(""),
+ user.user_display_name().value_or(""));
+}
+
+PublicKeyCredentialUserEntity
+CredentialMetadata::UserEntity::ToPublicKeyCredentialUserEntity() {
+ auto user_entity = PublicKeyCredentialUserEntity(id);
+ if (!name.empty()) {
+ user_entity.SetUserName(name);
+ }
+ if (!display_name.empty()) {
+ user_entity.SetDisplayName(display_name);
+ }
+ return user_entity;
+}
+
+CredentialMetadata::UserEntity::UserEntity(std::vector<uint8_t> id_,
+ std::string name_,
+ std::string display_name_)
+ : id(std::move(id_)),
+ name(std::move(name_)),
+ display_name(std::move(display_name_)) {}
+CredentialMetadata::UserEntity::UserEntity(
+ const CredentialMetadata::UserEntity&) = default;
+CredentialMetadata::UserEntity::UserEntity(CredentialMetadata::UserEntity&&) =
+ default;
+CredentialMetadata::UserEntity& CredentialMetadata::UserEntity::operator=(
+ CredentialMetadata::UserEntity&&) = default;
+CredentialMetadata::UserEntity::~UserEntity() = default;
+
+static constexpr size_t kNonceLength = 12;
+
+// static
+base::Optional<std::vector<uint8_t>> CredentialMetadata::SealCredentialId(
+ const std::string& secret,
+ const std::string& rp_id,
+ const UserEntity& user) {
+ CredentialMetadata cryptor(secret);
+
+ // The first 13 bytes are the version and nonce.
+ std::vector<uint8_t> result(1 + kNonceLength);
+ result[0] = kVersion;
+ // Pick a random nonce. N.B. the nonce is similar to an IV. It needs to be
+ // distinct (but not necessarily random). Nonce reuse breaks confidentiality
+ // (in particular, it leaks the XOR of the plaintexts encrypted under the
+ // same nonce and key).
+ base::span<uint8_t> nonce(result.data() + 1, kNonceLength);
+ RAND_bytes(nonce.data(), nonce.size()); // RAND_bytes always returns 1.
+
+ // The remaining bytes are the CBOR-encoded UserEntity, encrypted with
+ // AES-256-GCM and authenticated with the version and RP ID.
+ CBORValue::ArrayValue cbor_user;
+ cbor_user.emplace_back(CBORValue(user.id));
+ cbor_user.emplace_back(CBORValue(user.name, CBORValue::Type::BYTE_STRING));
+ cbor_user.emplace_back(
+ CBORValue(user.display_name, CBORValue::Type::BYTE_STRING));
+ base::Optional<std::vector<uint8_t>> pt =
+ CBORWriter::Write(CBORValue(std::move(cbor_user)));
+ if (!pt) {
+ return base::nullopt;
+ }
+ base::Optional<std::string> ciphertext = cryptor.Seal(
+ CredentialMetadata::Algorithm::kAes256Gcm, nonce, *pt, MakeAad(rp_id));
+ if (!ciphertext) {
+ return base::nullopt;
+ }
+ base::span<const char> cts(reinterpret_cast<const char*>(ciphertext->data()),
+ ciphertext->size());
+ result.insert(result.end(), cts.begin(), cts.end());
+ return result;
+}
+
+// static
+base::Optional<CredentialMetadata::UserEntity>
+CredentialMetadata::UnsealCredentialId(
+ const std::string& secret,
+ const std::string& rp_id,
+ base::span<const uint8_t> credential_id) {
+ CredentialMetadata cryptor(secret);
+
+ // Recover the nonce and check for the correct version byte. Then try to
+ // decrypt the remaining bytes.
+ if (credential_id.size() <= 1 + kNonceLength ||
+ credential_id[0] != kVersion) {
+ return base::nullopt;
+ }
+
+ base::Optional<std::string> plaintext =
+ cryptor.Unseal(CredentialMetadata::Algorithm::kAes256Gcm,
+ credential_id.subspan(1, kNonceLength),
+ credential_id.subspan(1 + kNonceLength), MakeAad(rp_id));
+ if (!plaintext) {
+ return base::nullopt;
+ }
+
+ // The recovered plaintext should decode into the UserEntity struct.
+ base::Optional<CBORValue> maybe_array = CBORReader::Read(base::make_span(
+ reinterpret_cast<const uint8_t*>(plaintext->data()), plaintext->size()));
+ if (!maybe_array || !maybe_array->is_array()) {
+ return base::nullopt;
+ }
+ const CBORValue::ArrayValue& array = maybe_array->GetArray();
+ if (array.size() != 3 || !array[0].is_bytestring() ||
+ !array[1].is_bytestring() || !array[2].is_bytestring()) {
+ return base::nullopt;
+ }
+ return UserEntity(array[0].GetBytestring(),
+ array[1].GetBytestringAsString().as_string(),
+ array[2].GetBytestringAsString().as_string());
+}
+
+// static
+base::Optional<std::string> CredentialMetadata::EncodeRpIdAndUserId(
+ const std::string& secret,
+ const std::string& rp_id,
+ base::span<const uint8_t> user_id) {
+ // Encoding RP ID along with the user ID hides whether the same user ID was
+ // reused on different RPs.
+ const auto* user_id_data = reinterpret_cast<const char*>(user_id.data());
+ return CredentialMetadata(secret).HmacForStorage(
+ rp_id + "/" + std::string(user_id_data, user_id_data + user_id.size()));
+}
+
+// static
+base::Optional<std::string> CredentialMetadata::EncodeRpId(
+ const std::string& secret,
+ const std::string& rp_id) {
+ // Encrypt with a fixed nonce to make the result deterministic while still
+ // allowing the RP ID to be recovered from the ciphertext later.
+ static constexpr std::array<uint8_t, kNonceLength> fixed_zero_nonce = {};
+ base::span<const uint8_t> pt(reinterpret_cast<const uint8_t*>(rp_id.data()),
+ rp_id.size());
+ std::string empty_ad;
+ // Using AES-GCM with a fixed nonce would break confidentiality, so this uses
+ // AES-GCM-SIV instead.
+ base::Optional<std::string> ct = CredentialMetadata(secret).Seal(
+ CredentialMetadata::Algorithm::kAes256GcmSiv, fixed_zero_nonce, pt,
+ empty_ad);
+ if (!ct) {
+ return base::nullopt;
+ }
+ // The keychain field that stores the encrypted RP ID only accepts NSString
+ // (not NSData), so we HexEncode to ensure the result is UTF-8-decodable.
+ return base::HexEncode(ct->data(), ct->size());
+}
+
+// static
+base::Optional<std::string> CredentialMetadata::DecodeRpId(
+ const std::string& secret,
+ const std::string& ciphertext) {
+ std::vector<uint8_t> ct;
+ if (!base::HexStringToBytes(ciphertext, &ct)) {
+ return base::nullopt;
+ }
+ static constexpr std::array<uint8_t, kNonceLength> fixed_zero_nonce = {};
+ std::string empty_ad;
+ return CredentialMetadata(secret).Unseal(
+ CredentialMetadata::Algorithm::kAes256GcmSiv, fixed_zero_nonce, ct,
+ empty_ad);
+}
+
+// static
+std::string CredentialMetadata::MakeAad(const std::string& rp_id) {
+ return std::string(1, kVersion) + rp_id;
+}
+
+// static
+std::string CredentialMetadata::DeriveKey(base::StringPiece secret,
+ Algorithm alg) {
+ static constexpr size_t kKeyLength = 32u;
+ std::string key;
+ const uint8_t info = static_cast<uint8_t>(alg);
+ const bool hkdf_init = ::HKDF(
+ reinterpret_cast<uint8_t*>(base::WriteInto(&key, kKeyLength + 1)),
+ kKeyLength, EVP_sha256(), reinterpret_cast<const uint8_t*>(secret.data()),
+ secret.size(), nullptr /* salt */, 0, &info, 1);
+ DCHECK(hkdf_init);
+ return key;
+}
+
+// static
+base::Optional<crypto::Aead::AeadAlgorithm> CredentialMetadata::ToAeadAlgorithm(
+ Algorithm alg) {
+ switch (alg) {
+ case CredentialMetadata::Algorithm::kAes256Gcm:
+ return crypto::Aead::AES_256_GCM;
+ case CredentialMetadata::Algorithm::kAes256GcmSiv:
+ return crypto::Aead::AES_256_GCM_SIV;
+ case CredentialMetadata::Algorithm::kHmacSha256:
+ NOTREACHED() << "invalid AEAD";
+ return base::nullopt;
+ }
+}
+
+base::Optional<std::string> CredentialMetadata::Seal(
+ CredentialMetadata::Algorithm algorithm,
+ base::span<const uint8_t> nonce,
+ base::span<const uint8_t> plaintext,
+ base::StringPiece authenticated_data) const {
+ auto opt_aead_algorithm = ToAeadAlgorithm(algorithm);
+ if (!opt_aead_algorithm)
+ return base::nullopt;
+
+ const std::string key = DeriveKey(secret_, algorithm);
+ crypto::Aead aead(*opt_aead_algorithm);
+ aead.Init(&key);
+ std::string ciphertext;
+ if (!aead.Seal(
+ base::StringPiece(reinterpret_cast<const char*>(plaintext.data()),
+ plaintext.size()),
+ base::StringPiece(reinterpret_cast<const char*>(nonce.data()),
+ nonce.size()),
+ authenticated_data, &ciphertext)) {
+ return base::nullopt;
+ }
+ return ciphertext;
+}
+
+base::Optional<std::string> CredentialMetadata::Unseal(
+ CredentialMetadata::Algorithm algorithm,
+ base::span<const uint8_t> nonce,
+ base::span<const uint8_t> ciphertext,
+ base::StringPiece authenticated_data) const {
+ auto opt_aead_algorithm = ToAeadAlgorithm(algorithm);
+ if (!opt_aead_algorithm)
+ return base::nullopt;
+
+ const std::string key = DeriveKey(secret_, algorithm);
+ crypto::Aead aead(*opt_aead_algorithm);
+ aead.Init(&key);
+ std::string plaintext;
+ if (!aead.Open(
+ base::StringPiece(reinterpret_cast<const char*>(ciphertext.data()),
+ ciphertext.size()),
+ base::StringPiece(reinterpret_cast<const char*>(nonce.data()),
+ nonce.size()),
+ authenticated_data, &plaintext)) {
+ return base::nullopt;
+ }
+ return plaintext;
+}
+
+base::Optional<std::string> CredentialMetadata::HmacForStorage(
+ base::StringPiece data) const {
+ crypto::HMAC hmac(crypto::HMAC::SHA256);
+ const std::string key = DeriveKey(secret_, Algorithm::kHmacSha256);
+ std::vector<uint8_t> digest(hmac.DigestLength());
+ if (!hmac.Init(key) || !hmac.Sign(data, digest.data(), hmac.DigestLength())) {
+ return base::nullopt;
+ }
+ // The keychain fields that store RP ID and User ID seem to only accept
+ // NSString (not NSData), so we HexEncode to ensure the result to be
+ // UTF-8-decodable.
+ return base::HexEncode(digest.data(), digest.size());
+}
+
+} // namespace mac
+} // namespace fido
+} // namespace device
diff --git a/chromium/device/fido/mac/credential_metadata.h b/chromium/device/fido/mac/credential_metadata.h
new file mode 100644
index 00000000000..ec79d81a46f
--- /dev/null
+++ b/chromium/device/fido/mac/credential_metadata.h
@@ -0,0 +1,151 @@
+// 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_CREDENTIAL_METADATA_H_
+#define DEVICE_FIDO_MAC_CREDENTIAL_METADATA_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/component_export.h"
+#include "base/containers/span.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/strings/string_piece_forward.h"
+#include "crypto/aead.h"
+#include "crypto/hmac.h"
+#include "crypto/symmetric_key.h"
+
+namespace device {
+
+class PublicKeyCredentialUserEntity;
+
+namespace fido {
+namespace mac {
+
+// CredentialMetadata generates credential IDs from the associated user entity
+// (user ID, name and display name) by encrypting them under a key tied to the
+// current Chrome profile. This gives us separation of credentials per Chrome
+// profile. It also guarantees that account metadata in the OS keychain is
+// rendered unusable after the Chrome profile and the associated encryption key
+// have been deleted, in order to limit leakage of account metadata, such as
+// the list of RPs with registered credentials, into the OS keychain.
+//
+// Credential IDs have following format
+// | version | nonce | AEAD(pt=CBOR(user_entity), |
+// | (1 byte) | (12 bytes) | nonce=nonce, |
+// | | | ad=(version, rpID)) |
+// with version as 0x00, a random 12-byte nonce, and using AES-256-GCM as the
+// AEAD.
+//
+// CredentialMetadata also encodes the user ID and RP ID for storage in the OS
+// keychain by computing their HMAC.
+//
+// TODO(martinkr): We currently do not store profile icon URLs.
+class COMPONENT_EXPORT(DEVICE_FIDO) CredentialMetadata {
+ public:
+ // Generate a new random secret to use with the public interface of
+ // CredentialMetadata. Chrome stores this secret in the Profile Prefs.
+ static std::string GenerateRandomSecret();
+
+ // UserEntity loosely corresponds to a PublicKeyCredentialUserEntity
+ // (https://www.w3.org/TR/webauthn/#sctn-user-credential-params). Values of
+ // this type should be moved whenever possible.
+ struct UserEntity {
+ public:
+ static UserEntity FromPublicKeyCredentialUserEntity(
+ const PublicKeyCredentialUserEntity&);
+
+ UserEntity(std::vector<uint8_t> id_,
+ std::string name_,
+ std::string display_);
+ UserEntity(const UserEntity&);
+ UserEntity(UserEntity&&);
+ UserEntity& operator=(UserEntity&&);
+ ~UserEntity();
+
+ PublicKeyCredentialUserEntity ToPublicKeyCredentialUserEntity();
+
+ std::vector<uint8_t> id;
+ std::string name;
+ std::string display_name;
+ };
+
+ // SealCredentialId encrypts the given UserEntity into a credential id.
+ static base::Optional<std::vector<uint8_t>> SealCredentialId(
+ const std::string& secret,
+ const std::string& rp_id,
+ const UserEntity& user);
+
+ // UnsealCredentialId attempts to decrypt a UserEntity from a given credential
+ // id.
+ static base::Optional<UserEntity> UnsealCredentialId(
+ const std::string& secret,
+ const std::string& rp_id,
+ base::span<const uint8_t> credential_id);
+
+ // EncodeRpIdAndUserId encodes the concatenation of RP ID and user ID for
+ // storage in the macOS keychain.
+ static base::Optional<std::string> EncodeRpIdAndUserId(
+ const std::string& secret,
+ const std::string& rp_id,
+ base::span<const uint8_t> user_id);
+
+ // EncodeRpId encodes the given RP ID for storage in the macOS keychain.
+ static base::Optional<std::string> EncodeRpId(const std::string& secret,
+ const std::string& rp_id);
+
+ // DecodeRpId attempts to decode a given RP ID from the keychain. This can be
+ // used to test whether a set of credential metadata was created under the
+ // given secret without knowing the RP ID (which would be required to unseal
+ // a credential ID).
+ static base::Optional<std::string> DecodeRpId(const std::string& secret,
+ const std::string& ciphertext);
+
+ private:
+ enum Algorithm : uint8_t {
+ kAes256Gcm = 0,
+ kHmacSha256 = 1,
+ kAes256GcmSiv = 2,
+ };
+ static constexpr uint8_t kVersion = 0x00;
+
+ // MakeAad returns the concatenation of |kVersion| and |rp_id|,
+ // which is used as the additional authenticated data (AAD) input to the AEAD.
+ static std::string MakeAad(const std::string& rp_id);
+
+ // Derives keys from the caller-provided secret to avoid using the same key
+ // for different algorithms.
+ static std::string DeriveKey(base::StringPiece secret, Algorithm alg);
+ static base::Optional<crypto::Aead::AeadAlgorithm> ToAeadAlgorithm(
+ Algorithm alg);
+
+ CredentialMetadata(const std::string& secret);
+ ~CredentialMetadata();
+
+ base::Optional<std::string> Seal(Algorithm alg,
+ base::span<const uint8_t> nonce,
+ base::span<const uint8_t> plaintext,
+ base::StringPiece authenticated_data) const;
+ base::Optional<std::string> Unseal(
+ Algorithm alg,
+ base::span<const uint8_t> nonce,
+ base::span<const uint8_t> ciphertext,
+ base::StringPiece authenticated_data) const;
+ base::Optional<std::string> HmacForStorage(base::StringPiece data) const;
+
+ // Used to derive keys for the HMAC and AEAD operations. Chrome picks
+ // different secrets for each user profile. This ensures that credentials are
+ // logically tied to the Chrome user profile under which they were created.
+ const std::string& secret_;
+
+ DISALLOW_COPY_AND_ASSIGN(CredentialMetadata);
+};
+
+} // namespace mac
+} // namespace fido
+} // namespace device
+
+#endif // DEVICE_FIDO_MAC_CREDENTIAL_METADATA_H_
diff --git a/chromium/device/fido/mac/credential_metadata_unittest.cc b/chromium/device/fido/mac/credential_metadata_unittest.cc
new file mode 100644
index 00000000000..48bf13addae
--- /dev/null
+++ b/chromium/device/fido/mac/credential_metadata_unittest.cc
@@ -0,0 +1,154 @@
+// 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/credential_metadata.h"
+
+#include "device/fido/public_key_credential_user_entity.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace device {
+namespace fido {
+namespace mac {
+namespace {
+
+bool UserEqual(const CredentialMetadata::UserEntity& lhs,
+ const CredentialMetadata::UserEntity& rhs) {
+ return lhs.id == rhs.id && lhs.name == rhs.name &&
+ lhs.display_name == rhs.display_name;
+}
+
+base::span<const uint8_t> to_bytes(base::StringPiece in) {
+ return base::make_span(reinterpret_cast<const uint8_t*>(in.data()),
+ in.size());
+}
+
+class CredentialMetadataTest : public ::testing::Test {
+ protected:
+ CredentialMetadata::UserEntity DefaultUser() {
+ return CredentialMetadata::UserEntity(default_id_, "user", "user@acme.com");
+ }
+
+ std::vector<uint8_t> SealCredentialId(CredentialMetadata::UserEntity user) {
+ return *CredentialMetadata::SealCredentialId(key_, rp_id_, std::move(user));
+ }
+
+ CredentialMetadata::UserEntity UnsealCredentialId(
+ base::span<const uint8_t> credential_id) {
+ return *CredentialMetadata::UnsealCredentialId(key_, rp_id_, credential_id);
+ }
+
+ std::string EncodeRpIdAndUserId(base::StringPiece user_id) {
+ return *CredentialMetadata::EncodeRpIdAndUserId(key_, rp_id_,
+ to_bytes(user_id));
+ }
+ std::string EncodeRpId() {
+ return *CredentialMetadata::EncodeRpId(key_, rp_id_);
+ }
+
+ std::string DecodeRpId(const std::string& ct) {
+ return *CredentialMetadata::DecodeRpId(key_, ct);
+ }
+
+ std::vector<uint8_t> default_id_ = {0, 1, 2, 3};
+ std::string rp_id_ = "acme.com";
+ std::string key_ = "supersecretsupersecretsupersecre";
+ std::string wrong_key_ = "SUPERsecretsupersecretsupersecre";
+};
+
+TEST_F(CredentialMetadataTest, CredentialId) {
+ std::vector<uint8_t> id = SealCredentialId(DefaultUser());
+ EXPECT_EQ(0, (id)[0]);
+ EXPECT_EQ(54u, id.size());
+ EXPECT_TRUE(UserEqual(UnsealCredentialId(id), DefaultUser()));
+}
+
+TEST_F(CredentialMetadataTest, CredentialId_IsRandom) {
+ EXPECT_NE(SealCredentialId(DefaultUser()), SealCredentialId(DefaultUser()));
+}
+
+TEST_F(CredentialMetadataTest, CredentialId_FailDecode) {
+ const auto id = SealCredentialId(DefaultUser());
+ // Flipping a bit in version, nonce, or ciphertext will fail credential
+ // decryption.
+ for (size_t i = 0; i < id.size(); i++) {
+ std::vector<uint8_t> new_id(id);
+ new_id[i] = new_id[i] ^ 0x01;
+ EXPECT_FALSE(CredentialMetadata::UnsealCredentialId(key_, rp_id_, new_id));
+ }
+}
+
+TEST_F(CredentialMetadataTest, CredentialId_InvalidRp) {
+ std::vector<uint8_t> id = SealCredentialId(DefaultUser());
+ // The credential id is authenticated with the RP, thus decryption under a
+ // different RP fails.
+ EXPECT_FALSE(CredentialMetadata::UnsealCredentialId(key_, "notacme.com", id));
+}
+
+TEST_F(CredentialMetadataTest, EncodeRpIdAndUserId) {
+ EXPECT_EQ(64u, EncodeRpIdAndUserId("user@acme.com").size())
+ << EncodeRpIdAndUserId("user@acme.com");
+
+ EXPECT_EQ(EncodeRpIdAndUserId("user"), EncodeRpIdAndUserId("user"));
+ EXPECT_NE(EncodeRpIdAndUserId("userA"), EncodeRpIdAndUserId("userB"));
+ EXPECT_NE(EncodeRpIdAndUserId("user"),
+ *CredentialMetadata::EncodeRpIdAndUserId(key_, "notacme.com",
+ to_bytes("user")));
+ EXPECT_NE(EncodeRpIdAndUserId("user"),
+ *CredentialMetadata::EncodeRpIdAndUserId(wrong_key_, rp_id_,
+ to_bytes("user")));
+}
+
+TEST_F(CredentialMetadataTest, EncodeRpId) {
+ EXPECT_EQ(48u, EncodeRpId().size());
+
+ EXPECT_EQ(EncodeRpId(), EncodeRpId());
+ EXPECT_NE(EncodeRpId(), *CredentialMetadata::EncodeRpId(key_, "notacme.com"));
+ EXPECT_NE(EncodeRpId(), *CredentialMetadata::EncodeRpId(wrong_key_, rp_id_));
+}
+
+TEST_F(CredentialMetadataTest, DecodeRpId) {
+ EXPECT_EQ(rp_id_, DecodeRpId(EncodeRpId()));
+ EXPECT_NE(rp_id_,
+ *CredentialMetadata::DecodeRpId(
+ key_, *CredentialMetadata::EncodeRpId(key_, "notacme.com")));
+ EXPECT_FALSE(CredentialMetadata::DecodeRpId(wrong_key_, EncodeRpId()));
+}
+
+TEST(CredentialMetadata, GenerateRandomSecret) {
+ std::string s1 = CredentialMetadata::GenerateRandomSecret();
+ EXPECT_EQ(32u, s1.size());
+ std::string s2 = CredentialMetadata::GenerateRandomSecret();
+ EXPECT_EQ(32u, s2.size());
+ EXPECT_NE(s1, s2);
+}
+
+TEST(CredentialMetadata, FromPublicKeyCredentialUserEntity) {
+ std::vector<uint8_t> user_id = {{1, 2, 3}};
+ PublicKeyCredentialUserEntity in(user_id);
+ in.SetUserName("username");
+ in.SetDisplayName("display name");
+ in.SetIconUrl(GURL("http://rp.foo/user.png"));
+ CredentialMetadata::UserEntity out =
+ CredentialMetadata::UserEntity::FromPublicKeyCredentialUserEntity(
+ std::move(in));
+ EXPECT_EQ(user_id, out.id);
+ EXPECT_EQ("username", out.name);
+ EXPECT_EQ("display name", out.display_name);
+}
+
+TEST(CredentialMetadata, ToPublicKeyCredentialUserEntity) {
+ std::vector<uint8_t> user_id = {{1, 2, 3}};
+ CredentialMetadata::UserEntity in(user_id, "username", "display name");
+ PublicKeyCredentialUserEntity out = in.ToPublicKeyCredentialUserEntity();
+ EXPECT_EQ(user_id, out.user_id());
+ EXPECT_EQ("username", out.user_name().value());
+ EXPECT_EQ("display name", out.user_display_name().value());
+ EXPECT_FALSE(out.user_icon_url().has_value());
+}
+
+} // namespace
+} // 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 a36e2dd09d8..3146c8f9b77 100644
--- a/chromium/device/fido/mac/get_assertion_operation.h
+++ b/chromium/device/fido/mac/get_assertion_operation.h
@@ -31,7 +31,7 @@ class API_AVAILABLE(macosx(10.12.2))
AuthenticatorGetAssertionResponse> {
public:
GetAssertionOperation(CtapGetAssertionRequest request,
- std::string profile_id,
+ std::string metadata_secret,
std::string keychain_access_group,
Callback callback);
~GetAssertionOperation() override;
@@ -40,7 +40,7 @@ class API_AVAILABLE(macosx(10.12.2))
private:
const std::string& RpId() const override;
- void PromptTouchIdDone(bool success, NSError* err) override;
+ void PromptTouchIdDone(bool success) override;
DISALLOW_COPY_AND_ASSIGN(GetAssertionOperation);
};
diff --git a/chromium/device/fido/mac/get_assertion_operation.mm b/chromium/device/fido/mac/get_assertion_operation.mm
index a7a5572c0a0..4f1bec16d51 100644
--- a/chromium/device/fido/mac/get_assertion_operation.mm
+++ b/chromium/device/fido/mac/get_assertion_operation.mm
@@ -16,6 +16,8 @@
#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"
namespace device {
namespace fido {
@@ -24,12 +26,12 @@ namespace mac {
using base::ScopedCFTypeRef;
GetAssertionOperation::GetAssertionOperation(CtapGetAssertionRequest request,
- std::string profile_id,
+ std::string metadata_secret,
std::string keychain_access_group,
Callback callback)
: OperationBase<CtapGetAssertionRequest, AuthenticatorGetAssertionResponse>(
std::move(request),
- std::move(profile_id),
+ std::move(metadata_secret),
std::move(keychain_access_group),
std::move(callback)) {}
GetAssertionOperation::~GetAssertionOperation() = default;
@@ -39,16 +41,19 @@ const std::string& GetAssertionOperation::RpId() const {
}
void GetAssertionOperation::Run() {
+ if (!Init()) {
+ std::move(callback())
+ .Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
+ return;
+ }
+
// Prompt the user for consent.
// TODO(martinkr): Localize reason strings.
PromptTouchId("sign in to " + RpId());
}
-void GetAssertionOperation::PromptTouchIdDone(bool success, NSError* err) {
+void GetAssertionOperation::PromptTouchIdDone(bool success) {
if (!success) {
- // err is autoreleased.
- CHECK(err != nil);
- DVLOG(1) << "Touch ID prompt failed: " << base::mac::NSToCFCast(err);
std::move(callback())
.Run(CtapDeviceResponseCode::kCtap2ErrOperationDenied, base::nullopt);
return;
@@ -120,6 +125,21 @@ void GetAssertionOperation::PromptTouchIdDone(bool success, NSError* err) {
.Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials, base::nullopt);
return;
}
+
+ // Decrypt the user entity from the credential ID.
+ base::Optional<CredentialMetadata::UserEntity> credential_user =
+ CredentialMetadata::UnsealCredentialId(metadata_secret(), RpId(),
+ 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
+ // been decryptable.
+ DVLOG(1) << "UnsealCredentialId failed";
+ std::move(callback())
+ .Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials, base::nullopt);
+ return;
+ }
+
base::ScopedCFTypeRef<SecKeyRef> public_key(
Keychain::GetInstance().KeyCopyPublicKey(private_key));
if (!public_key) {
@@ -130,8 +150,8 @@ void GetAssertionOperation::PromptTouchIdDone(bool success, NSError* err) {
return;
}
- base::Optional<AuthenticatorData> authenticator_data =
- MakeAuthenticatorData(RpId(), std::move(credential_id), public_key);
+ base::Optional<AuthenticatorData> authenticator_data = MakeAuthenticatorData(
+ RpId(), credential_id, SecKeyRefToECPublicKey(public_key));
if (!authenticator_data) {
DLOG(ERROR) << "MakeAuthenticatorData failed";
std::move(callback())
@@ -146,10 +166,14 @@ void GetAssertionOperation::PromptTouchIdDone(bool success, NSError* err) {
.Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
return;
}
+ auto response = AuthenticatorGetAssertionResponse(
+ std::move(*authenticator_data), std::move(*signature));
+ response.SetCredential(PublicKeyCredentialDescriptor(
+ CredentialType::kPublicKey, std::move(credential_id)));
+ response.SetUserEntity(credential_user->ToPublicKeyCredentialUserEntity());
+
std::move(callback())
- .Run(CtapDeviceResponseCode::kSuccess,
- AuthenticatorGetAssertionResponse(std::move(*authenticator_data),
- std::move(*signature)));
+ .Run(CtapDeviceResponseCode::kSuccess, std::move(response));
}
} // namespace mac
diff --git a/chromium/device/fido/mac/get_assertion_operation_unittest_mac.mm b/chromium/device/fido/mac/get_assertion_operation_unittest_mac.mm
index d0bcc7fbcce..62457f7ee97 100644
--- a/chromium/device/fido/mac/get_assertion_operation_unittest_mac.mm
+++ b/chromium/device/fido/mac/get_assertion_operation_unittest_mac.mm
@@ -4,12 +4,15 @@
#include "device/fido/mac/get_assertion_operation.h"
+#include <array>
+
#include <Foundation/Foundation.h>
#include <Security/Security.h>
#include "base/strings/string_number_conversions.h"
#include "base/test/scoped_task_environment.h"
+#include "device/fido/fido_constants.h"
#include "device/fido/mac/make_credential_operation.h"
#include "device/fido/test_callback_receiver.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -22,7 +25,7 @@ namespace {
using test::TestCallbackReceiver;
-const std::vector<uint8_t> kClientDataHash = {1, 2, 3, 4, 5};
+const std::array<uint8_t, kClientDataHashLength> kClientDataHash = {};
const std::string kRpId = "rp.example.com";
const std::vector<uint8_t> kUserId = {10, 11, 12, 13, 14, 15};
const char kKeychainAccessGroup[] =
@@ -75,6 +78,8 @@ API_AVAILABLE(macos(10.12.2)) {
EXPECT_EQ(CtapDeviceResponseCode::kSuccess, error);
auto opt_response = std::move(std::get<1>(result));
ASSERT_TRUE(opt_response);
+ ASSERT_TRUE(opt_response->credential());
+ EXPECT_FALSE(opt_response->credential()->id().empty());
};
}
} // namespace mac
diff --git a/chromium/device/fido/mac/keychain.h b/chromium/device/fido/mac/keychain.h
index 0b627798868..8772cea8997 100644
--- a/chromium/device/fido/mac/keychain.h
+++ b/chromium/device/fido/mac/keychain.h
@@ -8,6 +8,7 @@
#import <Foundation/Foundation.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"
@@ -24,7 +25,7 @@ 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 API_AVAILABLE(macosx(10.12.2)) Keychain {
+class COMPONENT_EXPORT(DEVICE_FIDO) API_AVAILABLE(macosx(10.12.2)) Keychain {
public:
static const Keychain& GetInstance();
diff --git a/chromium/device/fido/mac/make_credential_operation.h b/chromium/device/fido/mac/make_credential_operation.h
index f8d59652cb6..4f9f78c73f2 100644
--- a/chromium/device/fido/mac/make_credential_operation.h
+++ b/chromium/device/fido/mac/make_credential_operation.h
@@ -56,8 +56,11 @@ class API_AVAILABLE(macosx(10.12.2))
void Run() override;
private:
+ // OperationBase:
const std::string& RpId() const override;
- void PromptTouchIdDone(bool success, NSError* err) override;
+ void PromptTouchIdDone(bool success) override;
+
+ base::Optional<std::vector<uint8_t>> GenerateCredentialIdForRequest() const;
};
} // namespace mac
diff --git a/chromium/device/fido/mac/make_credential_operation.mm b/chromium/device/fido/mac/make_credential_operation.mm
index 36e11ff5429..e5669be33cd 100644
--- a/chromium/device/fido/mac/make_credential_operation.mm
+++ b/chromium/device/fido/mac/make_credential_operation.mm
@@ -11,9 +11,10 @@
#include "base/mac/foundation_util.h"
#include "base/mac/mac_logging.h"
#include "base/mac/scoped_cftyperef.h"
-#include "device/fido/fido_attestation_statement.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"
@@ -25,13 +26,13 @@ using base::ScopedCFTypeRef;
MakeCredentialOperation::MakeCredentialOperation(
CtapMakeCredentialRequest request,
- std::string profile_id,
+ std::string metadata_secret,
std::string keychain_access_group,
Callback callback)
: OperationBase<CtapMakeCredentialRequest,
AuthenticatorMakeCredentialResponse>(
std::move(request),
- std::move(profile_id),
+ std::move(metadata_secret),
std::move(keychain_access_group),
std::move(callback)) {}
MakeCredentialOperation::~MakeCredentialOperation() = default;
@@ -41,6 +42,12 @@ const std::string& MakeCredentialOperation::RpId() const {
}
void MakeCredentialOperation::Run() {
+ if (!Init()) {
+ std::move(callback())
+ .Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
+ return;
+ }
+
// Verify pubKeyCredParams contains ES-256, which is the only algorithm we
// support.
auto is_es256 =
@@ -63,11 +70,8 @@ void MakeCredentialOperation::Run() {
PromptTouchId("register with " + RpId());
}
-void MakeCredentialOperation::PromptTouchIdDone(bool success, NSError* err) {
+void MakeCredentialOperation::PromptTouchIdDone(bool success) {
if (!success) {
- // err is autoreleased.
- CHECK(err != nil);
- DVLOG(1) << "Touch ID prompt failed: " << base::mac::NSToCFCast(err);
std::move(callback())
.Run(CtapDeviceResponseCode::kCtap2ErrOperationDenied, base::nullopt);
return;
@@ -102,13 +106,19 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success, NSError* err) {
}
// Delete the key pair for this RP + user handle if one already exists.
- const std::vector<uint8_t> keychain_item_id =
- KeychainItemIdentifier(RpId(), request().user().user_id());
+ base::Optional<std::string> encoded_rp_id_user_id =
+ CredentialMetadata::EncodeRpIdAndUserId(metadata_secret(), RpId(),
+ request().user().user_id());
+ if (!encoded_rp_id_user_id) {
+ // Internal error.
+ std::move(callback())
+ .Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
+ return;
+ }
{
ScopedCFTypeRef<CFMutableDictionaryRef> query = DefaultKeychainQuery();
- CFDictionarySetValue(query, kSecAttrApplicationLabel,
- [NSData dataWithBytes:keychain_item_id.data()
- length:keychain_item_id.size()]);
+ CFDictionarySetValue(query, kSecAttrApplicationTag,
+ base::SysUTF8ToNSString(*encoded_rp_id_user_id));
OSStatus status = Keychain::GetInstance().ItemDelete(query);
if (status != errSecSuccess && status != errSecItemNotFound) {
// Internal keychain error.
@@ -120,6 +130,15 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success, NSError* err) {
}
// Generate the new key pair.
+ base::Optional<std::vector<uint8_t>> credential_id =
+ GenerateCredentialIdForRequest();
+ if (!credential_id) {
+ DLOG(ERROR) << "GenerateCredentialIdForRequest failed";
+ std::move(callback())
+ .Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
+ return;
+ }
+
ScopedCFTypeRef<CFMutableDictionaryRef> params(
CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr));
CFDictionarySetValue(params, kSecAttrKeyType,
@@ -136,9 +155,11 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success, NSError* err) {
access_control());
CFDictionarySetValue(private_key_params, kSecUseAuthenticationContext,
authentication_context());
+ CFDictionarySetValue(private_key_params, kSecAttrApplicationTag,
+ base::SysUTF8ToNSString(*encoded_rp_id_user_id));
CFDictionarySetValue(private_key_params, kSecAttrApplicationLabel,
- [NSData dataWithBytes:keychain_item_id.data()
- length:keychain_item_id.size()]);
+ [NSData dataWithBytes:credential_id->data()
+ length:credential_id->size()]);
ScopedCFTypeRef<CFErrorRef> cferr;
ScopedCFTypeRef<SecKeyRef> private_key(
@@ -161,8 +182,8 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success, NSError* err) {
// Create attestation object. There is no separate attestation key pair, so
// we perform self-attestation.
- base::Optional<AuthenticatorData> authenticator_data =
- MakeAuthenticatorData(RpId(), keychain_item_id, public_key);
+ base::Optional<AuthenticatorData> authenticator_data = MakeAuthenticatorData(
+ RpId(), *credential_id, SecKeyRefToECPublicKey(public_key));
if (!authenticator_data) {
DLOG(ERROR) << "MakeAuthenticatorData failed";
std::move(callback())
@@ -180,13 +201,21 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success, NSError* err) {
std::vector<std::vector<uint8_t>> no_certificates;
AuthenticatorMakeCredentialResponse response(AttestationObject(
std::move(*authenticator_data),
- // TODO(martinkr): Add a PackedAttestationStatement for self-attestation.
- std::make_unique<FidoAttestationStatement>(std::move(*signature),
- std::move(no_certificates))));
+ std::make_unique<PackedAttestationStatement>(
+ CoseAlgorithmIdentifier::kCoseEs256, std::move(*signature),
+ std::move(no_certificates))));
std::move(callback())
.Run(CtapDeviceResponseCode::kSuccess, std::move(response));
}
+base::Optional<std::vector<uint8_t>>
+MakeCredentialOperation::GenerateCredentialIdForRequest() const {
+ return CredentialMetadata::SealCredentialId(
+ metadata_secret(), RpId(),
+ CredentialMetadata::UserEntity::FromPublicKeyCredentialUserEntity(
+ request().user()));
+}
+
} // namespace mac
} // namespace fido
} // namespace device
diff --git a/chromium/device/fido/mac/make_credential_operation_unittest_mac.mm b/chromium/device/fido/mac/make_credential_operation_unittest_mac.mm
index 39d7aa9b65e..183646af040 100644
--- a/chromium/device/fido/mac/make_credential_operation_unittest_mac.mm
+++ b/chromium/device/fido/mac/make_credential_operation_unittest_mac.mm
@@ -4,12 +4,15 @@
#include "device/fido/mac/make_credential_operation.h"
+#include <array>
+
#include <Foundation/Foundation.h>
#include <Security/Security.h>
#include "base/strings/string_number_conversions.h"
#include "base/test/scoped_task_environment.h"
+#include "device/fido/fido_constants.h"
#include "device/fido/test_callback_receiver.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -21,7 +24,7 @@ namespace {
using test::TestCallbackReceiver;
-const std::vector<uint8_t> kClientDataHash = {1, 2, 3, 4, 5};
+const std::array<uint8_t, kClientDataHashLength> kClientDataHash = {};
const std::string kRpId = "rp.example.com";
const std::vector<uint8_t> kUserId = {10, 11, 12, 13, 14, 15};
const char kKeychainAccessGroup[] =
diff --git a/chromium/device/fido/mac/operation_base.h b/chromium/device/fido/mac/operation_base.h
index 5112cbf8017..545252023a5 100644
--- a/chromium/device/fido/mac/operation_base.h
+++ b/chromium/device/fido/mac/operation_base.h
@@ -13,6 +13,7 @@
#include "base/mac/scoped_cftyperef.h"
#include "base/macros.h"
#include "base/strings/sys_string_conversions.h"
+#include "device/fido/mac/credential_metadata.h"
#include "device/fido/mac/operation.h"
#include "device/fido/mac/touch_id_context.h"
@@ -29,17 +30,29 @@ class API_AVAILABLE(macosx(10.12.2)) OperationBase : public Operation {
base::Optional<Response>)>;
OperationBase(Request request,
- std::string profile_id,
+ std::string metadata_secret,
std::string keychain_access_group,
Callback callback)
: request_(std::move(request)),
- profile_id_(std::move(profile_id)),
+ 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>()) {}
+
~OperationBase() override = default;
protected:
+ // Subclasses must call Init() at the beginning of Run().
+ bool Init() {
+ base::Optional<std::string> encoded_rp_id =
+ CredentialMetadata::EncodeRpId(metadata_secret(), RpId());
+ if (!encoded_rp_id)
+ return false;
+
+ encoded_rp_id_ = std::move(*encoded_rp_id);
+ return true;
+ }
+
// PromptTouchId triggers a Touch ID consent dialog with the given reason
// string. Subclasses implement the PromptTouchIdDone callback to receive the
// result.
@@ -52,8 +65,8 @@ class API_AVAILABLE(macosx(10.12.2)) OperationBase : public Operation {
base::Unretained(this)));
}
- // Callback for |PromptTouchId|. Any NSError that gets passed is autoreleased.
- virtual void PromptTouchIdDone(bool success, NSError* err) = 0;
+ // Callback for |PromptTouchId|.
+ virtual void PromptTouchIdDone(bool success) = 0;
// Subclasses override RpId to return the RP ID from the type-specific
// request.
@@ -67,28 +80,33 @@ class API_AVAILABLE(macosx(10.12.2)) OperationBase : public Operation {
}
// DefaultKeychainQuery returns a default keychain query dictionary that has
- // the keychain item class, profile ID and RP ID filled out (but not the
- // credential ID). More fields can be set on the return value to refine the
- // query.
+ // the keychain item class, keychain access group and RP ID filled out (but
+ // not the credential ID). More fields can be set on the return value to
+ // refine the query.
base::ScopedCFTypeRef<CFMutableDictionaryRef> DefaultKeychainQuery() const {
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(RpId()));
- CFDictionarySetValue(query, kSecAttrApplicationTag,
- base::SysUTF8ToNSString(profile_id_));
+ DCHECK(!encoded_rp_id_.empty());
+ CFDictionarySetValue(query, kSecAttrLabel,
+ base::SysUTF8ToNSString(encoded_rp_id_));
return query;
}
+ const std::string& metadata_secret() const { return metadata_secret_; }
+
const Request& request() const { return request_; }
Callback& callback() { return callback_; }
private:
Request request_;
- std::string profile_id_;
+ // The secret parameter passed to |CredentialMetadata| operations to encrypt
+ // or encode credential metadata for storage in the macOS keychain.
+ std::string metadata_secret_;
std::string keychain_access_group_;
+ std::string encoded_rp_id_ = "";
Callback callback_;
std::unique_ptr<TouchIdContext> touch_id_context_;
diff --git a/chromium/device/fido/mac/touch_id_context.h b/chromium/device/fido/mac/touch_id_context.h
index 29133b482d2..e54f64d3d85 100644
--- a/chromium/device/fido/mac/touch_id_context.h
+++ b/chromium/device/fido/mac/touch_id_context.h
@@ -23,9 +23,8 @@ namespace mac {
class API_AVAILABLE(macosx(10.12.2)) TouchIdContext {
public:
// The callback is invoked when the Touch ID prompt completes. It receives a
- // boolean indicating success and an autoreleased NSError if the prompt was
- // denied or failed.
- using Callback = base::OnceCallback<void(bool, NSError*)>;
+ // boolean indicating whether obtaining the fingerprint was successful.
+ using Callback = base::OnceCallback<void(bool)>;
TouchIdContext();
~TouchIdContext();
@@ -44,6 +43,8 @@ class API_AVAILABLE(macosx(10.12.2)) TouchIdContext {
SecAccessControlRef access_control() const { return access_control_; }
private:
+ void RunCallback(bool success);
+
base::scoped_nsobject<LAContext> context_;
base::ScopedCFTypeRef<SecAccessControlRef> access_control_;
Callback callback_;
diff --git a/chromium/device/fido/mac/touch_id_context.mm b/chromium/device/fido/mac/touch_id_context.mm
index 8a31f2bca26..d196ea59c6f 100644
--- a/chromium/device/fido/mac/touch_id_context.mm
+++ b/chromium/device/fido/mac/touch_id_context.mm
@@ -6,7 +6,12 @@
#import <Foundation/Foundation.h>
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/sequenced_task_runner.h"
#include "base/strings/sys_string_conversions.h"
+#include "base/threading/sequenced_task_runner_handle.h"
namespace device {
namespace fido {
@@ -29,10 +34,17 @@ TouchIdContext::TouchIdContext()
callback_(),
weak_ptr_factory_(this) {}
-TouchIdContext::~TouchIdContext() = default;
+TouchIdContext::~TouchIdContext() {
+ // Invalidating the LAContext will dismiss any pending UI dialog (e.g. if the
+ // transaction was cancelled while we are waiting for a fingerprint). Simply
+ // releasing the LAContext does not have the same effect.
+ [context_ invalidate];
+}
void TouchIdContext::PromptTouchId(std::string reason, Callback callback) {
callback_ = std::move(callback);
+ scoped_refptr<base::SequencedTaskRunner> runner =
+ base::SequencedTaskRunnerHandle::Get();
auto weak_self = weak_ptr_factory_.GetWeakPtr();
// If evaluation succeeds (i.e. user provides a fingerprint), |context_| can
// be used for one signing operation. N.B. even in |MakeCredentialOperation|,
@@ -42,13 +54,35 @@ void TouchIdContext::PromptTouchId(std::string reason, Callback callback) {
operation:LAAccessControlOperationUseKeySign
localizedReason:base::SysUTF8ToNSString(reason)
reply:^(BOOL success, NSError* error) {
- if (!weak_self) {
- return;
+ // The reply block is invoked in a separate
+ // thread. We want to invoke the callback in the
+ // original thread, so we post it onto the
+ // originating runner. The weak_self and runner
+ // variables inside the block are const-copies of
+ // the ones in the enclosing scope (c.f.
+ // http://clang.llvm.org/docs/Block-ABI-Apple.html#imported-variables).
+ if (!success) {
+ // |error| is autoreleased in this block. It
+ // is not currently passed onto the other
+ // thread running the callback; but if it
+ // were, it would have to be retained first
+ // (and probably captured in a
+ // scoped_nsobject<NSError>).
+ DCHECK(error != nil);
+ DVLOG(1) << "Touch ID prompt failed: "
+ << base::mac::NSToCFCast(error);
}
- std::move(callback_).Run(success, error);
+ runner->PostTask(
+ FROM_HERE,
+ base::BindOnce(&TouchIdContext::RunCallback,
+ weak_self, success));
}];
}
+void TouchIdContext::RunCallback(bool success) {
+ std::move(callback_).Run(success);
+}
+
} // namespace mac
} // namespace fido
} // namespace device
diff --git a/chromium/device/fido/mac/util.h b/chromium/device/fido/mac/util.h
index fe0935ccc35..6f6d8caa3dc 100644
--- a/chromium/device/fido/mac/util.h
+++ b/chromium/device/fido/mac/util.h
@@ -5,33 +5,32 @@
#ifndef DEVICE_FIDO_MAC_UTIL_H_
#define DEVICE_FIDO_MAC_UTIL_H_
+#include <memory>
#include <string>
#include <vector>
#import <Security/Security.h>
#include "base/callback.h"
+#include "base/component_export.h"
+#include "base/containers/span.h"
#include "base/mac/availability.h"
-#include "base/mac/scoped_cftyperef.h"
-#include "base/mac/scoped_nsobject.h"
+#include "device/fido/authenticator_data.h"
+#include "device/fido/ec_public_key.h"
+#include "device/fido/fido_constants.h"
namespace device {
namespace fido {
namespace mac {
-// KeychainItemIdentifier returns the unique identifier for a key pair, derived
-// from an RP ID and user handle. It is stored in the keychain items
-// kSecAttrApplicationLabel attribute and can be used for lookup.
-std::vector<uint8_t> KeychainItemIdentifier(std::string rp_id,
- std::vector<uint8_t> user_id);
-
// MakeAuthenticatorData returns an AuthenticatorData instance for the Touch ID
// authenticator with the given Relying Party ID, credential ID and public key.
// It returns |base::nullopt| on failure.
+COMPONENT_EXPORT(DEVICE_FIDO)
base::Optional<AuthenticatorData> MakeAuthenticatorData(
const std::string& rp_id,
std::vector<uint8_t> credential_id,
- SecKeyRef public_key) API_AVAILABLE(macosx(10.12.2));
+ std::unique_ptr<ECPublicKey> public_key);
// GenerateSignature signs the concatenation of the serialization of the given
// authenticator data and the given client data hash, as required for
@@ -39,9 +38,15 @@ base::Optional<AuthenticatorData> MakeAuthenticatorData(
// fails.
base::Optional<std::vector<uint8_t>> GenerateSignature(
const AuthenticatorData& authenticator_data,
- const std::vector<uint8_t>& client_data_hash,
+ base::span<const uint8_t, kClientDataHashLength> client_data_hash,
SecKeyRef private_key) API_AVAILABLE(macosx(10.12.2));
+// SecKeyRefToECPublicKey converts a SecKeyRef for a public key into an
+// equivalent |ECPublicKey| instance. It returns |nullptr| if the key cannot be
+// converted.
+std::unique_ptr<ECPublicKey> SecKeyRefToECPublicKey(SecKeyRef public_key_ref)
+ API_AVAILABLE(macosx(10.12.2));
+
std::vector<uint8_t> TouchIdAaguid();
} // namespace mac
diff --git a/chromium/device/fido/mac/util.mm b/chromium/device/fido/mac/util.mm
index 38b71a5556d..af4f61051a6 100644
--- a/chromium/device/fido/mac/util.mm
+++ b/chromium/device/fido/mac/util.mm
@@ -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/mac/get_assertion_operation.h"
+#include "device/fido/mac/util.h"
#include <array>
#include <set>
@@ -18,7 +18,6 @@
#include "base/strings/string_number_conversions.h"
#include "components/cbor/cbor_writer.h"
#include "device/fido/ec_public_key.h"
-#include "device/fido/fido_attestation_statement.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_parsing_utils.h"
#include "device/fido/mac/keychain.h"
@@ -41,11 +40,10 @@ std::vector<uint8_t> TouchIdAaguid() {
return std::vector<uint8_t>(kAaguid.begin(), kAaguid.end());
}
-namespace {
-
-// MakeECPublicKey converts a SecKeyRef for a public key into an equivalent
-// |ECPublicKey| instance. It returns |nullptr| if the key cannot be converted.
-std::unique_ptr<ECPublicKey> MakeECPublicKey(SecKeyRef public_key_ref)
+// SecKeyRefToECPublicKey converts a SecKeyRef for a public key into an
+// equivalent |ECPublicKey| instance. It returns |nullptr| if the key cannot be
+// converted.
+std::unique_ptr<ECPublicKey> SecKeyRefToECPublicKey(SecKeyRef public_key_ref)
API_AVAILABLE(macosx(10.12.2)) {
CHECK(public_key_ref);
ScopedCFTypeRef<CFErrorRef> err;
@@ -67,51 +65,56 @@ std::unique_ptr<ECPublicKey> MakeECPublicKey(SecKeyRef public_key_ref)
return key;
}
-} // namespace
+namespace {
-// KeychainItemIdentifier returns the unique identifier for a given RP ID
-// and user handle. It is stored in the keychain items Application Label and
-// used for later lookup.
-std::vector<uint8_t> KeychainItemIdentifier(std::string rp_id,
- std::vector<uint8_t> user_id) {
- std::vector<CBORValue> array;
- array.emplace_back(CBORValue(rp_id));
- array.emplace_back(CBORValue(user_id));
- auto value = CBORWriter::Write(CBORValue(std::move(array)));
- CHECK(value);
- return *value;
+// Returns the current time in seconds since epoch as a privacy-preserving
+// signature counter. Because of the conversion to a 32-bit unsigned integer,
+// the counter will overflow in the year 2108.
+std::array<uint8_t, 4> GetTimestampSignatureCounter() {
+ // TODO(martinkr): The timestamp somewhat defeats the supposed "cloning
+ // detection" properties of a less predictable counter. If we do want real
+ // counters, they should be at least per RP and could probably be stored in
+ // PrefService.
+ uint32_t sign_counter = static_cast<uint32_t>(base::Time::Now().ToDoubleT());
+ return std::array<uint8_t, 4>{
+ static_cast<uint8_t>((sign_counter >> 24) & 0xff),
+ static_cast<uint8_t>((sign_counter >> 16) & 0xff),
+ static_cast<uint8_t>((sign_counter >> 8) & 0xff),
+ static_cast<uint8_t>(sign_counter & 0xff),
+ };
}
+} // namespace
+
base::Optional<AuthenticatorData> MakeAuthenticatorData(
const std::string& rp_id,
std::vector<uint8_t> credential_id,
- SecKeyRef public_key) API_AVAILABLE(macosx(10.12.2)) {
- if (credential_id.size() > 255) {
- LOG(ERROR) << "credential id too long: "
+ std::unique_ptr<ECPublicKey> public_key) {
+ if (credential_id.empty() || credential_id.size() > 255) {
+ LOG(ERROR) << "invalid credential id: "
<< base::HexEncode(credential_id.data(), credential_id.size());
return base::nullopt;
}
+ if (!public_key) {
+ LOG(ERROR) << "public key cannot be null";
+ return base::nullopt;
+ }
std::array<uint8_t, 2> encoded_credential_id_length = {
0, static_cast<uint8_t>(credential_id.size())};
constexpr uint8_t flags =
+ static_cast<uint8_t>(AuthenticatorData::Flag::kTestOfUserPresence) |
static_cast<uint8_t>(AuthenticatorData::Flag::kTestOfUserVerification) |
static_cast<uint8_t>(AuthenticatorData::Flag::kAttestation);
- std::vector<uint8_t> counter = {0, 0, 0, 0}; // implement
- auto ec_public_key = MakeECPublicKey(public_key);
- if (!ec_public_key) {
- LOG(ERROR) << "MakeECPublicKey failed";
- return base::nullopt;
- }
return AuthenticatorData(
- fido_parsing_utils::CreateSHA256Hash(rp_id), flags, counter,
+ fido_parsing_utils::CreateSHA256Hash(rp_id), flags,
+ GetTimestampSignatureCounter(),
AttestedCredentialData(kAaguid, encoded_credential_id_length,
- std::move(credential_id),
- std::move(ec_public_key)));
+ std::move(credential_id), std::move(public_key)));
}
base::Optional<std::vector<uint8_t>> GenerateSignature(
const AuthenticatorData& authenticator_data,
- const std::vector<uint8_t>& client_data_hash,
+ base::span<const uint8_t, kClientDataHashLength> client_data_hash,
SecKeyRef private_key) API_AVAILABLE(macosx(10.12.2)) {
const std::vector<uint8_t> serialized_authenticator_data =
authenticator_data.SerializeToByteArray();
diff --git a/chromium/device/fido/mac/util_unittest.cc b/chromium/device/fido/mac/util_unittest.cc
new file mode 100644
index 00000000000..99b5610d700
--- /dev/null
+++ b/chromium/device/fido/mac/util_unittest.cc
@@ -0,0 +1,54 @@
+// 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/util.h"
+
+#include "base/time/time.h"
+#include "base/time/time_override.h"
+#include "device/fido/authenticator_data.h"
+#include "device/fido/ec_public_key.h"
+#include "device/fido/fido_parsing_utils.h"
+#include "device/fido/fido_test_data.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::ElementsAre;
+
+namespace device {
+namespace fido {
+namespace mac {
+namespace {
+
+std::unique_ptr<ECPublicKey> TestKey() {
+ return ECPublicKey::ParseX962Uncompressed(
+ fido_parsing_utils::kEs256, test_data::kX962UncompressedPublicKey);
+}
+
+base::Time g_fake_now;
+
+TEST(MakeAuthenticatorDataTest, TestTimestampSignatureCounter) {
+ g_fake_now = base::Time::UnixEpoch();
+ base::subtle::ScopedTimeClockOverrides time_clock_overrides(
+ []() { return g_fake_now; }, nullptr, nullptr);
+ const std::string rp_id = "example.com";
+ const std::vector<uint8_t> credential_id = {1, 2, 3, 4, 5};
+ // Epoch equals zero.
+ auto opt_auth_data = MakeAuthenticatorData(rp_id, credential_id, TestKey());
+ EXPECT_THAT(opt_auth_data->counter(), ElementsAre(0x00, 0x00, 0x00, 0x00));
+ // Time counter increments in seconds.
+ g_fake_now += base::TimeDelta::FromSeconds(1);
+ opt_auth_data = MakeAuthenticatorData(rp_id, credential_id, TestKey());
+ EXPECT_THAT(opt_auth_data->counter(), ElementsAre(0x00, 0x00, 0x00, 0x01));
+ g_fake_now += base::TimeDelta::FromSeconds(1024);
+ opt_auth_data = MakeAuthenticatorData(rp_id, credential_id, TestKey());
+ EXPECT_THAT(opt_auth_data->counter(), ElementsAre(0x00, 0x00, 0x04, 0x01));
+ ASSERT_TRUE(base::Time::FromUTCExploded({2106, 1, 0, 1}, &g_fake_now));
+ opt_auth_data = MakeAuthenticatorData(rp_id, credential_id, TestKey());
+ EXPECT_THAT(opt_auth_data->counter(), ElementsAre(0xff, 0xce, 0xdd, 0x80));
+}
+
+} // namespace
+} // namespace mac
+} // namespace fido
+} // namespace device
diff --git a/chromium/device/fido/make_credential_handler_unittest.cc b/chromium/device/fido/make_credential_handler_unittest.cc
index 7f8bee04a48..f5b277db77b 100644
--- a/chromium/device/fido/make_credential_handler_unittest.cc
+++ b/chromium/device/fido/make_credential_handler_unittest.cc
@@ -5,7 +5,9 @@
#include <memory>
#include <utility>
+#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
+#include "device/base/features.h"
#include "device/fido/authenticator_make_credential_response.h"
#include "device/fido/authenticator_selection_criteria.h"
#include "device/fido/ctap_make_credential_request.h"
@@ -27,10 +29,6 @@ namespace device {
namespace {
-constexpr uint8_t kClientDataHash[] = {0x01, 0x02, 0x03};
-constexpr uint8_t kUserId[] = {0x01, 0x02, 0x03};
-constexpr char kRpId[] = "acme.com";
-
using TestMakeCredentialRequestCallback = test::StatusAndValueCallbackReceiver<
FidoReturnCode,
base::Optional<AuthenticatorMakeCredentialResponse>>;
@@ -44,29 +42,41 @@ class FidoMakeCredentialHandlerTest : public ::testing::Test {
}
std::unique_ptr<MakeCredentialRequestHandler> CreateMakeCredentialHandler() {
+ return CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria());
+ }
+
+ std::unique_ptr<MakeCredentialRequestHandler>
+ CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria authenticator_selection_criteria) {
ForgeNextHidDiscovery();
- PublicKeyCredentialRpEntity rp(kRpId);
+ PublicKeyCredentialRpEntity rp(test_data::kRelyingPartyId);
PublicKeyCredentialUserEntity user(
- fido_parsing_utils::Materialize(kUserId));
+ fido_parsing_utils::Materialize(test_data::kUserId));
PublicKeyCredentialParams credential_params(
std::vector<PublicKeyCredentialParams::CredentialInfo>(1));
auto request_parameter = CtapMakeCredentialRequest(
- fido_parsing_utils::Materialize(kClientDataHash), std::move(rp),
- std::move(user), std::move(credential_params));
+ 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), AuthenticatorSelectionCriteria(),
- cb_.callback());
+ std::move(request_parameter),
+ std::move(authenticator_selection_criteria), cb_.callback());
+ }
+
+ void InitFeatureListAndDisableCtapFlag() {
+ scoped_feature_list_.InitAndDisableFeature(kNewCtap2Device);
}
test::FakeFidoDiscovery* discovery() const { return discovery_; }
TestMakeCredentialRequestCallback& callback() { return cb_; }
protected:
+ base::test::ScopedFeatureList scoped_feature_list_;
base::test::ScopedTaskEnvironment scoped_task_environment_{
base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
test::ScopedFakeFidoDiscoveryFactory scoped_fake_discovery_factory_;
@@ -74,7 +84,7 @@ class FidoMakeCredentialHandlerTest : public ::testing::Test {
TestMakeCredentialRequestCallback cb_;
};
-TEST_F(FidoMakeCredentialHandlerTest, TestMakeCredentialRequestHandler) {
+TEST_F(FidoMakeCredentialHandlerTest, TestCtap2MakeCredentialWithFlagEnabled) {
auto request_handler = CreateMakeCredentialHandler();
discovery()->WaitForCallToStartAndSimulateSuccess();
@@ -93,21 +103,219 @@ TEST_F(FidoMakeCredentialHandlerTest, TestMakeCredentialRequestHandler) {
EXPECT_TRUE(request_handler->is_complete());
}
-// Test a scenario where the connected authenticator is a U2F device. Request
-// be silently dropped and request should remain in incomplete state.
-TEST_F(FidoMakeCredentialHandlerTest,
- TestMakeCredentialIncorrectGetInfoResponse) {
+// Test a scenario where the connected authenticator is a U2F device.
+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);
+ 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());
+}
+
+// Test a scenario where the connected authenticator is a U2F device using a
+// logic that defaults to handling U2F devices.
+TEST_F(FidoMakeCredentialHandlerTest, TestU2fRegisterWithoutFlagEnabled) {
+ InitFeatureListAndDisableCtapFlag();
auto request_handler = CreateMakeCredentialHandler();
discovery()->WaitForCallToStartAndSimulateSuccess();
auto device = std::make_unique<MockFidoDevice>();
EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
+ 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());
+}
+
+TEST_F(FidoMakeCredentialHandlerTest, U2fRegisterWithUserVerificationRequired) {
+ 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, base::nullopt);
+
+ discovery()->AddDevice(std::move(device));
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(callback().was_called());
+}
+
+TEST_F(FidoMakeCredentialHandlerTest,
+ U2fRegisterWithPlatformDeviceRequirement) {
+ auto request_handler =
+ CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria::AuthenticatorAttachment::
+ kPlatform,
+ 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, base::nullopt);
+
+ discovery()->AddDevice(std::move(device));
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(callback().was_called());
+}
+
+TEST_F(FidoMakeCredentialHandlerTest, U2fRegisterWithResidentKeyRequirement) {
+ auto request_handler =
+ CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria(
+ 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);
discovery()->AddDevice(std::move(device));
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(callback().was_called());
+}
+
+TEST_F(FidoMakeCredentialHandlerTest,
+ UserVerificationAuthenticatorSelectionCriteria) {
+ 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));
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(callback().was_called());
+}
+
+TEST_F(FidoMakeCredentialHandlerTest,
+ PlatformDeviceAuthenticatorSelectionCriteria) {
+ auto request_handler =
+ CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria::AuthenticatorAttachment::
+ 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);
+
+ discovery()->AddDevice(std::move(device));
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(callback().was_called());
+}
+
+TEST_F(FidoMakeCredentialHandlerTest,
+ ResidentKeyAuthenticatorSelectionCriteria) {
+ auto request_handler =
+ CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria(
+ 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,
+ test_data::kTestGetInfoResponseWithoutResidentKeySupport);
+
+ discovery()->AddDevice(std::move(device));
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(callback().was_called());
+}
+
+TEST_F(FidoMakeCredentialHandlerTest,
+ SatisfyAllAuthenticatorSelectionCriteria) {
+ auto request_handler =
+ CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria(
+ AuthenticatorSelectionCriteria::AuthenticatorAttachment::
+ kPlatform,
+ true /* 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::kTestAuthenticatorGetInfoResponse);
+ device->ExpectCtap2CommandAndRespondWith(
+ CtapRequestCommand::kAuthenticatorMakeCredential,
+ test_data::kTestMakeCredentialResponse);
+
+ discovery()->AddDevice(std::move(device));
+
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
+ callback().WaitForCallback();
+ EXPECT_EQ(FidoReturnCode::kSuccess, callback().status());
+}
+
+TEST_F(FidoMakeCredentialHandlerTest, IncompatibleUserVerificationSetting) {
+ 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));
+
scoped_task_environment_.FastForwardUntilNoTasksRemain();
- EXPECT_FALSE(request_handler->is_complete());
+ EXPECT_FALSE(callback().was_called());
}
} // namespace device
diff --git a/chromium/device/fido/make_credential_request_handler.cc b/chromium/device/fido/make_credential_request_handler.cc
index f9c16af664c..804bb75f5a3 100644
--- a/chromium/device/fido/make_credential_request_handler.cc
+++ b/chromium/device/fido/make_credential_request_handler.cc
@@ -17,11 +17,28 @@ namespace device {
MakeCredentialRequestHandler::MakeCredentialRequestHandler(
service_manager::Connector* connector,
const base::flat_set<FidoTransportProtocol>& protocols,
- CtapMakeCredentialRequest request_parameter,
+ CtapMakeCredentialRequest request,
AuthenticatorSelectionCriteria authenticator_selection_criteria,
RegisterResponseCallback completion_callback)
- : FidoRequestHandler(connector, protocols, std::move(completion_callback)),
- request_parameter_(std::move(request_parameter)),
+ : 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) {
@@ -30,10 +47,58 @@ MakeCredentialRequestHandler::MakeCredentialRequestHandler(
MakeCredentialRequestHandler::~MakeCredentialRequestHandler() = default;
+namespace {
+
+bool CheckIfAuthenticatorSelectionCriteriaAreSatisfied(
+ FidoAuthenticator* authenticator,
+ const AuthenticatorSelectionCriteria& authenticator_selection_criteria,
+ CtapMakeCredentialRequest* request) {
+ using AuthenticatorAttachment =
+ AuthenticatorSelectionCriteria::AuthenticatorAttachment;
+ using UvAvailability =
+ AuthenticatorSupportedOptions::UserVerificationAvailability;
+
+ const auto& options = authenticator->Options();
+ if ((authenticator_selection_criteria.authenticator_attachement() ==
+ AuthenticatorAttachment::kPlatform &&
+ !options.is_platform_device()) ||
+ (authenticator_selection_criteria.authenticator_attachement() ==
+ AuthenticatorAttachment::kCrossPlatform &&
+ options.is_platform_device())) {
+ return false;
+ }
+
+ if (authenticator_selection_criteria.require_resident_key() &&
+ !options.supports_resident_key()) {
+ return false;
+ }
+
+ const auto& user_verification_requirement =
+ authenticator_selection_criteria.user_verification_requirement();
+ if (user_verification_requirement == UserVerificationRequirement::kRequired) {
+ request->SetUserVerificationRequired(true);
+ }
+
+ return user_verification_requirement !=
+ UserVerificationRequirement::kRequired ||
+ options.user_verification_availability() ==
+ UvAvailability::kSupportedAndConfigured;
+}
+
+} // namespace
+
void MakeCredentialRequestHandler::DispatchRequest(
FidoAuthenticator* authenticator) {
- return authenticator->MakeCredential(
- authenticator_selection_criteria_, request_parameter_,
+ // The user verification field of the request may be adjusted to the
+ // authenticator, so we need to make a copy.
+ CtapMakeCredentialRequest request_copy = request_parameter_;
+ if (!CheckIfAuthenticatorSelectionCriteriaAreSatisfied(
+ authenticator, authenticator_selection_criteria_, &request_copy)) {
+ return;
+ }
+
+ authenticator->MakeCredential(
+ std::move(request_copy),
base::BindOnce(&MakeCredentialRequestHandler::OnAuthenticatorResponse,
weak_factory_.GetWeakPtr(), authenticator));
}
diff --git a/chromium/device/fido/make_credential_request_handler.h b/chromium/device/fido/make_credential_request_handler.h
index 23cc5b6178f..6edfdc0f926 100644
--- a/chromium/device/fido/make_credential_request_handler.h
+++ b/chromium/device/fido/make_credential_request_handler.h
@@ -39,6 +39,13 @@ class COMPONENT_EXPORT(DEVICE_FIDO) MakeCredentialRequestHandler
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;
private:
diff --git a/chromium/device/fido/make_credential_task.cc b/chromium/device/fido/make_credential_task.cc
index 4c966ebd331..157116cbaa3 100644
--- a/chromium/device/fido/make_credential_task.cc
+++ b/chromium/device/fido/make_credential_task.cc
@@ -7,56 +7,87 @@
#include <utility>
#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/ctap_register_operation.h"
#include "device/fido/device_response_converter.h"
+#include "device/fido/u2f_command_constructor.h"
+#include "device/fido/u2f_register_operation.h"
namespace device {
+namespace {
+
+// Checks whether the incoming MakeCredential request has ClientPin option that
+// is compatible with the Chrome's CTAP2 implementation. According to the CTAP
+// spec, CTAP2 authenticators that have client pin set will always error out on
+// MakeCredential request when "pinAuth" parameter in the request is not set. As
+// ClientPin is not supported on Chrome yet, this check allows Chrome to avoid
+// such failures if possible by defaulting to U2F request when user verification
+// is not required and the device supports U2F protocol.
+// TODO(hongjunchoi): Remove this once ClientPin command is implemented.
+// See: https://crbug.com/870892
+bool IsClientPinOptionCompatible(const FidoDevice* device,
+ const CtapMakeCredentialRequest& request) {
+ if (request.user_verification_required())
+ return true;
+
+ DCHECK(device && device->device_info());
+ bool client_pin_set =
+ device->device_info()->options().client_pin_availability() ==
+ AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedAndPinSet;
+ bool supports_u2f = base::ContainsKey(device->device_info()->versions(),
+ ProtocolVersion::kU2f);
+ return !client_pin_set || !supports_u2f;
+}
+
+} // namespace
+
MakeCredentialTask::MakeCredentialTask(
FidoDevice* device,
CtapMakeCredentialRequest request_parameter,
- AuthenticatorSelectionCriteria authenticator_selection_criteria,
MakeCredentialTaskCallback callback)
: FidoTask(device),
request_parameter_(std::move(request_parameter)),
- authenticator_selection_criteria_(
- std::move(authenticator_selection_criteria)),
callback_(std::move(callback)),
weak_factory_(this) {}
MakeCredentialTask::~MakeCredentialTask() = default;
void MakeCredentialTask::StartTask() {
- GetAuthenticatorInfo(base::BindOnce(&MakeCredentialTask::MakeCredential,
- weak_factory_.GetWeakPtr()),
- base::BindOnce(&MakeCredentialTask::U2fRegister,
- weak_factory_.GetWeakPtr()));
+ if (base::FeatureList::IsEnabled(kNewCtap2Device) &&
+ device()->supported_protocol() == ProtocolVersion::kCtap &&
+ IsClientPinOptionCompatible(device(), request_parameter_)) {
+ MakeCredential();
+ } else {
+ U2fRegister();
+ }
}
void MakeCredentialTask::MakeCredential() {
- if (!CheckIfAuthenticatorSelectionCriteriaAreSatisfied()) {
+ register_operation_ = std::make_unique<Ctap2DeviceOperation<
+ CtapMakeCredentialRequest, AuthenticatorMakeCredentialResponse>>(
+ device(), request_parameter_,
+ base::BindOnce(&MakeCredentialTask::OnCtapMakeCredentialResponseReceived,
+ weak_factory_.GetWeakPtr()),
+ base::BindOnce(&ReadCTAPMakeCredentialResponse));
+ register_operation_->Start();
+}
+
+void MakeCredentialTask::U2fRegister() {
+ if (!IsConvertibleToU2fRegisterCommand(request_parameter_)) {
std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
base::nullopt);
return;
}
- register_operation_ = std::make_unique<CtapRegisterOperation>(
- device(), &request_parameter_,
+ register_operation_ = std::make_unique<U2fRegisterOperation>(
+ device(), request_parameter_,
base::BindOnce(&MakeCredentialTask::OnCtapMakeCredentialResponseReceived,
weak_factory_.GetWeakPtr()));
register_operation_->Start();
}
-void MakeCredentialTask::U2fRegister() {
- // TODO(hongjunchoi): Implement U2F register request logic to support
- // interoperability with U2F protocol. Currently all U2F devices are not
- // supported and request to U2F devices will be silently dropped.
- // See: https://crbug.com/798573
- std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
- base::nullopt);
-}
-
void MakeCredentialTask::OnCtapMakeCredentialResponseReceived(
CtapDeviceResponseCode return_code,
base::Optional<AuthenticatorMakeCredentialResponse> response_data) {
@@ -65,6 +96,8 @@ void MakeCredentialTask::OnCtapMakeCredentialResponseReceived(
return;
}
+ // TODO(martinkr): CheckRpIdHash invocation needs to move into the Request
+ // handler. See https://crbug.com/863988.
if (!response_data ||
!response_data->CheckRpIdHash(request_parameter_.rp().rp_id())) {
std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
@@ -75,48 +108,4 @@ void MakeCredentialTask::OnCtapMakeCredentialResponseReceived(
std::move(callback_).Run(return_code, std::move(response_data));
}
-bool MakeCredentialTask::CheckIfAuthenticatorSelectionCriteriaAreSatisfied() {
- using AuthenticatorAttachment =
- AuthenticatorSelectionCriteria::AuthenticatorAttachment;
- using UvAvailability =
- AuthenticatorSupportedOptions::UserVerificationAvailability;
-
- // U2F authenticators are non-platform devices that do not support resident
- // key or user verification.
- const auto& device_info = device()->device_info();
- if (!device_info) {
- return !authenticator_selection_criteria_.require_resident_key() &&
- authenticator_selection_criteria_.user_verification_requirement() !=
- UserVerificationRequirement::kRequired &&
- authenticator_selection_criteria_.authenticator_attachement() !=
- AuthenticatorAttachment::kPlatform;
- }
-
- const auto& options = device_info->options();
- if ((authenticator_selection_criteria_.authenticator_attachement() ==
- AuthenticatorAttachment::kPlatform &&
- !options.is_platform_device()) ||
- (authenticator_selection_criteria_.authenticator_attachement() ==
- AuthenticatorAttachment::kCrossPlatform &&
- options.is_platform_device())) {
- return false;
- }
-
- if (authenticator_selection_criteria_.require_resident_key() &&
- !options.supports_resident_key()) {
- return false;
- }
-
- const auto user_verification_requirement =
- authenticator_selection_criteria_.user_verification_requirement();
- if (user_verification_requirement == UserVerificationRequirement::kRequired) {
- request_parameter_.SetUserVerificationRequired(true);
- }
-
- return user_verification_requirement !=
- UserVerificationRequirement::kRequired ||
- options.user_verification_availability() ==
- UvAvailability::kSupportedAndConfigured;
-}
-
} // namespace device
diff --git a/chromium/device/fido/make_credential_task.h b/chromium/device/fido/make_credential_task.h
index 25025892edf..fc8ae97d776 100644
--- a/chromium/device/fido/make_credential_task.h
+++ b/chromium/device/fido/make_credential_task.h
@@ -16,9 +16,8 @@
#include "base/memory/weak_ptr.h"
#include "base/optional.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/ctap_register_operation.h"
+#include "device/fido/device_operation.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_task.h"
@@ -31,10 +30,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) MakeCredentialTask : public FidoTask {
using MakeCredentialTaskCallback = base::OnceCallback<void(
CtapDeviceResponseCode,
base::Optional<AuthenticatorMakeCredentialResponse>)>;
+ using RegisterOperation =
+ DeviceOperation<CtapMakeCredentialRequest,
+ AuthenticatorMakeCredentialResponse>;
MakeCredentialTask(FidoDevice* device,
CtapMakeCredentialRequest request_parameter,
- AuthenticatorSelectionCriteria authenticator_criteria,
MakeCredentialTaskCallback callback);
~MakeCredentialTask() override;
@@ -48,15 +49,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) MakeCredentialTask : public FidoTask {
CtapDeviceResponseCode return_code,
base::Optional<AuthenticatorMakeCredentialResponse> response_data);
- // Invoked after retrieving response to AuthenticatorGetInfo request. Filters
- // out authenticators based on |authenticator_selection_criteria_| constraints
- // provided by the relying party. If |device_| does not satisfy the
- // constraints, then this request is silently dropped.
- bool CheckIfAuthenticatorSelectionCriteriaAreSatisfied();
-
CtapMakeCredentialRequest request_parameter_;
- AuthenticatorSelectionCriteria authenticator_selection_criteria_;
- std::unique_ptr<CtapRegisterOperation> register_operation_;
+ std::unique_ptr<RegisterOperation> register_operation_;
MakeCredentialTaskCallback callback_;
base::WeakPtrFactory<MakeCredentialTask> weak_factory_;
diff --git a/chromium/device/fido/make_credential_task_unittest.cc b/chromium/device/fido/make_credential_task_unittest.cc
index 24cc98701ae..7c49f6bdc34 100644
--- a/chromium/device/fido/make_credential_task_unittest.cc
+++ b/chromium/device/fido/make_credential_task_unittest.cc
@@ -9,15 +9,19 @@
#include "base/bind.h"
#include "base/numerics/safe_conversions.h"
#include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
+#include "device/base/features.h"
#include "device/fido/authenticator_make_credential_response.h"
#include "device/fido/ctap_make_credential_request.h"
+#include "device/fido/device_response_converter.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_parsing_utils.h"
#include "device/fido/fido_test_data.h"
#include "device/fido/make_credential_task.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"
@@ -27,53 +31,36 @@ namespace device {
namespace {
-constexpr uint8_t kClientDataHash[] = {0x01, 0x02, 0x03};
-constexpr uint8_t kUserId[] = {0x01, 0x02, 0x03};
-constexpr char kRpId[] = "acme.com";
+constexpr std::array<uint8_t, kAaguidLength> kTestDeviceAaguid = {
+ {0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, 0x4D, 0x15, 0x80, 0x06, 0x17, 0x11,
+ 0x1F, 0x9E, 0xDC, 0x7D}};
using TestMakeCredentialTaskCallback =
::device::test::StatusAndValueCallbackReceiver<
CtapDeviceResponseCode,
base::Optional<AuthenticatorMakeCredentialResponse>>;
-} // namespace
-
class FidoMakeCredentialTaskTest : public testing::Test {
public:
- FidoMakeCredentialTaskTest()
- : scoped_task_environment_(
- base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
+ FidoMakeCredentialTaskTest() { scoped_feature_list_.emplace(); }
std::unique_ptr<MakeCredentialTask> CreateMakeCredentialTask(
FidoDevice* device) {
- PublicKeyCredentialRpEntity rp(kRpId);
+ PublicKeyCredentialRpEntity rp(test_data::kRelyingPartyId);
PublicKeyCredentialUserEntity user(
- fido_parsing_utils::Materialize(kUserId));
+ fido_parsing_utils::Materialize(test_data::kUserId));
return std::make_unique<MakeCredentialTask>(
device,
CtapMakeCredentialRequest(
- fido_parsing_utils::Materialize(kClientDataHash), std::move(rp),
- std::move(user),
+ test_data::kClientDataHash, std::move(rp), std::move(user),
PublicKeyCredentialParams(
std::vector<PublicKeyCredentialParams::CredentialInfo>(1))),
- AuthenticatorSelectionCriteria(), callback_receiver_.callback());
+ callback_receiver_.callback());
}
- std::unique_ptr<MakeCredentialTask>
- CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
- FidoDevice* device,
- AuthenticatorSelectionCriteria criteria) {
- PublicKeyCredentialRpEntity rp(kRpId);
- PublicKeyCredentialUserEntity user(
- fido_parsing_utils::Materialize(kUserId));
- return std::make_unique<MakeCredentialTask>(
- device,
- CtapMakeCredentialRequest(
- fido_parsing_utils::Materialize(kClientDataHash), std::move(rp),
- std::move(user),
- PublicKeyCredentialParams(
- std::vector<PublicKeyCredentialParams::CredentialInfo>(1))),
- std::move(criteria), callback_receiver_.callback());
+ void RemoveCtapFlag() {
+ scoped_feature_list_.emplace();
+ scoped_feature_list_->InitAndDisableFeature(kNewCtap2Device);
}
TestMakeCredentialTaskCallback& make_credential_callback_receiver() {
@@ -81,16 +68,13 @@ class FidoMakeCredentialTaskTest : public testing::Test {
}
protected:
+ base::Optional<base::test::ScopedFeatureList> scoped_feature_list_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
TestMakeCredentialTaskCallback callback_receiver_;
};
-TEST_F(FidoMakeCredentialTaskTest, TestMakeCredentialSuccess) {
- auto device = std::make_unique<MockFidoDevice>();
-
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
+TEST_F(FidoMakeCredentialTaskTest, MakeCredentialSuccess) {
+ auto device = MockFidoDevice::MakeCtap();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorMakeCredential,
test_data::kTestMakeCredentialResponse);
@@ -105,26 +89,26 @@ TEST_F(FidoMakeCredentialTaskTest, TestMakeCredentialSuccess) {
EXPECT_TRUE(device->device_info());
}
-TEST_F(FidoMakeCredentialTaskTest, TestIncorrectAuthenticatorGetInfoResponse) {
- auto device = std::make_unique<MockFidoDevice>();
-
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
-
+TEST_F(FidoMakeCredentialTaskTest, TestRegisterSuccessWithFake) {
+ auto device = std::make_unique<VirtualCtap2Device>();
+ test::TestCallbackReceiver<> done_init;
+ device->DiscoverSupportedProtocolAndDeviceInfo(done_init.callback());
+ done_init.WaitForCallback();
const auto task = CreateMakeCredentialTask(device.get());
make_credential_callback_receiver().WaitForCallback();
- EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
+
+ EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
make_credential_callback_receiver().status());
- EXPECT_EQ(ProtocolVersion::kU2f, device->supported_protocol());
- EXPECT_FALSE(device->device_info());
-}
-TEST_F(FidoMakeCredentialTaskTest, TestMakeCredentialWithIncorrectRpIdHash) {
- auto device = std::make_unique<MockFidoDevice>();
+ // We don't verify the response from the fake, but do a quick sanity check.
+ ASSERT_TRUE(make_credential_callback_receiver().value());
+ EXPECT_EQ(
+ 32u,
+ make_credential_callback_receiver().value()->raw_credential_id().size());
+}
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
+TEST_F(FidoMakeCredentialTaskTest, MakeCredentialWithIncorrectRpIdHash) {
+ auto device = MockFidoDevice::MakeCtap();
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorMakeCredential,
test_data::kTestMakeCredentialResponseWithIncorrectRpIdHash);
@@ -136,127 +120,86 @@ TEST_F(FidoMakeCredentialTaskTest, TestMakeCredentialWithIncorrectRpIdHash) {
make_credential_callback_receiver().status());
}
-TEST_F(FidoMakeCredentialTaskTest,
- TestUserVerificationAuthenticatorSelectionCriteria) {
- auto device = std::make_unique<MockFidoDevice>();
+TEST_F(FidoMakeCredentialTaskTest, FallbackToU2fRegisterSuccess) {
+ auto device = MockFidoDevice::MakeU2f();
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fRegisterCommandApdu,
+ test_data::kApduEncodedNoErrorRegisterResponse);
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestGetInfoResponseWithoutUvSupport);
-
- const auto task = CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
- device.get(),
- AuthenticatorSelectionCriteria(
- AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
- false /* require_resident_key */,
- UserVerificationRequirement::kRequired));
- make_credential_callback_receiver().WaitForCallback();
-
- EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
- make_credential_callback_receiver().status());
- EXPECT_EQ(ProtocolVersion::kCtap, device->supported_protocol());
- EXPECT_TRUE(device->device_info());
- EXPECT_EQ(AuthenticatorSupportedOptions::UserVerificationAvailability::
- kSupportedButNotConfigured,
- device->device_info()->options().user_verification_availability());
-}
-
-TEST_F(FidoMakeCredentialTaskTest,
- TestPlatformDeviceAuthenticatorSelectionCriteria) {
- auto device = std::make_unique<MockFidoDevice>();
-
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestGetInfoResponseCrossPlatformDevice);
-
- const auto task = CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
- device.get(),
- AuthenticatorSelectionCriteria(
- AuthenticatorSelectionCriteria::AuthenticatorAttachment::kPlatform,
- false /* require_resident_key */,
- UserVerificationRequirement::kPreferred));
+ const auto task = CreateMakeCredentialTask(device.get());
make_credential_callback_receiver().WaitForCallback();
- EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
+ EXPECT_EQ(ProtocolVersion::kU2f, device->supported_protocol());
+ EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
make_credential_callback_receiver().status());
- EXPECT_EQ(ProtocolVersion::kCtap, device->supported_protocol());
- EXPECT_TRUE(device->device_info());
- EXPECT_FALSE(device->device_info()->options().is_platform_device());
}
-TEST_F(FidoMakeCredentialTaskTest,
- TestResidentKeyAuthenticatorSelectionCriteria) {
- auto device = std::make_unique<MockFidoDevice>();
+TEST_F(FidoMakeCredentialTaskTest, TestDefaultU2fRegisterOperationWithoutFlag) {
+ RemoveCtapFlag();
+ auto device = MockFidoDevice::MakeU2f();
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fRegisterCommandApdu,
+ test_data::kApduEncodedNoErrorRegisterResponse);
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestGetInfoResponseWithoutResidentKeySupport);
-
- const auto task = CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
- device.get(),
- AuthenticatorSelectionCriteria(
- AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
- true /* require_resident_key */,
- UserVerificationRequirement::kPreferred));
+ const auto task = CreateMakeCredentialTask(device.get());
make_credential_callback_receiver().WaitForCallback();
- EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
+ EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
make_credential_callback_receiver().status());
- EXPECT_EQ(ProtocolVersion::kCtap, device->supported_protocol());
- EXPECT_TRUE(device->device_info());
- EXPECT_FALSE(device->device_info()->options().supports_resident_key());
}
-TEST_F(FidoMakeCredentialTaskTest,
- TestSatisfyAllAuthenticatorSelectionCriteria) {
- auto device = std::make_unique<MockFidoDevice>();
-
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorMakeCredential,
- test_data::kTestMakeCredentialResponse);
+TEST_F(FidoMakeCredentialTaskTest, DefaultToU2fWhenClientPinSet) {
+ AuthenticatorGetInfoResponse device_info(
+ {ProtocolVersion::kCtap, ProtocolVersion::kU2f},
+ fido_parsing_utils::Materialize(kTestDeviceAaguid));
+ AuthenticatorSupportedOptions options;
+ options.SetClientPinAvailability(
+ AuthenticatorSupportedOptions::ClientPinAvailability::
+ kSupportedAndPinSet);
+ device_info.SetOptions(std::move(options));
+
+ auto device = MockFidoDevice::MakeCtap(std::move(device_info));
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fRegisterCommandApdu,
+ test_data::kApduEncodedNoErrorRegisterResponse);
- const auto task = CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
- device.get(),
- AuthenticatorSelectionCriteria(
- AuthenticatorSelectionCriteria::AuthenticatorAttachment::kPlatform,
- true /* require_resident_key */,
- UserVerificationRequirement::kRequired));
+ const auto task = CreateMakeCredentialTask(device.get());
make_credential_callback_receiver().WaitForCallback();
-
EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
make_credential_callback_receiver().status());
EXPECT_TRUE(make_credential_callback_receiver().value());
- EXPECT_EQ(ProtocolVersion::kCtap, device->supported_protocol());
- EXPECT_TRUE(device->device_info());
- const auto& device_options = device->device_info()->options();
- EXPECT_TRUE(device_options.is_platform_device());
- EXPECT_TRUE(device_options.supports_resident_key());
- EXPECT_EQ(AuthenticatorSupportedOptions::UserVerificationAvailability::
- kSupportedAndConfigured,
- device_options.user_verification_availability());
}
-TEST_F(FidoMakeCredentialTaskTest, TestIncompatibleUserVerificationSetting) {
- auto device = std::make_unique<MockFidoDevice>();
-
+TEST_F(FidoMakeCredentialTaskTest, EnforceClientPinWhenUserVerificationSet) {
+ AuthenticatorGetInfoResponse device_info(
+ {ProtocolVersion::kCtap, ProtocolVersion::kU2f},
+ fido_parsing_utils::Materialize(kTestDeviceAaguid));
+ AuthenticatorSupportedOptions options;
+ options.SetClientPinAvailability(
+ AuthenticatorSupportedOptions::ClientPinAvailability::
+ kSupportedAndPinSet);
+ device_info.SetOptions(std::move(options));
+
+ auto device = MockFidoDevice::MakeCtap(std::move(device_info));
device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestGetInfoResponseWithoutUvSupport);
-
- const auto task = CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
- device.get(),
- AuthenticatorSelectionCriteria(
- AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
- false /* require_resident_key */,
- UserVerificationRequirement::kRequired));
+ CtapRequestCommand::kAuthenticatorMakeCredential, base::nullopt);
+
+ PublicKeyCredentialRpEntity rp(test_data::kRelyingPartyId);
+ PublicKeyCredentialUserEntity user(
+ fido_parsing_utils::Materialize(test_data::kUserId));
+ auto request = CtapMakeCredentialRequest(
+ test_data::kClientDataHash, std::move(rp), std::move(user),
+ PublicKeyCredentialParams(
+ std::vector<PublicKeyCredentialParams::CredentialInfo>(1)));
+ request.SetUserVerificationRequired(true);
+ const auto task = std::make_unique<MakeCredentialTask>(
+ device.get(), std::move(request), callback_receiver_.callback());
+
make_credential_callback_receiver().WaitForCallback();
- EXPECT_EQ(ProtocolVersion::kCtap, device->supported_protocol());
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
make_credential_callback_receiver().status());
EXPECT_FALSE(make_credential_callback_receiver().value());
}
+} // namespace
} // namespace device
diff --git a/chromium/device/fido/mock_fido_ble_connection.h b/chromium/device/fido/mock_fido_ble_connection.h
index fdd96c356e0..a2db59fa0ed 100644
--- a/chromium/device/fido/mock_fido_ble_connection.h
+++ b/chromium/device/fido/mock_fido_ble_connection.h
@@ -45,9 +45,6 @@ class MockFidoBleConnection : public FidoBleConnection {
ReadCallback& read_callback() { return read_callback_; }
private:
- ConnectionStatusCallback connection_status_callback_;
- ReadCallback read_callback_;
-
DISALLOW_COPY_AND_ASSIGN(MockFidoBleConnection);
};
diff --git a/chromium/device/fido/mock_fido_device.cc b/chromium/device/fido/mock_fido_device.cc
index 42d61e8c835..cf499d5b6cc 100644
--- a/chromium/device/fido/mock_fido_device.cc
+++ b/chromium/device/fido/mock_fido_device.cc
@@ -10,18 +10,49 @@
#include "base/location.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/apdu/apdu_response.h"
+#include "device/fido/device_response_converter.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_parsing_utils.h"
#include "device/fido/fido_test_data.h"
namespace device {
+namespace {
+AuthenticatorGetInfoResponse DefaultAuthenticatorInfo() {
+ return *ReadCTAPGetInfoResponse(test_data::kTestAuthenticatorGetInfoResponse);
+}
+} // namespace
+
+// static
+std::unique_ptr<MockFidoDevice> MockFidoDevice::MakeU2f() {
+ return std::make_unique<MockFidoDevice>(ProtocolVersion::kU2f, base::nullopt);
+}
+
+// static
+std::unique_ptr<MockFidoDevice> MockFidoDevice::MakeCtap(
+ base::Optional<AuthenticatorGetInfoResponse> device_info) {
+ if (!device_info) {
+ device_info = DefaultAuthenticatorInfo();
+ }
+ return std::make_unique<MockFidoDevice>(ProtocolVersion::kCtap,
+ std::move(*device_info));
+}
+
// 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);
}
MockFidoDevice::MockFidoDevice() : weak_factory_(this) {}
+MockFidoDevice::MockFidoDevice(
+ ProtocolVersion protocol_version,
+ base::Optional<AuthenticatorGetInfoResponse> device_info)
+ : MockFidoDevice() {
+ set_supported_protocol(protocol_version);
+ if (device_info) {
+ SetDeviceInfo(std::move(*device_info));
+ }
+}
MockFidoDevice::~MockFidoDevice() = default;
void MockFidoDevice::TryWink(WinkCallback cb) {
@@ -33,64 +64,6 @@ void MockFidoDevice::DeviceTransact(std::vector<uint8_t> command,
DeviceTransactPtr(command, cb);
}
-// static
-void MockFidoDevice::NotSatisfied(const std::vector<uint8_t>& command,
- DeviceCallback& cb) {
- std::move(cb).Run(apdu::ApduResponse(
- std::vector<uint8_t>(),
- apdu::ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED)
- .GetEncodedResponse());
-}
-
-// static
-void MockFidoDevice::WrongData(const std::vector<uint8_t>& command,
- DeviceCallback& cb) {
- std::move(cb).Run(
- apdu::ApduResponse(std::vector<uint8_t>(),
- apdu::ApduResponse::Status::SW_WRONG_DATA)
- .GetEncodedResponse());
-}
-
-// static
-void MockFidoDevice::NoErrorSign(const std::vector<uint8_t>& command,
- DeviceCallback& cb) {
- std::move(cb).Run(
- apdu::ApduResponse(
- std::vector<uint8_t>(std::begin(test_data::kTestU2fSignResponse),
- std::end(test_data::kTestU2fSignResponse)),
- apdu::ApduResponse::Status::SW_NO_ERROR)
- .GetEncodedResponse());
-}
-
-// static
-void MockFidoDevice::NoErrorRegister(const std::vector<uint8_t>& command,
- DeviceCallback& cb) {
- std::move(cb).Run(
- apdu::ApduResponse(
- std::vector<uint8_t>(std::begin(test_data::kTestU2fRegisterResponse),
- std::end(test_data::kTestU2fRegisterResponse)),
- apdu::ApduResponse::Status::SW_NO_ERROR)
- .GetEncodedResponse());
-}
-
-// static
-void MockFidoDevice::SignWithCorruptedResponse(
- const std::vector<uint8_t>& command,
- DeviceCallback& cb) {
- std::move(cb).Run(
- apdu::ApduResponse(
- std::vector<uint8_t>(
- std::begin(test_data::kTestCorruptedU2fSignResponse),
- std::end(test_data::kTestCorruptedU2fSignResponse)),
- apdu::ApduResponse::Status::SW_NO_ERROR)
- .GetEncodedResponse());
-}
-
-// static
-void MockFidoDevice::WinkDoNothing(WinkCallback& cb) {
- std::move(cb).Run();
-}
-
void MockFidoDevice::ExpectWinkedAtLeastOnce() {
EXPECT_CALL(*this, TryWinkRef(::testing::_)).Times(::testing::AtLeast(1));
}
diff --git a/chromium/device/fido/mock_fido_device.h b/chromium/device/fido/mock_fido_device.h
index e8afa2c4e47..0df85aa9b3d 100644
--- a/chromium/device/fido/mock_fido_device.h
+++ b/chromium/device/fido/mock_fido_device.h
@@ -23,7 +23,13 @@ namespace device {
class MockFidoDevice : public FidoDevice {
public:
+ static std::unique_ptr<MockFidoDevice> MakeU2f();
+ static std::unique_ptr<MockFidoDevice> MakeCtap(
+ base::Optional<AuthenticatorGetInfoResponse> device_info = base::nullopt);
+
MockFidoDevice();
+ MockFidoDevice(ProtocolVersion protocol_version,
+ base::Optional<AuthenticatorGetInfoResponse> device_info);
~MockFidoDevice() override;
// TODO(crbug.com/729950): Remove these workarounds once support for move-only
@@ -40,23 +46,6 @@ 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;
-
- // Old interface ------------------------------------------------------------
-
- static void NotSatisfied(const std::vector<uint8_t>& command,
- DeviceCallback& cb);
- static void WrongData(const std::vector<uint8_t>& command,
- DeviceCallback& cb);
- static void NoErrorSign(const std::vector<uint8_t>& command,
- DeviceCallback& cb);
- static void NoErrorRegister(const std::vector<uint8_t>& command,
- DeviceCallback& cb);
- static void SignWithCorruptedResponse(const std::vector<uint8_t>& command,
- DeviceCallback& cb);
- static void WinkDoNothing(WinkCallback& cb);
-
- // New interface ------------------------------------------------------------
-
void ExpectWinkedAtLeastOnce();
void ExpectCtap2CommandAndRespondWith(
CtapRequestCommand command,
diff --git a/chromium/device/fido/public_key_credential_params.cc b/chromium/device/fido/public_key_credential_params.cc
index 4b7e53f2bb5..d9bc3b4a120 100644
--- a/chromium/device/fido/public_key_credential_params.cc
+++ b/chromium/device/fido/public_key_credential_params.cc
@@ -8,6 +8,39 @@
namespace device {
+// static
+base::Optional<PublicKeyCredentialParams>
+PublicKeyCredentialParams::CreateFromCBORValue(
+ const cbor::CBORValue& cbor_value) {
+ if (!cbor_value.is_array())
+ return base::nullopt;
+
+ std::vector<PublicKeyCredentialParams::CredentialInfo> credential_params;
+ for (const auto& credential : cbor_value.GetArray()) {
+ if (!credential.is_map() || credential.GetMap().size() != 2)
+ return base::nullopt;
+
+ const auto& credential_map = credential.GetMap();
+ const auto credential_type_it =
+ credential_map.find(cbor::CBORValue(kCredentialTypeMapKey));
+ const auto algorithm_type_it =
+ credential_map.find(cbor::CBORValue(kCredentialAlgorithmMapKey));
+
+ if (credential_type_it == credential_map.end() ||
+ !credential_type_it->second.is_string() ||
+ credential_type_it->second.GetString() != kPublicKey ||
+ algorithm_type_it == credential_map.end() ||
+ !algorithm_type_it->second.is_integer()) {
+ return base::nullopt;
+ }
+
+ credential_params.push_back(PublicKeyCredentialParams::CredentialInfo{
+ CredentialType::kPublicKey, algorithm_type_it->second.GetInteger()});
+ }
+
+ return PublicKeyCredentialParams(std::move(credential_params));
+}
+
PublicKeyCredentialParams::PublicKeyCredentialParams(
std::vector<CredentialInfo> credential_params)
: public_key_credential_params_(std::move(credential_params)) {}
@@ -32,10 +65,10 @@ cbor::CBORValue PublicKeyCredentialParams::ConvertToCBOR() const {
for (const auto& credential : public_key_credential_params_) {
cbor::CBORValue::MapValue cbor_credential_map;
- cbor_credential_map[cbor::CBORValue("type")] =
- cbor::CBORValue(CredentialTypeToString(credential.type));
- cbor_credential_map[cbor::CBORValue("alg")] =
- cbor::CBORValue(credential.algorithm);
+ cbor_credential_map.emplace(kCredentialTypeMapKey,
+ CredentialTypeToString(credential.type));
+ cbor_credential_map.emplace(kCredentialAlgorithmMapKey,
+ credential.algorithm);
credential_param_array.emplace_back(std::move(cbor_credential_map));
}
return cbor::CBORValue(std::move(credential_param_array));
diff --git a/chromium/device/fido/public_key_credential_params.h b/chromium/device/fido/public_key_credential_params.h
index 174080e3e61..06d2dc3d40d 100644
--- a/chromium/device/fido/public_key_credential_params.h
+++ b/chromium/device/fido/public_key_credential_params.h
@@ -12,6 +12,7 @@
#include "base/component_export.h"
#include "base/macros.h"
#include "base/numerics/safe_conversions.h"
+#include "base/optional.h"
#include "components/cbor/cbor_values.h"
#include "device/fido/fido_constants.h"
@@ -27,6 +28,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) PublicKeyCredentialParams {
int algorithm = base::strict_cast<int>(CoseAlgorithmIdentifier::kCoseEs256);
};
+ static base::Optional<PublicKeyCredentialParams> CreateFromCBORValue(
+ const cbor::CBORValue& cbor_value);
+
explicit PublicKeyCredentialParams(
std::vector<CredentialInfo> credential_params);
PublicKeyCredentialParams(const PublicKeyCredentialParams& other);
diff --git a/chromium/device/fido/public_key_credential_rp_entity.cc b/chromium/device/fido/public_key_credential_rp_entity.cc
index 287bc97cfd4..7b83513ac27 100644
--- a/chromium/device/fido/public_key_credential_rp_entity.cc
+++ b/chromium/device/fido/public_key_credential_rp_entity.cc
@@ -4,10 +4,49 @@
#include "device/fido/public_key_credential_rp_entity.h"
+#include <algorithm>
#include <utility>
+#include "device/fido/fido_constants.h"
+
namespace device {
+// static
+base::Optional<PublicKeyCredentialRpEntity>
+PublicKeyCredentialRpEntity::CreateFromCBORValue(const cbor::CBORValue& cbor) {
+ if (!cbor.is_map() || cbor.GetMap().size() > 3)
+ return base::nullopt;
+
+ const auto& rp_map = cbor.GetMap();
+ bool is_rp_map_format_correct =
+ std::all_of(rp_map.begin(), rp_map.end(), [](const auto& element) {
+ if (!element.first.is_string() || !element.second.is_string())
+ return false;
+
+ const auto& key = element.first.GetString();
+ return (key == kEntityIdMapKey || key == kEntityNameMapKey ||
+ key == kIconUrlMapKey);
+ });
+
+ if (!is_rp_map_format_correct)
+ return base::nullopt;
+
+ const auto& id_it = rp_map.find(cbor::CBORValue(kEntityIdMapKey));
+ const auto& name_it = rp_map.find(cbor::CBORValue(kEntityNameMapKey));
+ const auto& icon_it = rp_map.find(cbor::CBORValue(kIconUrlMapKey));
+ if (id_it == rp_map.end())
+ return base::nullopt;
+ PublicKeyCredentialRpEntity rp(id_it->second.GetString());
+
+ if (name_it != rp_map.end())
+ rp.SetRpName(name_it->second.GetString());
+
+ if (icon_it != rp_map.end())
+ rp.SetRpIconUrl(GURL(icon_it->second.GetString()));
+
+ return rp;
+}
+
PublicKeyCredentialRpEntity::PublicKeyCredentialRpEntity(std::string rp_id)
: rp_id_(std::move(rp_id)) {}
@@ -39,11 +78,13 @@ PublicKeyCredentialRpEntity& PublicKeyCredentialRpEntity::SetRpIconUrl(
cbor::CBORValue PublicKeyCredentialRpEntity::ConvertToCBOR() const {
cbor::CBORValue::MapValue rp_map;
- rp_map[cbor::CBORValue("id")] = cbor::CBORValue(rp_id_);
+ rp_map.emplace(kEntityIdMapKey, rp_id_);
if (rp_name_)
- rp_map[cbor::CBORValue("name")] = cbor::CBORValue(*rp_name_);
+ rp_map.emplace(kEntityNameMapKey, *rp_name_);
+
if (rp_icon_url_)
- rp_map[cbor::CBORValue("icon")] = cbor::CBORValue(rp_icon_url_->spec());
+ rp_map.emplace(kIconUrlMapKey, rp_icon_url_->spec());
+
return cbor::CBORValue(std::move(rp_map));
}
diff --git a/chromium/device/fido/public_key_credential_rp_entity.h b/chromium/device/fido/public_key_credential_rp_entity.h
index 701f73fbf76..d8c16e3a0ed 100644
--- a/chromium/device/fido/public_key_credential_rp_entity.h
+++ b/chromium/device/fido/public_key_credential_rp_entity.h
@@ -21,6 +21,9 @@ namespace device {
// and optional relying party display image url.
class COMPONENT_EXPORT(DEVICE_FIDO) PublicKeyCredentialRpEntity {
public:
+ static base::Optional<PublicKeyCredentialRpEntity> CreateFromCBORValue(
+ const cbor::CBORValue& cbor);
+
explicit PublicKeyCredentialRpEntity(std::string rp_id);
PublicKeyCredentialRpEntity(const PublicKeyCredentialRpEntity& other);
PublicKeyCredentialRpEntity(PublicKeyCredentialRpEntity&& other);
diff --git a/chromium/device/fido/public_key_credential_user_entity.cc b/chromium/device/fido/public_key_credential_user_entity.cc
index fceef8abe75..114f1a24a94 100644
--- a/chromium/device/fido/public_key_credential_user_entity.cc
+++ b/chromium/device/fido/public_key_credential_user_entity.cc
@@ -6,17 +6,9 @@
#include <utility>
-namespace device {
-
-namespace {
+#include "device/fido/fido_constants.h"
-// Keys for storing user entity information in CBOR map.
-constexpr char kUserIdKey[] = "id";
-constexpr char kUserNameKey[] = "name";
-constexpr char kUserDisplayNameKey[] = "displayName";
-constexpr char kUserIconUrlKey[] = "icon";
-
-} // namespace
+namespace device {
// static
base::Optional<PublicKeyCredentialUserEntity>
@@ -27,24 +19,24 @@ PublicKeyCredentialUserEntity::CreateFromCBORValue(
const cbor::CBORValue::MapValue& cbor_map = cbor.GetMap();
- auto user_id = cbor_map.find(cbor::CBORValue(kUserIdKey));
+ auto user_id = cbor_map.find(cbor::CBORValue(kEntityIdMapKey));
if (user_id == cbor_map.end() || !user_id->second.is_bytestring())
return base::nullopt;
PublicKeyCredentialUserEntity user(user_id->second.GetBytestring());
- auto user_name = cbor_map.find(cbor::CBORValue(kUserNameKey));
+ auto user_name = cbor_map.find(cbor::CBORValue(kEntityNameMapKey));
if (user_name != cbor_map.end() && user_name->second.is_string()) {
user.SetUserName(user_name->second.GetString());
}
- auto user_display_name = cbor_map.find(cbor::CBORValue(kUserDisplayNameKey));
+ auto user_display_name = cbor_map.find(cbor::CBORValue(kDisplayNameMapKey));
if (user_display_name != cbor_map.end() &&
user_display_name->second.is_string()) {
user.SetDisplayName(user_display_name->second.GetString());
}
- auto user_icon_url = cbor_map.find(cbor::CBORValue(kUserIconUrlKey));
+ auto user_icon_url = cbor_map.find(cbor::CBORValue(kIconUrlMapKey));
if (user_icon_url != cbor_map.end() && user_icon_url->second.is_string()) {
user.SetIconUrl(GURL(user_icon_url->second.GetString()));
}
@@ -72,15 +64,13 @@ PublicKeyCredentialUserEntity::~PublicKeyCredentialUserEntity() = default;
cbor::CBORValue PublicKeyCredentialUserEntity::ConvertToCBOR() const {
cbor::CBORValue::MapValue user_map;
- user_map[cbor::CBORValue(kUserIdKey)] = cbor::CBORValue(user_id_);
+ user_map.emplace(kEntityIdMapKey, user_id_);
if (user_name_)
- user_map[cbor::CBORValue(kUserNameKey)] = cbor::CBORValue(*user_name_);
+ user_map.emplace(kEntityNameMapKey, *user_name_);
if (user_icon_url_)
- user_map[cbor::CBORValue(kUserIconUrlKey)] =
- cbor::CBORValue(user_icon_url_->spec());
+ user_map.emplace(kIconUrlMapKey, user_icon_url_->spec());
if (user_display_name_) {
- user_map[cbor::CBORValue(kUserDisplayNameKey)] =
- cbor::CBORValue(*user_display_name_);
+ user_map.emplace(kDisplayNameMapKey, *user_display_name_);
}
return cbor::CBORValue(std::move(user_map));
}
diff --git a/chromium/device/fido/response_data.cc b/chromium/device/fido/response_data.cc
index 09f3e4e4632..994ed978ca3 100644
--- a/chromium/device/fido/response_data.cc
+++ b/chromium/device/fido/response_data.cc
@@ -33,7 +33,8 @@ std::string ResponseData::GetId() const {
}
bool ResponseData::CheckRpIdHash(const std::string& rp_id) const {
- return GetRpIdHash() == fido_parsing_utils::CreateSHA256Hash(rp_id);
+ return base::make_span(GetRpIdHash()) ==
+ base::make_span(fido_parsing_utils::CreateSHA256Hash(rp_id));
}
} // namespace device
diff --git a/chromium/device/fido/response_data.h b/chromium/device/fido/response_data.h
index 03076a12dc1..ad5082ce941 100644
--- a/chromium/device/fido/response_data.h
+++ b/chromium/device/fido/response_data.h
@@ -6,12 +6,15 @@
#define DEVICE_FIDO_RESPONSE_DATA_H_
#include <stdint.h>
+
+#include <array>
#include <memory>
#include <string>
#include <vector>
#include "base/component_export.h"
#include "base/macros.h"
+#include "device/fido/fido_constants.h"
namespace device {
@@ -21,7 +24,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) ResponseData {
public:
virtual ~ResponseData();
- virtual const std::vector<uint8_t>& GetRpIdHash() const = 0;
+ virtual const std::array<uint8_t, kRpIdHashLength>& GetRpIdHash() const = 0;
std::string GetId() const;
diff --git a/chromium/device/fido/scoped_virtual_fido_device.cc b/chromium/device/fido/scoped_virtual_fido_device.cc
index d5afaf87a4a..4449bc15249 100644
--- a/chromium/device/fido/scoped_virtual_fido_device.cc
+++ b/chromium/device/fido/scoped_virtual_fido_device.cc
@@ -10,33 +10,45 @@
#include "base/bind_helpers.h"
#include "base/location.h"
#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
#include "base/threading/sequenced_task_runner_handle.h"
+#include "device/fido/virtual_ctap2_device.h"
#include "device/fido/virtual_u2f_device.h"
namespace device {
namespace test {
// A FidoDiscovery that always vends a single |VirtualFidoDevice|.
-class VirtualFidoDeviceDiscovery : public FidoDiscovery {
+class VirtualFidoDeviceDiscovery
+ : public FidoDiscovery,
+ public base::SupportsWeakPtr<VirtualFidoDeviceDiscovery> {
public:
explicit VirtualFidoDeviceDiscovery(
- scoped_refptr<VirtualFidoDevice::State> state)
+ scoped_refptr<VirtualFidoDevice::State> state,
+ ProtocolVersion supported_protocol)
: FidoDiscovery(FidoTransportProtocol::kUsbHumanInterfaceDevice),
- state_(std::move(state)) {}
+ state_(std::move(state)),
+ supported_protocol_(supported_protocol) {}
~VirtualFidoDeviceDiscovery() override = default;
protected:
void StartInternal() override {
- auto device = std::make_unique<VirtualU2fDevice>(state_);
+ std::unique_ptr<FidoDevice> device;
+ if (supported_protocol_ == ProtocolVersion::kCtap)
+ device = std::make_unique<VirtualCtap2Device>(state_);
+ else
+ device = std::make_unique<VirtualU2fDevice>(state_);
+
AddDevice(std::move(device));
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&VirtualFidoDeviceDiscovery::NotifyDiscoveryStarted,
- base::Unretained(this), true /* success */));
+ AsWeakPtr(), true /* success */));
}
private:
scoped_refptr<VirtualFidoDevice::State> state_;
+ ProtocolVersion supported_protocol_;
DISALLOW_COPY_AND_ASSIGN(VirtualFidoDeviceDiscovery);
};
@@ -44,6 +56,11 @@ ScopedVirtualFidoDevice::ScopedVirtualFidoDevice()
: state_(new VirtualFidoDevice::State) {}
ScopedVirtualFidoDevice::~ScopedVirtualFidoDevice() = default;
+void ScopedVirtualFidoDevice::SetSupportedProtocol(
+ ProtocolVersion supported_protocol) {
+ supported_protocol_ = ProtocolVersion::kCtap;
+}
+
VirtualFidoDevice::State* ScopedVirtualFidoDevice::mutable_state() {
return state_.get();
}
@@ -54,7 +71,8 @@ std::unique_ptr<FidoDiscovery> ScopedVirtualFidoDevice::CreateFidoDiscovery(
if (transport != FidoTransportProtocol::kUsbHumanInterfaceDevice) {
return nullptr;
}
- return std::make_unique<VirtualFidoDeviceDiscovery>(state_);
+ return std::make_unique<VirtualFidoDeviceDiscovery>(state_,
+ supported_protocol_);
}
} // namespace test
diff --git a/chromium/device/fido/scoped_virtual_fido_device.h b/chromium/device/fido/scoped_virtual_fido_device.h
index a9c8d8583e2..4c63e5229f1 100644
--- a/chromium/device/fido/scoped_virtual_fido_device.h
+++ b/chromium/device/fido/scoped_virtual_fido_device.h
@@ -8,6 +8,7 @@
#include <memory>
#include "base/macros.h"
+#include "device/fido/fido_constants.h"
#include "device/fido/fido_discovery.h"
#include "device/fido/virtual_fido_device.h"
@@ -23,6 +24,7 @@ class ScopedVirtualFidoDevice
ScopedVirtualFidoDevice();
~ScopedVirtualFidoDevice() override;
+ void SetSupportedProtocol(ProtocolVersion supported_protocol);
VirtualFidoDevice::State* mutable_state();
protected:
@@ -31,6 +33,7 @@ class ScopedVirtualFidoDevice
::service_manager::Connector* connector) override;
private:
+ ProtocolVersion supported_protocol_ = ProtocolVersion::kU2f;
scoped_refptr<VirtualFidoDevice::State> state_;
DISALLOW_COPY_AND_ASSIGN(ScopedVirtualFidoDevice);
};
diff --git a/chromium/device/fido/u2f_command_constructor.cc b/chromium/device/fido/u2f_command_constructor.cc
index 2b4fb1172ec..c195f22735d 100644
--- a/chromium/device/fido/u2f_command_constructor.cc
+++ b/chromium/device/fido/u2f_command_constructor.cc
@@ -8,6 +8,7 @@
#include <utility>
#include "components/apdu/apdu_command.h"
+#include "device/fido/fido_constants.h"
#include "device/fido/fido_parsing_utils.h"
namespace device {
@@ -41,7 +42,7 @@ base::Optional<std::vector<uint8_t>> ConvertToU2fRegisterCommand(
return ConstructU2fRegisterCommand(
fido_parsing_utils::CreateSHA256Hash(request.rp().rp_id()),
- request.client_data_hash());
+ request.client_data_hash(), request.is_individual_attestation());
}
base::Optional<std::vector<uint8_t>> ConvertToU2fCheckOnlySignCommand(
@@ -63,27 +64,23 @@ base::Optional<std::vector<uint8_t>> ConvertToU2fSignCommand(
if (!IsConvertibleToU2fSignCommand(request))
return base::nullopt;
- auto application_parameter =
+ const auto& application_parameter =
application_parameter_type == ApplicationParameterType::kPrimary
? fido_parsing_utils::CreateSHA256Hash(request.rp_id())
- : std::vector<uint8_t>();
+ : request.alternative_application_parameter().value_or(
+ std::array<uint8_t, kRpIdHashLength>());
- return ConstructU2fSignCommand(std::move(application_parameter),
+ return ConstructU2fSignCommand(application_parameter,
request.client_data_hash(), key_handle,
check_only);
}
-base::Optional<std::vector<uint8_t>> ConstructU2fRegisterCommand(
- base::span<const uint8_t> application_parameter,
- base::span<const uint8_t> challenge_parameter,
+std::vector<uint8_t> ConstructU2fRegisterCommand(
+ base::span<const uint8_t, kU2fApplicationParamLength> application_parameter,
+ base::span<const uint8_t, kU2fChallengeParamLength> challenge_parameter,
bool is_individual_attestation) {
- if (application_parameter.size() != kU2fParameterLength ||
- challenge_parameter.size() != kU2fParameterLength) {
- return base::nullopt;
- }
-
std::vector<uint8_t> data;
- data.reserve(challenge_parameter.size() + application_parameter.size());
+ data.reserve(kU2fChallengeParamLength + kU2fApplicationParamLength);
fido_parsing_utils::Append(&data, challenge_parameter);
fido_parsing_utils::Append(&data, application_parameter);
@@ -97,18 +94,16 @@ base::Optional<std::vector<uint8_t>> ConstructU2fRegisterCommand(
}
base::Optional<std::vector<uint8_t>> ConstructU2fSignCommand(
- base::span<const uint8_t> application_parameter,
- base::span<const uint8_t> challenge_parameter,
+ base::span<const uint8_t, kU2fApplicationParamLength> application_parameter,
+ base::span<const uint8_t, kU2fChallengeParamLength> challenge_parameter,
base::span<const uint8_t> key_handle,
bool check_only) {
- if (application_parameter.size() != kU2fParameterLength ||
- challenge_parameter.size() != kU2fParameterLength ||
- key_handle.size() > kMaxKeyHandleLength) {
+ if (key_handle.size() > kMaxKeyHandleLength) {
return base::nullopt;
}
std::vector<uint8_t> data;
- data.reserve(challenge_parameter.size() + application_parameter.size() + 1 +
+ data.reserve(kU2fChallengeParamLength + kU2fApplicationParamLength + 1 +
key_handle.size());
fido_parsing_utils::Append(&data, challenge_parameter);
fido_parsing_utils::Append(&data, application_parameter);
@@ -123,7 +118,7 @@ base::Optional<std::vector<uint8_t>> ConstructU2fSignCommand(
return command.GetEncodedCommand();
}
-base::Optional<std::vector<uint8_t>> ConstructBogusU2fRegistrationCommand() {
+std::vector<uint8_t> ConstructBogusU2fRegistrationCommand() {
return ConstructU2fRegisterCommand(kBogusAppParam, kBogusChallenge);
}
diff --git a/chromium/device/fido/u2f_command_constructor.h b/chromium/device/fido/u2f_command_constructor.h
index 69161d110cc..6c977bca5ee 100644
--- a/chromium/device/fido/u2f_command_constructor.h
+++ b/chromium/device/fido/u2f_command_constructor.h
@@ -14,7 +14,6 @@
#include "base/optional.h"
#include "device/fido/ctap_get_assertion_request.h"
#include "device/fido/ctap_make_credential_request.h"
-#include "device/fido/fido_constants.h"
namespace device {
@@ -57,22 +56,22 @@ base::Optional<std::vector<uint8_t>> ConvertToU2fSignCommand(
// TODO(hongjunchoi): Move this logic inside ConvertToU2fRegisterCommand()
// once U2fRegister is removed.
COMPONENT_EXPORT(DEVICE_FIDO)
-base::Optional<std::vector<uint8_t>> ConstructU2fRegisterCommand(
- base::span<const uint8_t> application_parameter,
- base::span<const uint8_t> challenge_parameter,
+std::vector<uint8_t> ConstructU2fRegisterCommand(
+ base::span<const uint8_t, kU2fApplicationParamLength> application_parameter,
+ base::span<const uint8_t, kU2fChallengeParamLength> challenge_parameter,
bool is_individual_attestation = false);
// TODO(hongjunchoi): Move this logic inside ConvertToU2fSignCommand() once
// U2fSign is deleted.
COMPONENT_EXPORT(DEVICE_FIDO)
base::Optional<std::vector<uint8_t>> ConstructU2fSignCommand(
- base::span<const uint8_t> application_parameter,
- base::span<const uint8_t> challenge_parameter,
+ base::span<const uint8_t, kU2fApplicationParamLength> application_parameter,
+ base::span<const uint8_t, kU2fChallengeParamLength> challenge_parameter,
base::span<const uint8_t> key_handle,
bool check_only = false);
COMPONENT_EXPORT(DEVICE_FIDO)
-base::Optional<std::vector<uint8_t>> ConstructBogusU2fRegistrationCommand();
+std::vector<uint8_t> ConstructBogusU2fRegistrationCommand();
} // namespace device
diff --git a/chromium/device/fido/u2f_command_constructor_unittest.cc b/chromium/device/fido/u2f_command_constructor_unittest.cc
index 2824ba44fc8..c44a777c29c 100644
--- a/chromium/device/fido/u2f_command_constructor_unittest.cc
+++ b/chromium/device/fido/u2f_command_constructor_unittest.cc
@@ -29,80 +29,36 @@ CtapMakeCredentialRequest ConstructMakeCredentialRequest() {
.SetIconUrl(GURL("https://pics.acme.com/00/p/aBjjjpqPb.png"));
return CtapMakeCredentialRequest(
- fido_parsing_utils::Materialize(test_data::kClientDataHash),
- std::move(rp), std::move(user),
+ test_data::kClientDataHash, std::move(rp), std::move(user),
PublicKeyCredentialParams(PublicKeyCredentialParams(
std::vector<PublicKeyCredentialParams::CredentialInfo>(1))));
}
CtapGetAssertionRequest ConstructGetAssertionRequest() {
- return CtapGetAssertionRequest(
- "acme.com", fido_parsing_utils::Materialize(test_data::kClientDataHash));
+ return CtapGetAssertionRequest("acme.com", test_data::kClientDataHash);
}
} // namespace
TEST(U2fCommandConstructorTest, TestCreateU2fRegisterCommand) {
- const auto& register_command_without_individual_attestation =
+ const auto register_command_without_individual_attestation =
ConstructU2fRegisterCommand(test_data::kApplicationParameter,
test_data::kChallengeParameter,
false /* is_individual_attestation */);
- ASSERT_TRUE(register_command_without_individual_attestation);
- EXPECT_THAT(*register_command_without_individual_attestation,
+ EXPECT_THAT(register_command_without_individual_attestation,
::testing::ElementsAreArray(test_data::kU2fRegisterCommandApdu));
- const auto& register_command_with_individual_attestation =
+ const auto register_command_with_individual_attestation =
ConstructU2fRegisterCommand(test_data::kApplicationParameter,
test_data::kChallengeParameter,
true /* is_individual_attestation */);
- ASSERT_TRUE(register_command_with_individual_attestation);
- EXPECT_THAT(*register_command_with_individual_attestation,
+ EXPECT_THAT(register_command_with_individual_attestation,
::testing::ElementsAreArray(
test_data::kU2fRegisterCommandApduWithIndividualAttestation));
}
-TEST(U2fCommandConstructorTest, TestCreateRegisterWithIncorrectParameters) {
- std::vector<uint8_t> application_parameter(kU2fParameterLength, 0x01);
- std::vector<uint8_t> challenge_parameter(kU2fParameterLength, 0xff);
-
- const auto& register_command_without_individual_attestation =
- ConstructU2fRegisterCommand(application_parameter, challenge_parameter,
- false /* is_individual_attestation */);
-
- ASSERT_TRUE(register_command_without_individual_attestation);
- ASSERT_LE(3u, register_command_without_individual_attestation->size());
- // Individual attestation bit should be cleared.
- EXPECT_EQ(0, (*register_command_without_individual_attestation)[2] & 0x80);
-
- const auto register_request_with_individual_attestation =
- ConstructU2fRegisterCommand(application_parameter, challenge_parameter,
- true /* is_individual_attestation */);
-
- ASSERT_TRUE(register_request_with_individual_attestation);
- ASSERT_LE(3u, register_request_with_individual_attestation->size());
- // Individual attestation bit should be set.
- EXPECT_EQ(0x80, (*register_request_with_individual_attestation)[2] & 0x80);
-
- // Expect null result with incorrectly sized application_parameter.
- application_parameter.push_back(0xff);
- auto incorrect_register_cmd =
- ConstructU2fRegisterCommand(application_parameter, challenge_parameter,
- false /* is_individual_attestation */);
-
- EXPECT_FALSE(incorrect_register_cmd);
- application_parameter.pop_back();
-
- // Expect null result with incorrectly sized challenge.
- challenge_parameter.push_back(0xff);
- incorrect_register_cmd =
- ConstructU2fRegisterCommand(application_parameter, challenge_parameter,
- false /* is_individual_attestation */);
-
- EXPECT_FALSE(incorrect_register_cmd);
-}
-
TEST(U2fCommandConstructorTest, TestConvertCtapMakeCredentialToU2fRegister) {
const auto make_credential_param = ConstructMakeCredentialRequest();
@@ -164,8 +120,7 @@ TEST(U2fCommandConstructorTest, TestU2fRegisterCredentialAlgorithmRequirement) {
.SetIconUrl(GURL("https://pics.acme.com/00/p/aBjjjpqPb.png"));
CtapMakeCredentialRequest make_credential_param(
- fido_parsing_utils::Materialize(test_data::kClientDataHash),
- std::move(rp), std::move(user),
+ test_data::kClientDataHash, std::move(rp), std::move(user),
PublicKeyCredentialParams({{CredentialType::kPublicKey, -257}}));
EXPECT_FALSE(IsConvertibleToU2fRegisterCommand(make_credential_param));
@@ -237,4 +192,20 @@ TEST(U2fCommandConstructorTest, TestU2fSignUserVerificationRequirement) {
EXPECT_FALSE(IsConvertibleToU2fSignCommand(get_assertion_req));
}
+TEST(U2fCommandConstructorTest, TestCreateSignWithIncorrectKeyHandle) {
+ std::array<uint8_t, kU2fApplicationParamLength> application_parameter = {
+ 0x01};
+ std::array<uint8_t, kU2fChallengeParamLength> challenge_parameter = {0x02};
+ std::vector<uint8_t> key_handle(kMaxKeyHandleLength, 0xff);
+
+ const auto valid_sign_command = ConstructU2fSignCommand(
+ application_parameter, challenge_parameter, key_handle);
+ ASSERT_TRUE(valid_sign_command);
+
+ key_handle.push_back(0xff);
+ const auto invalid_sign_command = ConstructU2fSignCommand(
+ application_parameter, challenge_parameter, key_handle);
+ ASSERT_FALSE(invalid_sign_command);
+}
+
} // namespace device
diff --git a/chromium/device/fido/u2f_register.cc b/chromium/device/fido/u2f_register.cc
deleted file mode 100644
index a05818eaffc..00000000000
--- a/chromium/device/fido/u2f_register.cc
+++ /dev/null
@@ -1,174 +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/u2f_register.h"
-
-#include <utility>
-
-#include "base/stl_util.h"
-#include "components/apdu/apdu_command.h"
-#include "components/apdu/apdu_response.h"
-#include "device/fido/authenticator_make_credential_response.h"
-#include "device/fido/u2f_command_constructor.h"
-#include "services/service_manager/public/cpp/connector.h"
-
-namespace device {
-
-// static
-std::unique_ptr<U2fRequest> U2fRegister::TryRegistration(
- service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& transports,
- std::vector<std::vector<uint8_t>> registered_keys,
- std::vector<uint8_t> challenge_digest,
- std::vector<uint8_t> application_parameter,
- bool individual_attestation_ok,
- RegisterResponseCallback completion_callback) {
- std::unique_ptr<U2fRequest> request = std::make_unique<U2fRegister>(
- connector, transports, std::move(registered_keys),
- std::move(challenge_digest), std::move(application_parameter),
- individual_attestation_ok, std::move(completion_callback));
- request->Start();
- return request;
-}
-
-U2fRegister::U2fRegister(
- service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& transports,
- std::vector<std::vector<uint8_t>> registered_keys,
- std::vector<uint8_t> challenge_digest,
- std::vector<uint8_t> application_parameter,
- bool individual_attestation_ok,
- RegisterResponseCallback completion_callback)
- : U2fRequest(connector,
- transports,
- std::move(application_parameter),
- std::move(challenge_digest),
- std::move(registered_keys)),
- individual_attestation_ok_(individual_attestation_ok),
- completion_callback_(std::move(completion_callback)),
- weak_factory_(this) {}
-
-U2fRegister::~U2fRegister() = default;
-
-void U2fRegister::TryDevice() {
- DCHECK(current_device_);
- if (!registered_keys_.empty() && !CheckedForDuplicateRegistration()) {
- auto it = registered_keys_.cbegin();
- InitiateDeviceTransaction(
- GetU2fSignApduCommand(application_parameter_, *it,
- true /* check_only */),
- base::BindOnce(&U2fRegister::OnTryCheckRegistration,
- weak_factory_.GetWeakPtr(), it));
- } else {
- InitiateDeviceTransaction(
- GetU2fRegisterApduCommand(individual_attestation_ok_),
- base::BindOnce(&U2fRegister::OnTryDevice, weak_factory_.GetWeakPtr(),
- false /* is_duplicate_registration */));
- }
-}
-
-void U2fRegister::OnTryCheckRegistration(
- std::vector<std::vector<uint8_t>>::const_iterator it,
- base::Optional<std::vector<uint8_t>> response) {
- const auto apdu_response =
- response ? apdu::ApduResponse::CreateFromMessage(std::move(*response))
- : base::nullopt;
- auto return_code = apdu_response ? apdu_response->status()
- : apdu::ApduResponse::Status::SW_WRONG_DATA;
-
- switch (return_code) {
- case apdu::ApduResponse::Status::SW_NO_ERROR:
- case apdu::ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED: {
- // Duplicate registration found. Call bogus registration to check for
- // user presence (touch) and terminate the registration process.
- InitiateDeviceTransaction(
- ConstructBogusU2fRegistrationCommand(),
- base::BindOnce(&U2fRegister::OnTryDevice, weak_factory_.GetWeakPtr(),
- true /* is_duplicate_registration */));
- break;
- }
-
- case apdu::ApduResponse::Status::SW_WRONG_DATA:
- // Continue to iterate through the provided key handles in the exclude
- // list and check for already registered keys.
- if (++it != registered_keys_.end()) {
- InitiateDeviceTransaction(
- GetU2fSignApduCommand(application_parameter_, *it,
- true /* check_only */),
- base::BindOnce(&U2fRegister::OnTryCheckRegistration,
- weak_factory_.GetWeakPtr(), it));
- } else {
- checked_device_id_list_.insert(current_device_->GetId());
- if (devices_.empty()) {
- // When all devices have been checked, proceed to registration.
- CompleteNewDeviceRegistration();
- } else {
- state_ = State::IDLE;
- Transition();
- }
- }
- break;
- default:
- // Some sort of failure occurred. Abandon this device and move on.
- AbandonCurrentDeviceAndTransition();
- break;
- }
-}
-
-void U2fRegister::CompleteNewDeviceRegistration() {
- if (current_device_)
- attempted_devices_.push_back(std::move(current_device_));
-
- devices_.splice(devices_.end(), std::move(attempted_devices_));
- state_ = State::IDLE;
- Transition();
- return;
-}
-
-bool U2fRegister::CheckedForDuplicateRegistration() {
- return base::ContainsKey(checked_device_id_list_, current_device_->GetId());
-}
-
-void U2fRegister::OnTryDevice(bool is_duplicate_registration,
- base::Optional<std::vector<uint8_t>> response) {
- const auto apdu_response =
- response ? apdu::ApduResponse::CreateFromMessage(std::move(*response))
- : base::nullopt;
- auto return_code = apdu_response ? apdu_response->status()
- : apdu::ApduResponse::Status::SW_WRONG_DATA;
- switch (return_code) {
- case apdu::ApduResponse::Status::SW_NO_ERROR: {
- state_ = State::COMPLETE;
- if (is_duplicate_registration) {
- std::move(completion_callback_)
- .Run(FidoReturnCode::kUserConsentButCredentialExcluded,
- base::nullopt);
- break;
- }
- auto response =
- AuthenticatorMakeCredentialResponse::CreateFromU2fRegisterResponse(
- application_parameter_, apdu_response->data());
- if (!response) {
- // The response data was corrupted / didn't parse properly.
- std::move(completion_callback_)
- .Run(FidoReturnCode::kAuthenticatorResponseInvalid, base::nullopt);
- break;
- }
- std::move(completion_callback_)
- .Run(FidoReturnCode::kSuccess, std::move(response));
- break;
- }
- case apdu::ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED:
- // Waiting for user touch, move on and try this device later.
- state_ = State::IDLE;
- Transition();
- break;
- default:
- // An error has occurred, quit trying this device.
- AbandonCurrentDeviceAndTransition();
- break;
- }
-}
-
-} // namespace device
diff --git a/chromium/device/fido/u2f_register.h b/chromium/device/fido/u2f_register.h
deleted file mode 100644
index 139d806adc1..00000000000
--- a/chromium/device/fido/u2f_register.h
+++ /dev/null
@@ -1,85 +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_FIDO_U2F_REGISTER_H_
-#define DEVICE_FIDO_U2F_REGISTER_H_
-
-#include <memory>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/component_export.h"
-#include "base/containers/flat_set.h"
-#include "base/macros.h"
-#include "base/optional.h"
-#include "device/fido/authenticator_make_credential_response.h"
-#include "device/fido/fido_constants.h"
-#include "device/fido/fido_transport_protocol.h"
-#include "device/fido/u2f_request.h"
-
-namespace service_manager {
-class Connector;
-};
-
-namespace device {
-
-class COMPONENT_EXPORT(DEVICE_FIDO) U2fRegister : public U2fRequest {
- public:
- using RegisterResponseCallback = base::OnceCallback<void(
- FidoReturnCode status_code,
- base::Optional<AuthenticatorMakeCredentialResponse> response_data)>;
-
- static std::unique_ptr<U2fRequest> TryRegistration(
- service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& transports,
- std::vector<std::vector<uint8_t>> registered_keys,
- std::vector<uint8_t> challenge_digest,
- std::vector<uint8_t> application_parameter,
- bool individual_attestation_ok,
- RegisterResponseCallback completion_callback);
-
- U2fRegister(service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& transports,
- std::vector<std::vector<uint8_t>> registered_keys,
- std::vector<uint8_t> challenge_digest,
- std::vector<uint8_t> application_parameter,
- bool individual_attestation_ok,
- RegisterResponseCallback completion_callback);
- ~U2fRegister() override;
-
- private:
- void TryDevice() override;
- void OnTryDevice(bool is_duplicate_registration,
- base::Optional<std::vector<uint8_t>> response);
-
- // Callback function called when non-empty exclude list was provided. This
- // function iterates through all key handles in |registered_keys_| for all
- // devices and checks for already registered keys.
- void OnTryCheckRegistration(
- std::vector<std::vector<uint8_t>>::const_iterator it,
- base::Optional<std::vector<uint8_t>> response);
- // Function handling registration flow after all devices were checked for
- // already registered keys.
- void CompleteNewDeviceRegistration();
- // Returns whether |current_device_| has been checked for duplicate
- // registration for all key handles provided in |registered_keys_|.
- bool CheckedForDuplicateRegistration();
-
- // Indicates whether the token should be signaled that using an individual
- // attestation certificate is acceptable.
- const bool individual_attestation_ok_;
- RegisterResponseCallback completion_callback_;
-
- // List of authenticators that did not create any of the key handles in the
- // exclude list.
- std::set<std::string> checked_device_id_list_;
- base::WeakPtrFactory<U2fRegister> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(U2fRegister);
-};
-
-} // namespace device
-
-#endif // DEVICE_FIDO_U2F_REGISTER_H_
diff --git a/chromium/device/fido/u2f_register_operation.cc b/chromium/device/fido/u2f_register_operation.cc
new file mode 100644
index 00000000000..5451cca30e9
--- /dev/null
+++ b/chromium/device/fido/u2f_register_operation.cc
@@ -0,0 +1,150 @@
+// 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/u2f_register_operation.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "components/apdu/apdu_response.h"
+#include "device/fido/authenticator_make_credential_response.h"
+#include "device/fido/ctap_make_credential_request.h"
+#include "device/fido/device_response_converter.h"
+#include "device/fido/fido_constants.h"
+#include "device/fido/fido_device.h"
+#include "device/fido/fido_parsing_utils.h"
+#include "device/fido/u2f_command_constructor.h"
+
+namespace device {
+
+U2fRegisterOperation::U2fRegisterOperation(
+ FidoDevice* device,
+ const CtapMakeCredentialRequest& request,
+ DeviceResponseCallback callback)
+ : DeviceOperation(device, request, std::move(callback)),
+ weak_factory_(this) {}
+
+U2fRegisterOperation::~U2fRegisterOperation() = default;
+
+void U2fRegisterOperation::Start() {
+ DCHECK(IsConvertibleToU2fRegisterCommand(request()));
+
+ const auto& exclude_list = request().exclude_list();
+ if (!exclude_list || exclude_list->empty()) {
+ TryRegistration(false /* is_duplicate_registration */);
+ } else {
+ auto it = request().exclude_list()->cbegin();
+ DispatchDeviceRequest(
+ ConvertToU2fCheckOnlySignCommand(request(), *it),
+ base::BindOnce(&U2fRegisterOperation::OnCheckForExcludedKeyHandle,
+ weak_factory_.GetWeakPtr(), it));
+ }
+}
+
+void U2fRegisterOperation::TryRegistration(bool is_duplicate_registration) {
+ auto command = is_duplicate_registration
+ ? ConstructBogusU2fRegistrationCommand()
+ : ConvertToU2fRegisterCommand(request());
+
+ DispatchDeviceRequest(
+ std::move(command),
+ base::BindOnce(&U2fRegisterOperation::OnRegisterResponseReceived,
+ weak_factory_.GetWeakPtr(), is_duplicate_registration));
+}
+
+void U2fRegisterOperation::OnRegisterResponseReceived(
+ bool is_duplicate_registration,
+ base::Optional<std::vector<uint8_t>> device_response) {
+ const auto& apdu_response =
+ device_response
+ ? apdu::ApduResponse::CreateFromMessage(std::move(*device_response))
+ : base::nullopt;
+ auto return_code = apdu_response ? apdu_response->status()
+ : apdu::ApduResponse::Status::SW_WRONG_DATA;
+
+ switch (return_code) {
+ case apdu::ApduResponse::Status::SW_NO_ERROR: {
+ if (is_duplicate_registration) {
+ std::move(callback())
+ .Run(CtapDeviceResponseCode::kCtap2ErrCredentialExcluded,
+ base::nullopt);
+ break;
+ }
+
+ auto response =
+ AuthenticatorMakeCredentialResponse::CreateFromU2fRegisterResponse(
+ fido_parsing_utils::CreateSHA256Hash(request().rp().rp_id()),
+ apdu_response->data());
+ std::move(callback())
+ .Run(CtapDeviceResponseCode::kSuccess, std::move(response));
+ break;
+ }
+ case apdu::ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED:
+ // Waiting for user touch, retry after delay.
+ base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&U2fRegisterOperation::TryRegistration,
+ weak_factory_.GetWeakPtr(), is_duplicate_registration),
+ kU2fRetryDelay);
+
+ break;
+ default:
+ // An error has occurred, quit trying this device.
+ std::move(callback())
+ .Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
+ break;
+ }
+}
+
+void U2fRegisterOperation::OnCheckForExcludedKeyHandle(
+ ExcludeListIterator it,
+ base::Optional<std::vector<uint8_t>> device_response) {
+ const auto& apdu_response =
+ device_response
+ ? apdu::ApduResponse::CreateFromMessage(std::move(*device_response))
+ : base::nullopt;
+ auto return_code = apdu_response ? apdu_response->status()
+ : apdu::ApduResponse::Status::SW_WRONG_DATA;
+ switch (return_code) {
+ case apdu::ApduResponse::Status::SW_NO_ERROR:
+ case apdu::ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED: {
+ // Duplicate registration found. Call bogus registration to check for
+ // user presence (touch) and terminate the registration process.
+ DispatchDeviceRequest(
+ ConstructBogusU2fRegistrationCommand(),
+ base::BindOnce(&U2fRegisterOperation::OnRegisterResponseReceived,
+ weak_factory_.GetWeakPtr(),
+ true /* is_duplicate_registration */));
+ break;
+ }
+
+ case apdu::ApduResponse::Status::SW_WRONG_DATA:
+ // Continue to iterate through the provided key handles in the exclude
+ // list and check for already registered keys.
+ if (++it != request().exclude_list()->cend()) {
+ DispatchDeviceRequest(
+ ConvertToU2fCheckOnlySignCommand(request(), *it),
+ base::BindOnce(&U2fRegisterOperation::OnCheckForExcludedKeyHandle,
+ weak_factory_.GetWeakPtr(), it));
+ } else {
+ // Reached the end of exclude list with no duplicate credential.
+ // Proceed with registration.
+ DispatchDeviceRequest(
+ ConvertToU2fRegisterCommand(request()),
+ base::BindOnce(&U2fRegisterOperation::OnRegisterResponseReceived,
+ weak_factory_.GetWeakPtr(),
+ false /* is_duplicate_registration */));
+ }
+ break;
+ default:
+ // Some sort of failure occurred. Silently drop device request.
+ std::move(callback())
+ .Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
+ break;
+ }
+}
+
+} // namespace device
diff --git a/chromium/device/fido/u2f_register_operation.h b/chromium/device/fido/u2f_register_operation.h
new file mode 100644
index 00000000000..39a6fd1136f
--- /dev/null
+++ b/chromium/device/fido/u2f_register_operation.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_FIDO_U2F_REGISTER_OPERATION_H_
+#define DEVICE_FIDO_U2F_REGISTER_OPERATION_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/component_export.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "device/fido/device_operation.h"
+
+namespace device {
+
+class FidoDevice;
+class CtapMakeCredentialRequest;
+class AuthenticatorMakeCredentialResponse;
+class PublicKeyCredentialDescriptor;
+
+// Represents per device registration logic for U2F tokens. Handles regular U2F
+// registration as well as the logic of iterating key handles in the exclude
+// list and conducting check-only U2F sign to prevent duplicate registration.
+// U2fRegistrationOperation is owned by MakeCredentialTask and |request_| is
+// also owned by MakeCredentialTask.
+class COMPONENT_EXPORT(DEVICE_FIDO) U2fRegisterOperation
+ : public DeviceOperation<CtapMakeCredentialRequest,
+ AuthenticatorMakeCredentialResponse> {
+ public:
+ U2fRegisterOperation(FidoDevice* device,
+ const CtapMakeCredentialRequest& request,
+ DeviceResponseCallback callback);
+ ~U2fRegisterOperation() override;
+
+ // DeviceOperation:
+ void Start() override;
+
+ private:
+ using ExcludeListIterator =
+ std::vector<PublicKeyCredentialDescriptor>::const_iterator;
+
+ void TryRegistration(bool is_duplicate_registration);
+ void OnRegisterResponseReceived(
+ bool is_duplicate_registration,
+ base::Optional<std::vector<uint8_t>> device_response);
+ void OnCheckForExcludedKeyHandle(
+ ExcludeListIterator it,
+ base::Optional<std::vector<uint8_t>> device_response);
+
+ base::WeakPtrFactory<U2fRegisterOperation> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(U2fRegisterOperation);
+};
+
+} // namespace device
+
+#endif // DEVICE_FIDO_U2F_REGISTER_OPERATION_H_
diff --git a/chromium/device/fido/u2f_register_operation_unittest.cc b/chromium/device/fido/u2f_register_operation_unittest.cc
new file mode 100644
index 00000000000..2986830eab6
--- /dev/null
+++ b/chromium/device/fido/u2f_register_operation_unittest.cc
@@ -0,0 +1,264 @@
+// 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/u2f_register_operation.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/test/scoped_task_environment.h"
+#include "device/fido/authenticator_make_credential_response.h"
+#include "device/fido/ctap_make_credential_request.h"
+#include "device/fido/fido_constants.h"
+#include "device/fido/fido_parsing_utils.h"
+#include "device/fido/fido_test_data.h"
+#include "device/fido/mock_fido_device.h"
+#include "device/fido/test_callback_receiver.h"
+#include "device/fido/virtual_u2f_device.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device {
+
+using ::testing::_;
+
+namespace {
+
+// Creates a CtapMakeCredentialRequest with given |registered_keys| as
+// exclude list.
+CtapMakeCredentialRequest CreateRegisterRequestWithRegisteredKeys(
+ std::vector<PublicKeyCredentialDescriptor> registered_keys,
+ bool is_individual_attestation = false) {
+ PublicKeyCredentialRpEntity rp(test_data::kRelyingPartyId);
+ PublicKeyCredentialUserEntity user(
+ fido_parsing_utils::Materialize(test_data::kUserId));
+
+ CtapMakeCredentialRequest request(
+ test_data::kClientDataHash, std::move(rp), std::move(user),
+ PublicKeyCredentialParams(
+ std::vector<PublicKeyCredentialParams::CredentialInfo>(1)));
+ request.SetExcludeList(std::move(registered_keys));
+ request.SetIsIndividualAttestation(is_individual_attestation);
+ return request;
+}
+
+// Creates a CtapMakeCredentialRequest with an empty exclude list.
+CtapMakeCredentialRequest CreateRegisterRequest(
+ bool is_individual_attestation = false) {
+ return CreateRegisterRequestWithRegisteredKeys(
+ std::vector<PublicKeyCredentialDescriptor>(), is_individual_attestation);
+}
+
+using TestRegisterCallback = ::device::test::StatusAndValueCallbackReceiver<
+ CtapDeviceResponseCode,
+ base::Optional<AuthenticatorMakeCredentialResponse>>;
+
+} // namespace
+
+class U2fRegisterOperationTest : public ::testing::Test {
+ public:
+ TestRegisterCallback& register_callback_receiver() {
+ return register_callback_receiver_;
+ }
+
+ private:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ TestRegisterCallback register_callback_receiver_;
+};
+
+TEST_F(U2fRegisterOperationTest, TestRegisterSuccess) {
+ auto request = CreateRegisterRequest();
+ auto device = std::make_unique<MockFidoDevice>();
+ EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device"));
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fRegisterCommandApdu,
+ test_data::kApduEncodedNoErrorRegisterResponse);
+
+ auto u2f_register = std::make_unique<U2fRegisterOperation>(
+ device.get(), std::move(request),
+ register_callback_receiver().callback());
+ u2f_register->Start();
+ register_callback_receiver().WaitForCallback();
+
+ EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
+ register_callback_receiver().status());
+ ASSERT_TRUE(register_callback_receiver().value());
+ EXPECT_THAT(register_callback_receiver().value()->raw_credential_id(),
+ ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
+}
+
+TEST_F(U2fRegisterOperationTest, TestRegisterSuccessWithFake) {
+ auto request = CreateRegisterRequest();
+
+ auto device = std::make_unique<VirtualU2fDevice>();
+ auto u2f_register = std::make_unique<U2fRegisterOperation>(
+ device.get(), std::move(request),
+ register_callback_receiver().callback());
+ u2f_register->Start();
+ register_callback_receiver().WaitForCallback();
+
+ EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
+ register_callback_receiver().status());
+ // We don't verify the response from the fake, but do a quick sanity check.
+ ASSERT_TRUE(register_callback_receiver().value());
+ EXPECT_EQ(32ul,
+ register_callback_receiver().value()->raw_credential_id().size());
+}
+
+TEST_F(U2fRegisterOperationTest, TestDelayedSuccess) {
+ auto request = CreateRegisterRequest();
+
+ auto device = std::make_unique<MockFidoDevice>();
+ EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device"));
+
+ // Device error out once waiting for user presence before retrying.
+ ::testing::InSequence s;
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fRegisterCommandApdu,
+ test_data::kU2fConditionNotSatisfiedApduResponse);
+
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fRegisterCommandApdu,
+ test_data::kApduEncodedNoErrorRegisterResponse);
+
+ auto u2f_register = std::make_unique<U2fRegisterOperation>(
+ device.get(), std::move(request),
+ register_callback_receiver().callback());
+ u2f_register->Start();
+ register_callback_receiver().WaitForCallback();
+
+ EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
+ register_callback_receiver().status());
+ ASSERT_TRUE(register_callback_receiver().value());
+ EXPECT_THAT(register_callback_receiver().value()->raw_credential_id(),
+ ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
+}
+
+// Tests a scenario where a single device is connected and registration call
+// is received with two unknown key handles. We expect that two check
+// only sign-in calls be processed before registration.
+TEST_F(U2fRegisterOperationTest, TestRegistrationWithExclusionList) {
+ auto request = CreateRegisterRequestWithRegisteredKeys(
+ {PublicKeyCredentialDescriptor(
+ CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha)),
+ PublicKeyCredentialDescriptor(
+ CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(test_data::kKeyHandleBeta))});
+
+ auto device = std::make_unique<MockFidoDevice>();
+ EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
+ // DeviceTransact() will be called three times including two check only
+ // sign-in calls and one registration call. For the first two calls, device
+ // will invoke MockFidoDevice::WrongData as the authenticator did not create
+ // the two key handles provided in the exclude list. At the third call,
+ // MockFidoDevice::NoErrorRegister will be invoked after registration.
+ ::testing::InSequence s;
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fCheckOnlySignCommandApduWithKeyAlpha,
+ test_data::kU2fWrongDataApduResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fCheckOnlySignCommandApduWithKeyBeta,
+ test_data::kU2fWrongDataApduResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fRegisterCommandApdu,
+ test_data::kApduEncodedNoErrorRegisterResponse);
+
+ auto u2f_register = std::make_unique<U2fRegisterOperation>(
+ device.get(), std::move(request),
+ register_callback_receiver().callback());
+ u2f_register->Start();
+ register_callback_receiver().WaitForCallback();
+
+ ASSERT_TRUE(register_callback_receiver().value());
+ EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
+ register_callback_receiver().status());
+ EXPECT_THAT(register_callback_receiver().value()->raw_credential_id(),
+ ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
+}
+
+// Tests a scenario where single device is connected and registration is
+// called with a key in the exclude list that was created by this device. We
+// assume that the duplicate key is the last key handle in the exclude list.
+// Therefore, after duplicate key handle is found, the process is expected to
+// terminate after calling bogus registration which checks for user presence.
+TEST_F(U2fRegisterOperationTest, TestRegistrationWithDuplicateHandle) {
+ // Simulate two unknown key handles followed by a duplicate key.
+ auto request = CreateRegisterRequestWithRegisteredKeys(
+ {PublicKeyCredentialDescriptor(
+ CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha)),
+ PublicKeyCredentialDescriptor(
+ CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(test_data::kKeyHandleBeta)),
+ PublicKeyCredentialDescriptor(
+ CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(test_data::kKeyHandleGamma))});
+
+ auto device = std::make_unique<MockFidoDevice>();
+ EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
+ // For three keys in exclude list, the first two keys will invoke
+ // MockFidoDevice::WrongData and the final duplicate key handle will invoke
+ // MockFidoDevice::NoErrorSign. Once duplicate key handle is found, bogus
+ // registration is called to confirm user presence. This invokes
+ // MockFidoDevice::NoErrorRegister.
+ ::testing::InSequence s;
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fCheckOnlySignCommandApduWithKeyAlpha,
+ test_data::kU2fWrongDataApduResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fCheckOnlySignCommandApduWithKeyBeta,
+ test_data::kU2fWrongDataApduResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fCheckOnlySignCommandApduWithKeyGamma,
+ test_data::kApduEncodedNoErrorSignResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fFakeRegisterCommand,
+ test_data::kApduEncodedNoErrorRegisterResponse);
+
+ auto u2f_register = std::make_unique<U2fRegisterOperation>(
+ device.get(), std::move(request),
+ register_callback_receiver().callback());
+ u2f_register->Start();
+ register_callback_receiver().WaitForCallback();
+
+ EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrCredentialExcluded,
+ register_callback_receiver().status());
+ EXPECT_FALSE(register_callback_receiver().value());
+}
+
+MATCHER_P(IndicatesIndividualAttestation, expected, "") {
+ return arg.size() > 2 && ((arg[2] & 0x80) == 0x80) == expected;
+}
+
+TEST_F(U2fRegisterOperationTest, TestIndividualAttestation) {
+ // Test that the individual attestation flag is correctly reflected in the
+ // resulting registration APDU.
+ for (const auto& individual_attestation : {false, true}) {
+ SCOPED_TRACE(individual_attestation);
+ TestRegisterCallback cb;
+ auto request = CreateRegisterRequest(individual_attestation);
+
+ auto device = std::make_unique<MockFidoDevice>();
+ EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
+
+ device->ExpectRequestAndRespondWith(
+ individual_attestation
+ ? test_data::kU2fRegisterCommandApduWithIndividualAttestation
+ : test_data::kU2fRegisterCommandApdu,
+ test_data::kApduEncodedNoErrorRegisterResponse);
+
+ auto u2f_register = std::make_unique<U2fRegisterOperation>(
+ device.get(), std::move(request), cb.callback());
+ u2f_register->Start();
+ cb.WaitForCallback();
+
+ EXPECT_EQ(CtapDeviceResponseCode::kSuccess, cb.status());
+ ASSERT_TRUE(cb.value());
+ EXPECT_THAT(cb.value()->raw_credential_id(),
+ ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
+ }
+}
+
+} // namespace device
diff --git a/chromium/device/fido/u2f_register_unittest.cc b/chromium/device/fido/u2f_register_unittest.cc
deleted file mode 100644
index b29834525d9..00000000000
--- a/chromium/device/fido/u2f_register_unittest.cc
+++ /dev/null
@@ -1,506 +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/u2f_register.h"
-
-#include <iterator>
-#include <utility>
-
-#include "base/run_loop.h"
-#include "base/test/scoped_task_environment.h"
-#include "device/fido/authenticator_make_credential_response.h"
-#include "device/fido/fake_fido_discovery.h"
-#include "device/fido/fido_constants.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/mock_fido_device.h"
-#include "device/fido/test_callback_receiver.h"
-#include "device/fido/virtual_u2f_device.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace device {
-
-using ::testing::_;
-
-namespace {
-
-std::vector<uint8_t> GetTestRegisterRequest() {
- return fido_parsing_utils::Materialize(test_data::kU2fRegisterCommandApdu);
-}
-
-using TestRegisterCallback = ::device::test::StatusAndValueCallbackReceiver<
- FidoReturnCode,
- base::Optional<AuthenticatorMakeCredentialResponse>>;
-
-} // namespace
-
-class U2fRegisterTest : public ::testing::Test {
- public:
- void ForgeNextHidDiscovery() {
- discovery_ = scoped_fake_discovery_factory_.ForgeNextHidDiscovery();
- }
-
- std::unique_ptr<U2fRegister> CreateRegisterRequest() {
- return CreateRegisterRequestWithRegisteredKeys(
- std::vector<std::vector<uint8_t>>());
- }
-
- // Creates a U2F register request with `none` attestation, and the given
- // previously |registered_keys|.
- std::unique_ptr<U2fRegister> CreateRegisterRequestWithRegisteredKeys(
- std::vector<std::vector<uint8_t>> registered_keys) {
- ForgeNextHidDiscovery();
- return std::make_unique<U2fRegister>(
- nullptr /* connector */,
- base::flat_set<FidoTransportProtocol>(
- {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
- registered_keys,
- fido_parsing_utils::Materialize(test_data::kChallengeParameter),
- fido_parsing_utils::Materialize(test_data::kApplicationParameter),
- false /* is_individual_attestation */,
- register_callback_receiver_.callback());
- }
-
- test::FakeFidoDiscovery* discovery() const { return discovery_; }
-
- TestRegisterCallback& register_callback_receiver() {
- return register_callback_receiver_;
- }
-
- protected:
- base::test::ScopedTaskEnvironment scoped_task_environment_;
- std::vector<std::vector<uint8_t>> key_handles_;
- base::flat_set<FidoTransportProtocol> protocols_;
- test::ScopedFakeFidoDiscoveryFactory scoped_fake_discovery_factory_;
- test::FakeFidoDiscovery* discovery_;
- TestRegisterCallback register_callback_receiver_;
-};
-
-
-TEST_F(U2fRegisterTest, TestRegisterSuccess) {
- auto request = CreateRegisterRequest();
- request->Start();
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device"));
- EXPECT_CALL(*device, DeviceTransactPtr(GetTestRegisterRequest(), _))
- .WillOnce(testing::Invoke(MockFidoDevice::NoErrorRegister));
- EXPECT_CALL(*device, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device));
-
- register_callback_receiver().WaitForCallback();
- EXPECT_EQ(FidoReturnCode::kSuccess, register_callback_receiver().status());
- ASSERT_TRUE(register_callback_receiver().value());
- EXPECT_THAT(register_callback_receiver().value()->raw_credential_id(),
- ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
-}
-
-TEST_F(U2fRegisterTest, TestRegisterSuccessWithFake) {
- auto request = CreateRegisterRequest();
- request->Start();
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- auto device = std::make_unique<VirtualU2fDevice>();
- discovery()->AddDevice(std::move(device));
-
- register_callback_receiver().WaitForCallback();
- EXPECT_EQ(FidoReturnCode::kSuccess, register_callback_receiver().status());
-
- // We don't verify the response from the fake, but do a quick sanity check.
- ASSERT_TRUE(register_callback_receiver().value());
- EXPECT_EQ(32ul,
- register_callback_receiver().value()->raw_credential_id().size());
-}
-
-TEST_F(U2fRegisterTest, TestDelayedSuccess) {
- auto request = CreateRegisterRequest();
- request->Start();
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device"));
- // Go through the state machine twice before success.
- EXPECT_CALL(*device, DeviceTransactPtr(GetTestRegisterRequest(), _))
- .WillOnce(::testing::Invoke(MockFidoDevice::NotSatisfied))
- .WillOnce(::testing::Invoke(MockFidoDevice::NoErrorRegister));
- EXPECT_CALL(*device, TryWinkRef(_))
- .Times(2)
- .WillRepeatedly(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device));
-
- register_callback_receiver().WaitForCallback();
- EXPECT_EQ(FidoReturnCode::kSuccess, register_callback_receiver().status());
- ASSERT_TRUE(register_callback_receiver().value());
- EXPECT_THAT(register_callback_receiver().value()->raw_credential_id(),
- ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
-}
-
-TEST_F(U2fRegisterTest, TestMultipleDevices) {
- auto request = CreateRegisterRequest();
- request->Start();
-
- auto device0 = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device0, GetId()).WillRepeatedly(::testing::Return("device0"));
- EXPECT_CALL(*device0, DeviceTransactPtr(GetTestRegisterRequest(), _))
- .WillOnce(::testing::Invoke(MockFidoDevice::NotSatisfied));
- // One wink per device.
- EXPECT_CALL(*device0, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device0));
-
- // Second device will have a successful touch.
- auto device1 = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device1, GetId()).WillRepeatedly(::testing::Return("device1"));
- EXPECT_CALL(*device1, DeviceTransactPtr(GetTestRegisterRequest(), _))
- .WillOnce(::testing::Invoke(MockFidoDevice::NoErrorRegister));
- // One wink per device.
- EXPECT_CALL(*device1, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device1));
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- register_callback_receiver().WaitForCallback();
- EXPECT_EQ(FidoReturnCode::kSuccess, register_callback_receiver().status());
- ASSERT_TRUE(register_callback_receiver().value());
- EXPECT_THAT(register_callback_receiver().value()->raw_credential_id(),
- ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
-}
-
-// Tests a scenario where a single device is connected and registration call
-// is received with three unknown key handles. We expect that three check only
-// sign-in calls be processed before registration.
-TEST_F(U2fRegisterTest, TestSingleDeviceRegistrationWithExclusionList) {
- // Simulate three unknown key handles.
- auto request = CreateRegisterRequestWithRegisteredKeys(
- {fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha),
- fido_parsing_utils::Materialize(test_data::kKeyHandleBeta)});
- request->Start();
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
- // DeviceTransact() will be called three times including two check
- // only sign-in calls and one registration call. For the first two calls,
- // device will invoke MockFidoDevice::WrongData as the authenticator did not
- // create the two key handles provided in the exclude list. At the third
- // call, MockFidoDevice::NoErrorRegister will be invoked after registration.
- EXPECT_CALL(*device.get(),
- DeviceTransactPtr(
- fido_parsing_utils::Materialize(
- test_data::kU2fCheckOnlySignCommandApduWithKeyAlpha),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- EXPECT_CALL(
- *device.get(),
- DeviceTransactPtr(fido_parsing_utils::Materialize(
- test_data::kU2fCheckOnlySignCommandApduWithKeyBeta),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- EXPECT_CALL(*device.get(), DeviceTransactPtr(GetTestRegisterRequest(), _))
- .WillOnce(::testing::Invoke(MockFidoDevice::NoErrorRegister));
-
- // TryWink() will be called twice. First during the check only sign-in. After
- // check only sign operation is complete, request state is changed to IDLE,
- // and TryWink() is called again before Register() is called.
- EXPECT_CALL(*device, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device));
-
- register_callback_receiver().WaitForCallback();
- ASSERT_TRUE(register_callback_receiver().value());
- EXPECT_EQ(FidoReturnCode::kSuccess, register_callback_receiver().status());
- EXPECT_THAT(register_callback_receiver().value()->raw_credential_id(),
- ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
-}
-
-// Tests a scenario where two devices are connected and registration call is
-// received with three unknown key handles. We assume that user will proceed the
-// registration with second device, "device1".
-TEST_F(U2fRegisterTest, TestMultipleDeviceRegistrationWithExclusionList) {
- // Simulate three unknown key handles.
- auto request = CreateRegisterRequestWithRegisteredKeys(
- {fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha),
- fido_parsing_utils::Materialize(test_data::kKeyHandleBeta),
- fido_parsing_utils::Materialize(test_data::kKeyHandleGamma)});
- request->Start();
-
- auto device0 = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device0, GetId()).WillRepeatedly(::testing::Return("device0"));
- // DeviceTransact() will be called four times: three times to check for
- // duplicate key handles and once for registration. Since user
- // will register using "device1", the fourth call will invoke
- // MockFidoDevice::NotSatisfied.
- EXPECT_CALL(*device0.get(),
- DeviceTransactPtr(
- fido_parsing_utils::Materialize(
- test_data::kU2fCheckOnlySignCommandApduWithKeyAlpha),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- EXPECT_CALL(
- *device0.get(),
- DeviceTransactPtr(fido_parsing_utils::Materialize(
- test_data::kU2fCheckOnlySignCommandApduWithKeyBeta),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- EXPECT_CALL(*device0.get(),
- DeviceTransactPtr(
- fido_parsing_utils::Materialize(
- test_data::kU2fCheckOnlySignCommandApduWithKeyGamma),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- EXPECT_CALL(*device0.get(), DeviceTransactPtr(GetTestRegisterRequest(), _))
- .WillOnce(::testing::Invoke(MockFidoDevice::NotSatisfied));
-
- // TryWink() will be called twice on both devices -- during check only
- // sign-in operation and during registration attempt.
- EXPECT_CALL(*device0, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device0));
-
- auto device1 = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1"));
- // We assume that user registers with second device. Therefore, the fourth
- // DeviceTransact() will invoke MockFidoDevice::NoErrorRegister after
- // successful registration.
- EXPECT_CALL(*device1.get(),
- DeviceTransactPtr(
- fido_parsing_utils::Materialize(
- test_data::kU2fCheckOnlySignCommandApduWithKeyAlpha),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- EXPECT_CALL(
- *device1.get(),
- DeviceTransactPtr(fido_parsing_utils::Materialize(
- test_data::kU2fCheckOnlySignCommandApduWithKeyBeta),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- EXPECT_CALL(*device1.get(),
- DeviceTransactPtr(
- fido_parsing_utils::Materialize(
- test_data::kU2fCheckOnlySignCommandApduWithKeyGamma),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- EXPECT_CALL(*device1.get(), DeviceTransactPtr(GetTestRegisterRequest(), _))
- .WillOnce(::testing::Invoke(MockFidoDevice::NoErrorRegister));
-
- // TryWink() will be called twice on both devices -- during check only
- // sign-in operation and during registration attempt.
- EXPECT_CALL(*device1, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device1));
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- register_callback_receiver().WaitForCallback();
- EXPECT_EQ(FidoReturnCode::kSuccess, register_callback_receiver().status());
- ASSERT_TRUE(register_callback_receiver().value());
- EXPECT_THAT(register_callback_receiver().value()->raw_credential_id(),
- ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
-}
-
-// Tests a scenario where single device is connected and registration is called
-// with a key in the exclude list that was created by this device. We assume
-// that the duplicate key is the last key handle in the exclude list. Therefore,
-// after duplicate key handle is found, the process is expected to terminate
-// after calling bogus registration which checks for user presence.
-TEST_F(U2fRegisterTest, TestSingleDeviceRegistrationWithDuplicateHandle) {
- // Simulate three unknown key handles followed by a duplicate key.
- auto request = CreateRegisterRequestWithRegisteredKeys(
- {fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha),
- fido_parsing_utils::Materialize(test_data::kKeyHandleBeta),
- fido_parsing_utils::Materialize(test_data::kKeyHandleGamma)});
- request->Start();
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
- // For four keys in exclude list, the first three keys will invoke
- // MockFidoDevice::WrongData and the final duplicate key handle will invoke
- // MockFidoDevice::NoErrorSign. Once duplicate key handle is found, bogus
- // registration is called to confirm user presence. This invokes
- // MockFidoDevice::NoErrorRegister.
- EXPECT_CALL(*device.get(),
- DeviceTransactPtr(
- fido_parsing_utils::Materialize(
- test_data::kU2fCheckOnlySignCommandApduWithKeyAlpha),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- EXPECT_CALL(
- *device.get(),
- DeviceTransactPtr(fido_parsing_utils::Materialize(
- test_data::kU2fCheckOnlySignCommandApduWithKeyBeta),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- EXPECT_CALL(*device.get(),
- DeviceTransactPtr(
- fido_parsing_utils::Materialize(
- test_data::kU2fCheckOnlySignCommandApduWithKeyGamma),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::NoErrorSign));
-
- EXPECT_CALL(*device.get(),
- DeviceTransactPtr(fido_parsing_utils::Materialize(
- test_data::kU2fFakeRegisterCommand),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::NoErrorRegister));
-
- // Since duplicate key handle is found, registration process is terminated
- // before actual Register() is called on the device. Therefore, TryWink() is
- // invoked once.
- EXPECT_CALL(*device, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device));
-
- register_callback_receiver().WaitForCallback();
- EXPECT_EQ(FidoReturnCode::kUserConsentButCredentialExcluded,
- register_callback_receiver().status());
- EXPECT_FALSE(register_callback_receiver().value());
-}
-
-// Tests a scenario where one (device1) of the two devices connected has created
-// a key handle provided in exclude list. We assume that duplicate key is the
-// third key handle provided in the exclude list.
-TEST_F(U2fRegisterTest, TestMultipleDeviceRegistrationWithDuplicateHandle) {
- // Simulate two unknown key handles followed by a duplicate key.
- auto request = CreateRegisterRequestWithRegisteredKeys(
- {fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha),
- fido_parsing_utils::Materialize(test_data::kKeyHandleBeta),
- fido_parsing_utils::Materialize(test_data::kKeyHandleGamma)});
- request->Start();
-
- // Since the first device did not create any of the key handles provided in
- // exclude list, we expect that check only sign() should be called
- // four times, and all the calls to DeviceTransact() invoke
- // MockFidoDevice::WrongData.
- auto device0 = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0"));
- EXPECT_CALL(*device0.get(),
- DeviceTransactPtr(
- fido_parsing_utils::Materialize(
- test_data::kU2fCheckOnlySignCommandApduWithKeyAlpha),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- EXPECT_CALL(
- *device0.get(),
- DeviceTransactPtr(fido_parsing_utils::Materialize(
- test_data::kU2fCheckOnlySignCommandApduWithKeyBeta),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- EXPECT_CALL(*device0.get(),
- DeviceTransactPtr(
- fido_parsing_utils::Materialize(
- test_data::kU2fCheckOnlySignCommandApduWithKeyGamma),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- EXPECT_CALL(*device0, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device0));
-
- // Since the last key handle in exclude list is a duplicate key, we expect
- // that the first three calls to check only sign() invoke
- // MockFidoDevice::WrongData and that fourth sign() call invoke
- // MockFidoDevice::NoErrorSign. After duplicate key is found, process is
- // terminated after user presence is verified using bogus registration, which
- // invokes MockFidoDevice::NoErrorRegister.
- auto device1 = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device1, GetId()).WillRepeatedly(::testing::Return("device1"));
- EXPECT_CALL(*device1.get(),
- DeviceTransactPtr(
- fido_parsing_utils::Materialize(
- test_data::kU2fCheckOnlySignCommandApduWithKeyAlpha),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- EXPECT_CALL(
- *device1.get(),
- DeviceTransactPtr(fido_parsing_utils::Materialize(
- test_data::kU2fCheckOnlySignCommandApduWithKeyBeta),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- EXPECT_CALL(*device1.get(),
- DeviceTransactPtr(
- fido_parsing_utils::Materialize(
- test_data::kU2fCheckOnlySignCommandApduWithKeyGamma),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::NoErrorSign));
-
- EXPECT_CALL(*device1.get(),
- DeviceTransactPtr(fido_parsing_utils::Materialize(
- test_data::kU2fFakeRegisterCommand),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::NoErrorRegister));
-
- EXPECT_CALL(*device1, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device1));
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- register_callback_receiver().WaitForCallback();
- EXPECT_EQ(FidoReturnCode::kUserConsentButCredentialExcluded,
- register_callback_receiver().status());
- EXPECT_FALSE(register_callback_receiver().value());
-}
-
-MATCHER_P(IndicatesIndividualAttestation, expected, "") {
- return arg.size() > 2 && ((arg[2] & 0x80) == 0x80) == expected;
-}
-
-TEST_F(U2fRegisterTest, TestIndividualAttestation) {
- // Test that the individual attestation flag is correctly reflected in the
- // resulting registration APDU.
- for (const auto& individual_attestation : {false, true}) {
- SCOPED_TRACE(individual_attestation);
-
- ForgeNextHidDiscovery();
- TestRegisterCallback cb;
- auto request = std::make_unique<U2fRegister>(
- nullptr /* connector */,
- base::flat_set<FidoTransportProtocol>(
- {FidoTransportProtocol::kUsbHumanInterfaceDevice}) /* transports */,
- key_handles_,
- fido_parsing_utils::Materialize(test_data::kChallengeParameter),
- fido_parsing_utils::Materialize(test_data::kApplicationParameter),
- individual_attestation, cb.callback());
- request->Start();
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
- EXPECT_CALL(*device,
- DeviceTransactPtr(
- IndicatesIndividualAttestation(individual_attestation), _))
- .WillOnce(::testing::Invoke(MockFidoDevice::NoErrorRegister));
- EXPECT_CALL(*device, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device));
-
- cb.WaitForCallback();
- EXPECT_EQ(FidoReturnCode::kSuccess, cb.status());
- ASSERT_TRUE(cb.value());
- EXPECT_THAT(cb.value()->raw_credential_id(),
- ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
- }
-}
-
-} // namespace device
diff --git a/chromium/device/fido/u2f_request.cc b/chromium/device/fido/u2f_request.cc
deleted file mode 100644
index fd81c6cc2fa..00000000000
--- a/chromium/device/fido/u2f_request.cc
+++ /dev/null
@@ -1,198 +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/u2f_request.h"
-
-#include <algorithm>
-#include <set>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/stl_util.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "build/build_config.h"
-#include "components/apdu/apdu_command.h"
-#include "device/fido/u2f_command_constructor.h"
-#include "services/service_manager/public/cpp/connector.h"
-
-namespace device {
-
-U2fRequest::U2fRequest(service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& transports,
- std::vector<uint8_t> application_parameter,
- std::vector<uint8_t> challenge_digest,
- std::vector<std::vector<uint8_t>> registered_keys)
- : state_(State::INIT),
- application_parameter_(application_parameter),
- challenge_digest_(challenge_digest),
- registered_keys_(registered_keys),
- weak_factory_(this) {
- for (const auto transport : transports) {
- auto discovery = FidoDiscovery::Create(transport, connector);
- if (discovery == nullptr) {
- // This can occur if the given transport is not supported, or in tests
- // when the given transport is not configured.
- continue;
- }
- discovery->set_observer(this);
- discoveries_.push_back(std::move(discovery));
- }
-}
-
-U2fRequest::~U2fRequest() = default;
-
-void U2fRequest::Start() {
- if (state_ == State::INIT) {
- state_ = State::IDLE;
- for (auto& discovery : discoveries_)
- discovery->Start();
- }
-}
-
-base::Optional<std::vector<uint8_t>> U2fRequest::GetU2fSignApduCommand(
- const std::vector<uint8_t>& application_parameter,
- const std::vector<uint8_t>& key_handle,
- bool is_check_only_sign) const {
- return ConstructU2fSignCommand(application_parameter, challenge_digest_,
- key_handle, is_check_only_sign);
-}
-
-base::Optional<std::vector<uint8_t>> U2fRequest::GetU2fRegisterApduCommand(
- bool is_individual_attestation) const {
- return ConstructU2fRegisterCommand(application_parameter_, challenge_digest_,
- is_individual_attestation);
-}
-
-void U2fRequest::Transition() {
- switch (state_) {
- case State::IDLE:
- IterateDevice();
- if (!current_device_) {
- // No devices available.
- state_ = State::OFF;
- break;
- }
- state_ = State::WINK;
- current_device_->TryWink(
- base::BindOnce(&U2fRequest::Transition, weak_factory_.GetWeakPtr()));
- break;
- case State::WINK:
- state_ = State::BUSY;
- TryDevice();
- break;
- default:
- break;
- }
-}
-
-void U2fRequest::InitiateDeviceTransaction(
- base::Optional<std::vector<uint8_t>> cmd,
- FidoDevice::DeviceCallback callback) {
- if (!cmd) {
- std::move(callback).Run(base::nullopt);
- return;
- }
- current_device_->DeviceTransact(std::move(*cmd), std::move(callback));
-}
-
-void U2fRequest::AbandonCurrentDeviceAndTransition() {
- DCHECK_NE(nullptr, current_device_);
- abandoned_devices_.emplace_back(std::exchange(current_device_, nullptr));
- state_ = State::IDLE;
- Transition();
-}
-
-void U2fRequest::DiscoveryStarted(FidoDiscovery* discovery, bool success) {
-#if DCHECK_IS_ON()
- if (success) {
- // FidoDiscovery::Observer::DeviceAdded should have been already dispatched
- // for each of devices already known by |discovery|, so we should never
- // learn anything new here.
- std::set<std::string> device_ids_known_to_request;
- for (const auto* device : attempted_devices_)
- device_ids_known_to_request.insert(device->GetId());
- if (current_device_)
- device_ids_known_to_request.insert(current_device_->GetId());
- for (const auto* device : devices_)
- device_ids_known_to_request.insert(device->GetId());
- for (const auto* device : abandoned_devices_)
- device_ids_known_to_request.insert(device->GetId());
-
- std::set<std::string> device_ids_from_newly_started_discovery;
- for (const auto* device : discovery->GetDevices())
- device_ids_from_newly_started_discovery.insert(device->GetId());
- DCHECK(base::STLIncludes(device_ids_known_to_request,
- device_ids_from_newly_started_discovery));
- }
-#endif
-
- started_count_++;
- if ((state_ == State::IDLE || state_ == State::OFF) &&
- (success || started_count_ == discoveries_.size())) {
- state_ = State::IDLE;
- Transition();
- }
-}
-
-void U2fRequest::DeviceAdded(FidoDiscovery* discovery, FidoDevice* device) {
- devices_.push_back(device);
-
- // Start the state machine if this is the only device
- if (state_ == State::OFF) {
- state_ = State::IDLE;
- delay_callback_.Cancel();
- Transition();
- }
-}
-
-void U2fRequest::DeviceRemoved(FidoDiscovery* discovery, FidoDevice* device) {
- const std::string device_id = device->GetId();
- auto device_id_eq = [&device_id](const FidoDevice* this_device) {
- return device_id == this_device->GetId();
- };
-
- // Check if the active device was removed
- if (current_device_ && device_id_eq(current_device_)) {
- current_device_ = nullptr;
- state_ = State::IDLE;
- Transition();
- return;
- }
-
- // Remove the device if it exists in either device list
- devices_.remove_if(device_id_eq);
- attempted_devices_.remove_if(device_id_eq);
- abandoned_devices_.remove_if(device_id_eq);
-}
-
-void U2fRequest::IterateDevice() {
- // Move active device to attempted device list
- if (current_device_) {
- attempted_devices_.push_back(current_device_);
- current_device_ = nullptr;
- }
-
- // If there is an additional device on device list, make it active.
- // Otherwise, if all devices have been tried, move attempted devices back to
- // the main device list.
- if (devices_.size() > 0) {
- current_device_ = devices_.front();
- devices_.pop_front();
- } else if (attempted_devices_.size() > 0) {
- devices_ = std::move(attempted_devices_);
- // After trying every device, wait 200ms before trying again.
- delay_callback_.Reset(
- base::Bind(&U2fRequest::OnWaitComplete, weak_factory_.GetWeakPtr()));
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, delay_callback_.callback(),
- base::TimeDelta::FromMilliseconds(200));
- }
-}
-
-void U2fRequest::OnWaitComplete() {
- state_ = State::IDLE;
- Transition();
-}
-
-} // namespace device
diff --git a/chromium/device/fido/u2f_request.h b/chromium/device/fido/u2f_request.h
deleted file mode 100644
index e8717f0fa7f..00000000000
--- a/chromium/device/fido/u2f_request.h
+++ /dev/null
@@ -1,137 +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_FIDO_U2F_REQUEST_H_
-#define DEVICE_FIDO_U2F_REQUEST_H_
-
-#include <stdint.h>
-
-#include <list>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/cancelable_callback.h"
-#include "base/component_export.h"
-#include "base/containers/flat_set.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/optional.h"
-#include "device/fido/fido_constants.h"
-#include "device/fido/fido_device.h"
-#include "device/fido/fido_discovery.h"
-#include "device/fido/fido_transport_protocol.h"
-
-namespace service_manager {
-class Connector;
-}
-
-namespace device {
-
-class COMPONENT_EXPORT(DEVICE_FIDO) U2fRequest
- : public FidoDiscovery::Observer {
- public:
- using VersionCallback = base::OnceCallback<void(ProtocolVersion version)>;
-
- // U2fRequest will create a discovery instance and register itself as an
- // observer for each passed in transport protocol.
- // TODO(https://crbug.com/769631): Remove the dependency on Connector once U2F
- // is servicified.
- U2fRequest(service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& transports,
- std::vector<uint8_t> application_parameter,
- std::vector<uint8_t> challenge_digest,
- std::vector<std::vector<uint8_t>> registered_keys);
- ~U2fRequest() override;
-
- void Start();
-
- // Functions below are implemented in U2fRequest interface(and not in its
- // respective subclasses) as both {register, sign} commands are used during
- // registration process. That is, check-only sign command is sent during
- // registration to prevent duplicate registration.
- //
- // Returns APDU U2F request commands. Null optional is returned for
- // incorrectly formatted parameter.
- base::Optional<std::vector<uint8_t>> GetU2fSignApduCommand(
- const std::vector<uint8_t>& application_parameter,
- const std::vector<uint8_t>& key_handle,
- bool is_check_only_sign = false) const;
- base::Optional<std::vector<uint8_t>> GetU2fRegisterApduCommand(
- bool is_individual_attestation) const;
-
- protected:
- enum class State {
- INIT,
- BUSY,
- WINK,
- IDLE,
- OFF,
- COMPLETE,
- };
-
- void Transition();
-
- // Starts sign, register, and version request transaction on
- // |current_device_|.
- void InitiateDeviceTransaction(base::Optional<std::vector<uint8_t>> cmd,
- FidoDevice::DeviceCallback callback);
-
- virtual void TryDevice() = 0;
-
- // Moves |current_device_| to the list of |abandoned_devices_| and iterates
- // |current_device_|. Expects |current_device_| to be valid prior to calling
- // this method.
- void AbandonCurrentDeviceAndTransition();
-
- // Hold handles to the devices known to the system. Known devices are
- // partitioned into four parts:
- // [attempted_devices_), current_device_, [devices_), [abandoned_devices_)
- // During device iteration the |current_device_| gets pushed to
- // |attempted_devices_|, and, if possible, the first element of |devices_|
- // gets popped and becomes the new |current_device_|. Once all |devices_| are
- // exhausted, |attempted_devices_| get moved into |devices_| and
- // |current_device_| is reset. |abandoned_devices_| contains a list of devices
- // that have been tried in the past, but were abandoned because of an error.
- // Devices in this list won't be tried again.
- FidoDevice* current_device_ = nullptr;
- std::list<FidoDevice*> devices_;
- std::list<FidoDevice*> attempted_devices_;
- std::list<FidoDevice*> abandoned_devices_;
- State state_;
- std::vector<std::unique_ptr<FidoDiscovery>> discoveries_;
- std::vector<uint8_t> application_parameter_;
- std::vector<uint8_t> challenge_digest_;
- std::vector<std::vector<uint8_t>> registered_keys_;
-
- private:
- FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestIterateDevice);
- FRIEND_TEST_ALL_PREFIXES(U2fRequestTest,
- TestAbandonCurrentDeviceAndTransition);
- FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestBasicMachine);
- FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestAlreadyPresentDevice);
- FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestMultipleDiscoveries);
- FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestSlowDiscovery);
- FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestMultipleDiscoveriesWithFailures);
- FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestLegacyVersionRequest);
-
- // FidoDiscovery::Observer
- void DiscoveryStarted(FidoDiscovery* discovery, bool success) override;
- void DeviceAdded(FidoDiscovery* discovery, FidoDevice* device) override;
- void DeviceRemoved(FidoDiscovery* discovery, FidoDevice* device) override;
-
- void IterateDevice();
- void OnWaitComplete();
-
- base::CancelableClosure delay_callback_;
- size_t started_count_ = 0;
-
- base::WeakPtrFactory<U2fRequest> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(U2fRequest);
-};
-
-} // namespace device
-
-#endif // DEVICE_FIDO_U2F_REQUEST_H_
diff --git a/chromium/device/fido/u2f_request_unittest.cc b/chromium/device/fido/u2f_request_unittest.cc
deleted file mode 100644
index 33124f01557..00000000000
--- a/chromium/device/fido/u2f_request_unittest.cc
+++ /dev/null
@@ -1,364 +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/u2f_request.h"
-
-#include <list>
-#include <string>
-#include <utility>
-
-#include "base/test/scoped_task_environment.h"
-#include "device/fido/fake_fido_discovery.h"
-#include "device/fido/fido_transport_protocol.h"
-#include "device/fido/mock_fido_device.h"
-#include "device/fido/test_callback_receiver.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ::testing::_;
-
-namespace device {
-
-namespace {
-
-class FakeU2fRequest : public U2fRequest {
- public:
- explicit FakeU2fRequest(
- const base::flat_set<FidoTransportProtocol>& transports)
- : U2fRequest(nullptr /* connector */,
- transports,
- std::vector<uint8_t>(),
- std::vector<uint8_t>(),
- std::vector<std::vector<uint8_t>>()) {}
- ~FakeU2fRequest() override = default;
-
- void TryDevice() override {
- // Do nothing.
- }
-};
-
-using TestVersionCallback =
- ::device::test::ValueCallbackReceiver<ProtocolVersion>;
-
-} // namespace
-
-class U2fRequestTest : public ::testing::Test {
- protected:
- base::test::ScopedTaskEnvironment& scoped_task_environment() {
- return scoped_task_environment_;
- }
-
- test::ScopedFakeFidoDiscoveryFactory& discovery_factory() {
- return discovery_factory_;
- }
-
- TestVersionCallback& version_callback_receiver() {
- return version_callback_receiver_;
- }
-
- private:
- base::test::ScopedTaskEnvironment scoped_task_environment_{
- base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
- TestVersionCallback version_callback_receiver_;
- test::ScopedFakeFidoDiscoveryFactory discovery_factory_;
-};
-
-TEST_F(U2fRequestTest, TestIterateDevice) {
- auto* discovery = discovery_factory().ForgeNextHidDiscovery();
-
- FakeU2fRequest request({FidoTransportProtocol::kUsbHumanInterfaceDevice});
- request.Start();
-
- auto device0 = std::make_unique<MockFidoDevice>();
- auto device1 = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device0, GetId()).WillRepeatedly(::testing::Return("device0"));
- EXPECT_CALL(*device1, GetId()).WillRepeatedly(::testing::Return("device1"));
-
- // Add two U2F devices
- discovery->AddDevice(std::move(device0));
- discovery->AddDevice(std::move(device1));
-
- // Move first device to current.
- request.IterateDevice();
- ASSERT_NE(nullptr, request.current_device_);
- EXPECT_EQ(static_cast<size_t>(1), request.devices_.size());
-
- // Move second device to current, first to attempted.
- request.IterateDevice();
- ASSERT_NE(nullptr, request.current_device_);
- EXPECT_EQ(static_cast<size_t>(1), request.attempted_devices_.size());
-
- // Move second device from current to attempted, move attempted to devices as
- // all devices have been attempted.
- request.IterateDevice();
-
- ASSERT_EQ(nullptr, request.current_device_);
- EXPECT_EQ(static_cast<size_t>(2), request.devices_.size());
- EXPECT_EQ(static_cast<size_t>(0), request.attempted_devices_.size());
-
- // Moving attempted devices results in a delayed retry, after which the first
- // device will be tried again. Check for the expected behavior here.
- auto* mock_device = static_cast<MockFidoDevice*>(request.devices_.front());
- EXPECT_CALL(*mock_device, TryWinkRef(_));
- scoped_task_environment().FastForwardUntilNoTasksRemain();
-
- EXPECT_EQ(mock_device, request.current_device_);
- EXPECT_EQ(static_cast<size_t>(1), request.devices_.size());
- EXPECT_EQ(static_cast<size_t>(0), request.attempted_devices_.size());
-}
-
-TEST_F(U2fRequestTest, TestAbandonCurrentDeviceAndTransition) {
- auto* discovery = discovery_factory().ForgeNextHidDiscovery();
-
- FakeU2fRequest request({FidoTransportProtocol::kUsbHumanInterfaceDevice});
- request.Start();
-
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
-
- discovery->AddDevice(std::move(device));
-
- // Move device to current.
- request.IterateDevice();
- EXPECT_NE(nullptr, request.current_device_);
-
- // Abandon device.
- request.AbandonCurrentDeviceAndTransition();
- EXPECT_EQ(nullptr, request.current_device_);
- EXPECT_EQ(1u, request.abandoned_devices_.size());
-
- // Iterating through the device list should not change the state.
- request.IterateDevice();
- EXPECT_EQ(nullptr, request.current_device_);
- EXPECT_EQ(1u, request.abandoned_devices_.size());
-
- // Removing the device from the discovery should clear it from the list.
- discovery->RemoveDevice("device");
- EXPECT_TRUE(request.abandoned_devices_.empty());
-}
-
-TEST_F(U2fRequestTest, TestBasicMachine) {
- auto* discovery = discovery_factory().ForgeNextHidDiscovery();
- FakeU2fRequest request({FidoTransportProtocol::kUsbHumanInterfaceDevice});
- request.Start();
-
- ASSERT_NO_FATAL_FAILURE(discovery->WaitForCallToStartAndSimulateSuccess());
-
- // Add one U2F device
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId());
- EXPECT_CALL(*device, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery->AddDevice(std::move(device));
-
- EXPECT_EQ(U2fRequest::State::BUSY, request.state_);
-}
-
-TEST_F(U2fRequestTest, TestAlreadyPresentDevice) {
- auto* discovery = discovery_factory().ForgeNextHidDiscovery();
-
- FakeU2fRequest request({FidoTransportProtocol::kUsbHumanInterfaceDevice});
- request.Start();
-
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
- discovery->AddDevice(std::move(device));
-
- ASSERT_NO_FATAL_FAILURE(discovery->WaitForCallToStartAndSimulateSuccess());
- EXPECT_NE(nullptr, request.current_device_);
-}
-
-TEST_F(U2fRequestTest, TestMultipleDiscoveries) {
- auto* discovery_1 = discovery_factory().ForgeNextHidDiscovery();
- auto* discovery_2 = discovery_factory().ForgeNextBleDiscovery();
-
- // Create a fake request with two different discoveries that both start up
- // successfully.
- FakeU2fRequest request({FidoTransportProtocol::kUsbHumanInterfaceDevice,
- FidoTransportProtocol::kBluetoothLowEnergy});
- request.Start();
-
- ASSERT_NO_FATAL_FAILURE(discovery_1->WaitForCallToStartAndSimulateSuccess());
- ASSERT_NO_FATAL_FAILURE(discovery_2->WaitForCallToStartAndSimulateSuccess());
-
- // Let each discovery find a device.
- auto device_1 = std::make_unique<MockFidoDevice>();
- auto device_2 = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device_1, GetId()).WillRepeatedly(::testing::Return("device_1"));
- EXPECT_CALL(*device_2, GetId()).WillRepeatedly(::testing::Return("device_2"));
-
- auto* device_1_ptr = device_1.get();
- auto* device_2_ptr = device_2.get();
- discovery_1->AddDevice(std::move(device_1));
- discovery_2->AddDevice(std::move(device_2));
-
- // Iterate through the devices and make sure they are considered in the same
- // order as they were added.
- EXPECT_EQ(device_1_ptr, request.current_device_);
- request.IterateDevice();
-
- EXPECT_EQ(device_2_ptr, request.current_device_);
- request.IterateDevice();
-
- EXPECT_EQ(nullptr, request.current_device_);
- EXPECT_EQ(2u, request.devices_.size());
-
- // Add a third device.
- auto device_3 = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device_3, GetId()).WillRepeatedly(::testing::Return("device_3"));
- auto* device_3_ptr = device_3.get();
- discovery_1->AddDevice(std::move(device_3));
-
- // Exhaust the timeout and remove the first two devices, making sure the just
- // added one is the only device considered.
- scoped_task_environment().FastForwardUntilNoTasksRemain();
- discovery_2->RemoveDevice("device_2");
- discovery_1->RemoveDevice("device_1");
- EXPECT_EQ(device_3_ptr, request.current_device_);
-
- // Finally remove the last remaining device.
- discovery_1->RemoveDevice("device_3");
- EXPECT_EQ(nullptr, request.current_device_);
-}
-
-TEST_F(U2fRequestTest, TestSlowDiscovery) {
- auto* fast_discovery = discovery_factory().ForgeNextHidDiscovery();
- auto* slow_discovery = discovery_factory().ForgeNextBleDiscovery();
-
- // Create a fake request with two different discoveries that start at
- // different times.
- FakeU2fRequest request({FidoTransportProtocol::kUsbHumanInterfaceDevice,
- FidoTransportProtocol::kBluetoothLowEnergy});
-
- auto fast_device = std::make_unique<MockFidoDevice>();
- auto slow_device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*fast_device, GetId())
- .WillRepeatedly(::testing::Return("fast_device"));
- EXPECT_CALL(*slow_device, GetId())
- .WillRepeatedly(::testing::Return("slow_device"));
-
- bool fast_winked = false;
- EXPECT_CALL(*fast_device, TryWinkRef(_))
- .WillOnce(
- ::testing::DoAll(::testing::Assign(&fast_winked, true),
- ::testing::Invoke(MockFidoDevice::WinkDoNothing)))
- .WillRepeatedly(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- bool slow_winked = false;
- EXPECT_CALL(*slow_device, TryWinkRef(_))
- .WillOnce(testing::DoAll(testing::Assign(&slow_winked, true),
- testing::Invoke(MockFidoDevice::WinkDoNothing)));
-
- auto* fast_device_ptr = fast_device.get();
- auto* slow_device_ptr = slow_device.get();
-
- EXPECT_EQ(nullptr, request.current_device_);
- request.state_ = U2fRequest::State::INIT;
-
- // The discoveries will be started and |fast_discovery| will succeed
- // immediately with a device already found.
- request.Start();
-
- EXPECT_FALSE(fast_winked);
-
- ASSERT_NO_FATAL_FAILURE(fast_discovery->WaitForCallToStart());
- fast_discovery->AddDevice(std::move(fast_device));
- ASSERT_NO_FATAL_FAILURE(fast_discovery->SimulateStarted(true /* success */));
-
- EXPECT_TRUE(fast_winked);
- EXPECT_EQ(fast_device_ptr, request.current_device_);
- EXPECT_EQ(U2fRequest::State::BUSY, request.state_);
-
- // There are no more devices at this time.
- request.state_ = U2fRequest::State::IDLE;
- request.Transition();
- EXPECT_EQ(nullptr, request.current_device_);
- EXPECT_EQ(U2fRequest::State::OFF, request.state_);
-
- // All devices have been tried and have been re-enqueued to try again in the
- // future. Now |slow_discovery| starts:
- ASSERT_TRUE(slow_discovery->is_start_requested());
- ASSERT_FALSE(slow_discovery->is_running());
- ASSERT_NO_FATAL_FAILURE(slow_discovery->WaitForCallToStart());
- slow_discovery->AddDevice(std::move(slow_device));
- ASSERT_NO_FATAL_FAILURE(slow_discovery->SimulateStarted(true /* success */));
-
- // |fast_device| is already enqueued and will be retried immediately.
- EXPECT_EQ(fast_device_ptr, request.current_device_);
- EXPECT_EQ(U2fRequest::State::BUSY, request.state_);
-
- // Next the newly found |slow_device| will be tried.
- request.state_ = U2fRequest::State::IDLE;
- EXPECT_FALSE(slow_winked);
- request.Transition();
- EXPECT_TRUE(slow_winked);
- EXPECT_EQ(slow_device_ptr, request.current_device_);
- EXPECT_EQ(U2fRequest::State::BUSY, request.state_);
-
- // All discoveries are complete so the request transitions to |OFF|.
- request.state_ = U2fRequest::State::IDLE;
- request.Transition();
- EXPECT_EQ(nullptr, request.current_device_);
- EXPECT_EQ(U2fRequest::State::OFF, request.state_);
-}
-
-TEST_F(U2fRequestTest, TestMultipleDiscoveriesWithFailures) {
- // Create a fake request with two different discoveries that both start up
- // unsuccessfully.
- {
- auto* discovery_1 = discovery_factory().ForgeNextHidDiscovery();
- auto* discovery_2 = discovery_factory().ForgeNextBleDiscovery();
-
- FakeU2fRequest request({FidoTransportProtocol::kUsbHumanInterfaceDevice,
- FidoTransportProtocol::kBluetoothLowEnergy});
- request.Start();
-
- ASSERT_NO_FATAL_FAILURE(discovery_1->WaitForCallToStart());
- ASSERT_NO_FATAL_FAILURE(discovery_1->SimulateStarted(false /* success */));
- ASSERT_NO_FATAL_FAILURE(discovery_2->WaitForCallToStart());
- ASSERT_NO_FATAL_FAILURE(discovery_2->SimulateStarted(false /* success */));
-
- EXPECT_EQ(U2fRequest::State::OFF, request.state_);
- }
-
- // Create a fake request with two different discoveries, where only one starts
- // up successfully.
- {
- auto* discovery_1 = discovery_factory().ForgeNextHidDiscovery();
- auto* discovery_2 = discovery_factory().ForgeNextBleDiscovery();
-
- FakeU2fRequest request({FidoTransportProtocol::kUsbHumanInterfaceDevice,
- FidoTransportProtocol::kBluetoothLowEnergy});
- request.Start();
-
- ASSERT_NO_FATAL_FAILURE(discovery_1->WaitForCallToStart());
- ASSERT_NO_FATAL_FAILURE(discovery_1->SimulateStarted(false /* success */));
- ASSERT_NO_FATAL_FAILURE(discovery_2->WaitForCallToStart());
- ASSERT_NO_FATAL_FAILURE(discovery_2->SimulateStarted(true /* success */));
-
- auto device0 = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device0, GetId())
- .WillRepeatedly(::testing::Return("device_0"));
- EXPECT_CALL(*device0, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery_2->AddDevice(std::move(device0));
-
- EXPECT_EQ(U2fRequest::State::BUSY, request.state_);
-
- // Simulate an action that sets the request state to idle.
- // This and the call to Transition() below is necessary to trigger iterating
- // and trying the new device.
- request.state_ = U2fRequest::State::IDLE;
-
- // Adding another device should trigger examination and a busy state.
- auto device1 = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device1, GetId())
- .WillRepeatedly(::testing::Return("device_1"));
- EXPECT_CALL(*device1, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery_2->AddDevice(std::move(device1));
-
- request.Transition();
- EXPECT_EQ(U2fRequest::State::BUSY, request.state_);
- }
-}
-
-} // namespace device
diff --git a/chromium/device/fido/u2f_sign.cc b/chromium/device/fido/u2f_sign.cc
deleted file mode 100644
index 1cad2c8c8fa..00000000000
--- a/chromium/device/fido/u2f_sign.cc
+++ /dev/null
@@ -1,155 +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/u2f_sign.h"
-
-#include <utility>
-
-#include "components/apdu/apdu_command.h"
-#include "components/apdu/apdu_response.h"
-#include "device/fido/u2f_command_constructor.h"
-#include "services/service_manager/public/cpp/connector.h"
-
-namespace device {
-
-// static
-std::unique_ptr<U2fRequest> U2fSign::TrySign(
- service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& transports,
- std::vector<std::vector<uint8_t>> registered_keys,
- std::vector<uint8_t> challenge_digest,
- std::vector<uint8_t> application_parameter,
- base::Optional<std::vector<uint8_t>> alt_application_parameter,
- SignResponseCallback completion_callback) {
- std::unique_ptr<U2fRequest> request = std::make_unique<U2fSign>(
- connector, transports, registered_keys, challenge_digest,
- application_parameter, std::move(alt_application_parameter),
- std::move(completion_callback));
- request->Start();
-
- return request;
-}
-
-U2fSign::U2fSign(service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& transports,
- std::vector<std::vector<uint8_t>> registered_keys,
- std::vector<uint8_t> challenge_digest,
- std::vector<uint8_t> application_parameter,
- base::Optional<std::vector<uint8_t>> alt_application_parameter,
- SignResponseCallback completion_callback)
- : U2fRequest(connector,
- transports,
- std::move(application_parameter),
- std::move(challenge_digest),
- std::move(registered_keys)),
- alt_application_parameter_(std::move(alt_application_parameter)),
- completion_callback_(std::move(completion_callback)),
- weak_factory_(this) {
- // U2F devices require at least one key handle.
- // TODO(crbug.com/831712): When CTAP2 authenticators are supported, this check
- // should be enforced by handlers in fido/device on a per-device basis.
- CHECK(!registered_keys_.empty());
-}
-
-U2fSign::~U2fSign() = default;
-
-void U2fSign::TryDevice() {
- DCHECK(current_device_);
-
- // Try signing current device with the first registered key.
- auto it = registered_keys_.cbegin();
- InitiateDeviceTransaction(
- GetU2fSignApduCommand(application_parameter_, *it),
- base::BindOnce(&U2fSign::OnTryDevice, weak_factory_.GetWeakPtr(), it,
- ApplicationParameterType::kPrimary));
-}
-
-void U2fSign::OnTryDevice(std::vector<std::vector<uint8_t>>::const_iterator it,
- ApplicationParameterType application_parameter_type,
- base::Optional<std::vector<uint8_t>> response) {
- const auto apdu_response =
- response ? apdu::ApduResponse::CreateFromMessage(std::move(*response))
- : base::nullopt;
- auto return_code = apdu_response ? apdu_response->status()
- : apdu::ApduResponse::Status::SW_WRONG_DATA;
- auto response_data = return_code == apdu::ApduResponse::Status::SW_WRONG_DATA
- ? std::vector<uint8_t>()
- : apdu_response->data();
-
- switch (return_code) {
- case apdu::ApduResponse::Status::SW_NO_ERROR: {
- state_ = State::COMPLETE;
- if (it == registered_keys_.cend()) {
- // This was a response to a fake enrollment. Return an empty key handle.
- std::move(completion_callback_)
- .Run(FidoReturnCode::kUserConsentButCredentialNotRecognized,
- base::nullopt);
- } else {
- const std::vector<uint8_t>* const application_parameter_used =
- application_parameter_type == ApplicationParameterType::kPrimary
- ? &application_parameter_
- : &alt_application_parameter_.value();
- auto sign_response =
- AuthenticatorGetAssertionResponse::CreateFromU2fSignResponse(
- *application_parameter_used, std::move(response_data), *it);
- if (!sign_response) {
- std::move(completion_callback_)
- .Run(FidoReturnCode::kAuthenticatorResponseInvalid,
- base::nullopt);
- } else {
- std::move(completion_callback_)
- .Run(FidoReturnCode::kSuccess, std::move(sign_response));
- }
- }
- break;
- }
- case apdu::ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED: {
- // Key handle is accepted by this device, but waiting on user touch. Move
- // on and try this device again later.
- state_ = State::IDLE;
- Transition();
- break;
- }
- case apdu::ApduResponse::Status::SW_WRONG_DATA:
- case apdu::ApduResponse::Status::SW_WRONG_LENGTH: {
- if (application_parameter_type == ApplicationParameterType::kPrimary &&
- alt_application_parameter_ && it != registered_keys_.cend()) {
- // |application_parameter_| failed, but there is also
- // |alt_application_parameter_| to try.
- InitiateDeviceTransaction(
- GetU2fSignApduCommand(*alt_application_parameter_, *it),
- base::Bind(&U2fSign::OnTryDevice, weak_factory_.GetWeakPtr(), it,
- ApplicationParameterType::kAlternative));
- } else if (it == registered_keys_.cend()) {
- // The fake enrollment errored out. Move on to the next device.
- AbandonCurrentDeviceAndTransition();
- } else if (++it != registered_keys_.end()) {
- // Key is not for this device. Try signing with the next key.
- InitiateDeviceTransaction(
- GetU2fSignApduCommand(application_parameter_, *it),
- base::BindOnce(&U2fSign::OnTryDevice, weak_factory_.GetWeakPtr(),
- it, ApplicationParameterType::kPrimary));
- } else {
- // No provided key was accepted by this device. Send registration
- // (Fake enroll) request to device.
- // We do this to prevent user confusion. Otherwise, if the device
- // doesn't blink, the user might think it's broken rather than that
- // it's not registered. Once the user consents to use the device,
- // the relying party can inform them that it hasn't been registered.
- InitiateDeviceTransaction(
- ConstructBogusU2fRegistrationCommand(),
- base::BindOnce(&U2fSign::OnTryDevice, weak_factory_.GetWeakPtr(),
- registered_keys_.cend(),
- ApplicationParameterType::kPrimary));
- }
- break;
- }
- default:
- // Some sort of failure occured. Abandon this device and move on.
- AbandonCurrentDeviceAndTransition();
- break;
- }
-}
-
-} // namespace device
diff --git a/chromium/device/fido/u2f_sign.h b/chromium/device/fido/u2f_sign.h
deleted file mode 100644
index 10cad3e122b..00000000000
--- a/chromium/device/fido/u2f_sign.h
+++ /dev/null
@@ -1,67 +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_FIDO_U2F_SIGN_H_
-#define DEVICE_FIDO_U2F_SIGN_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/component_export.h"
-#include "base/containers/flat_set.h"
-#include "base/macros.h"
-#include "base/optional.h"
-#include "device/fido/authenticator_get_assertion_response.h"
-#include "device/fido/fido_constants.h"
-#include "device/fido/fido_transport_protocol.h"
-#include "device/fido/u2f_request.h"
-
-namespace service_manager {
-class Connector;
-}
-
-namespace device {
-
-class COMPONENT_EXPORT(DEVICE_FIDO) U2fSign : public U2fRequest {
- public:
- using SignResponseCallback = base::OnceCallback<void(
- FidoReturnCode status_code,
- base::Optional<AuthenticatorGetAssertionResponse> response_data)>;
-
- static std::unique_ptr<U2fRequest> TrySign(
- service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& transports,
- std::vector<std::vector<uint8_t>> registered_keys,
- std::vector<uint8_t> challenge_digest,
- std::vector<uint8_t> application_parameter,
- base::Optional<std::vector<uint8_t>> alt_application_parameter,
- SignResponseCallback completion_callback);
-
- U2fSign(service_manager::Connector* connector,
- const base::flat_set<FidoTransportProtocol>& transports,
- std::vector<std::vector<uint8_t>> registered_keys,
- std::vector<uint8_t> challenge_digest,
- std::vector<uint8_t> application_parameter,
- base::Optional<std::vector<uint8_t>> alt_application_parameter,
- SignResponseCallback completion_callback);
- ~U2fSign() override;
-
- private:
- void TryDevice() override;
- void OnTryDevice(std::vector<std::vector<uint8_t>>::const_iterator it,
- ApplicationParameterType application_parameter_type,
- base::Optional<std::vector<uint8_t>> response);
-
- base::Optional<std::vector<uint8_t>> alt_application_parameter_;
- SignResponseCallback completion_callback_;
-
- base::WeakPtrFactory<U2fSign> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(U2fSign);
-};
-
-} // namespace device
-
-#endif // DEVICE_FIDO_U2F_SIGN_H_
diff --git a/chromium/device/fido/u2f_sign_operation.cc b/chromium/device/fido/u2f_sign_operation.cc
new file mode 100644
index 00000000000..9ed577de836
--- /dev/null
+++ b/chromium/device/fido/u2f_sign_operation.cc
@@ -0,0 +1,190 @@
+// 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/u2f_sign_operation.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "components/apdu/apdu_response.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/fido_device.h"
+#include "device/fido/fido_parsing_utils.h"
+#include "device/fido/u2f_command_constructor.h"
+
+namespace device {
+
+U2fSignOperation::U2fSignOperation(FidoDevice* device,
+ const CtapGetAssertionRequest& request,
+ DeviceResponseCallback callback)
+ : DeviceOperation(device, request, std::move(callback)),
+ weak_factory_(this) {}
+
+U2fSignOperation::~U2fSignOperation() = default;
+
+void U2fSignOperation::Start() {
+ const auto& allow_list = request().allow_list();
+ if (allow_list && !allow_list->empty()) {
+ const auto it = allow_list->cbegin();
+ DispatchDeviceRequest(
+ ConvertToU2fSignCommand(request(), ApplicationParameterType::kPrimary,
+ it->id(), true /* is_check_only */),
+ base::BindOnce(&U2fSignOperation::OnCheckForKeyHandlePresence,
+ weak_factory_.GetWeakPtr(),
+ ApplicationParameterType::kPrimary, it));
+ } else {
+ // In order to make U2F authenticators blink on sign request with an empty
+ // allow list, we send fake enrollment to the device and error out if the
+ // user has provided user presence.
+ SendFakeEnrollment();
+ }
+}
+
+void U2fSignOperation::SendFakeEnrollment() {
+ DispatchDeviceRequest(
+ ConstructBogusU2fRegistrationCommand(),
+ base::BindOnce(&U2fSignOperation::OnSignResponseReceived,
+ weak_factory_.GetWeakPtr(), true /* is_fake_enrollment */,
+ ApplicationParameterType::kPrimary,
+ std::vector<uint8_t>()));
+}
+
+void U2fSignOperation::RetrySign(
+ bool is_fake_enrollment,
+ ApplicationParameterType application_parameter_type,
+ const std::vector<uint8_t>& key_handle) {
+ auto cmd = is_fake_enrollment
+ ? ConstructBogusU2fRegistrationCommand()
+ : ConvertToU2fSignCommand(
+ request(), application_parameter_type, key_handle);
+ DispatchDeviceRequest(
+ std::move(cmd),
+ base::BindOnce(&U2fSignOperation::OnSignResponseReceived,
+ weak_factory_.GetWeakPtr(), is_fake_enrollment,
+ application_parameter_type, key_handle));
+}
+
+void U2fSignOperation::OnSignResponseReceived(
+ bool is_fake_enrollment,
+ ApplicationParameterType application_parameter_type,
+ const std::vector<uint8_t>& key_handle,
+ base::Optional<std::vector<uint8_t>> device_response) {
+ const auto apdu_response =
+ device_response
+ ? apdu::ApduResponse::CreateFromMessage(std::move(*device_response))
+ : base::nullopt;
+ auto return_code = apdu_response ? apdu_response->status()
+ : apdu::ApduResponse::Status::SW_WRONG_DATA;
+
+ switch (return_code) {
+ case apdu::ApduResponse::Status::SW_NO_ERROR: {
+ if (is_fake_enrollment) {
+ std::move(callback())
+ .Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials, base::nullopt);
+ } else {
+ auto application_parameter =
+ application_parameter_type == ApplicationParameterType::kPrimary
+ ? fido_parsing_utils::CreateSHA256Hash(request().rp_id())
+ : request().alternative_application_parameter().value_or(
+ std::array<uint8_t, kRpIdHashLength>());
+ auto sign_response =
+ AuthenticatorGetAssertionResponse::CreateFromU2fSignResponse(
+ std::move(application_parameter), apdu_response->data(),
+ key_handle);
+ if (!sign_response) {
+ std::move(callback())
+ .Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
+ return;
+ }
+
+ std::move(callback())
+ .Run(CtapDeviceResponseCode::kSuccess, std::move(sign_response));
+ }
+ break;
+ }
+
+ case apdu::ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED: {
+ // Waiting for user touch. Retry after 200 milliseconds delay.
+ base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&U2fSignOperation::RetrySign,
+ weak_factory_.GetWeakPtr(), is_fake_enrollment,
+ application_parameter_type, key_handle),
+ kU2fRetryDelay);
+ break;
+ }
+ default:
+ // Some sort of failure occurred. Abandon this device and move on.
+ std::move(callback())
+ .Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
+ return;
+ }
+}
+
+void U2fSignOperation::OnCheckForKeyHandlePresence(
+ ApplicationParameterType application_parameter_type,
+ AllowedListIterator it,
+ base::Optional<std::vector<uint8_t>> device_response) {
+ DCHECK(request().allow_list());
+ const auto& apdu_response =
+ device_response
+ ? apdu::ApduResponse::CreateFromMessage(std::move(*device_response))
+ : base::nullopt;
+ auto return_code = apdu_response ? apdu_response->status()
+ : apdu::ApduResponse::Status::SW_WRONG_DATA;
+
+ switch (return_code) {
+ case apdu::ApduResponse::Status::SW_NO_ERROR:
+ case apdu::ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED: {
+ DispatchDeviceRequest(
+ ConvertToU2fSignCommand(request(), application_parameter_type,
+ it->id()),
+ base::BindOnce(&U2fSignOperation::OnSignResponseReceived,
+ weak_factory_.GetWeakPtr(),
+ false /* is_fake_enrollment */,
+ application_parameter_type, it->id()));
+ break;
+ }
+ case apdu::ApduResponse::Status::SW_WRONG_DATA:
+ case apdu::ApduResponse::Status::SW_WRONG_LENGTH: {
+ if (application_parameter_type == ApplicationParameterType::kPrimary &&
+ request().alternative_application_parameter()) {
+ // |application_parameter_| failed, but there is also
+ // |alternative_application_parameter_| to try.
+ DispatchDeviceRequest(
+ ConvertToU2fSignCommand(request(),
+ ApplicationParameterType::kAlternative,
+ it->id(), true /* is_check_only */),
+ base::BindOnce(&U2fSignOperation::OnCheckForKeyHandlePresence,
+ weak_factory_.GetWeakPtr(),
+ ApplicationParameterType::kAlternative, it));
+ } else if (++it != request().allow_list()->cend()) {
+ // Key is not for this device. Try signing with the next key.
+ DispatchDeviceRequest(
+ ConvertToU2fSignCommand(request(),
+ ApplicationParameterType::kPrimary,
+ it->id(), true /* check_only */),
+ base::BindOnce(&U2fSignOperation::OnCheckForKeyHandlePresence,
+ weak_factory_.GetWeakPtr(),
+ ApplicationParameterType::kPrimary, it));
+ } else {
+ // No provided key was accepted by this device. Send registration
+ // (Fake enroll) request to device.
+ SendFakeEnrollment();
+ }
+
+ break;
+ }
+ default:
+ // Some sort of failure occurred. Abandon this device and move on.
+ std::move(callback())
+ .Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
+ break;
+ }
+}
+
+} // namespace device
diff --git a/chromium/device/fido/u2f_sign_operation.h b/chromium/device/fido/u2f_sign_operation.h
new file mode 100644
index 00000000000..f303ae48f77
--- /dev/null
+++ b/chromium/device/fido/u2f_sign_operation.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_U2F_SIGN_OPERATION_H_
+#define DEVICE_FIDO_U2F_SIGN_OPERATION_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/component_export.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "device/fido/device_operation.h"
+#include "device/fido/fido_constants.h"
+
+namespace device {
+
+class FidoDevice;
+class CtapGetAssertionRequest;
+class AuthenticatorGetAssertionResponse;
+class PublicKeyCredentialDescriptor;
+
+// Represents per device authentication logic for U2F tokens. Handles iterating
+// through credentials in the allowed list, invokes check-only sign, and if
+// check-only sign returns success, invokes regular sign call with user
+// presence enforced.
+// https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html#using-the-ctap2-authenticatorgetassertion-command-with-ctap1-u2f-authenticators
+class COMPONENT_EXPORT(DEVICE_FIDO) U2fSignOperation
+ : public DeviceOperation<CtapGetAssertionRequest,
+ AuthenticatorGetAssertionResponse> {
+ public:
+ U2fSignOperation(FidoDevice* device,
+ const CtapGetAssertionRequest& request,
+ DeviceResponseCallback callback);
+ ~U2fSignOperation() override;
+
+ // DeviceOperation:
+ void Start() override;
+
+ private:
+ using AllowedListIterator =
+ std::vector<PublicKeyCredentialDescriptor>::const_iterator;
+
+ void SendFakeEnrollment();
+ void RetrySign(bool is_fake_enrollment,
+ ApplicationParameterType application_parameter_type,
+ const std::vector<uint8_t>& key_handle);
+ void OnSignResponseReceived(
+ bool is_fake_enrollment,
+ ApplicationParameterType application_parameter_type,
+ const std::vector<uint8_t>& key_handle,
+ base::Optional<std::vector<uint8_t>> device_response);
+ void OnCheckForKeyHandlePresence(
+ ApplicationParameterType application_parameter_type,
+ AllowedListIterator it,
+ base::Optional<std::vector<uint8_t>> device_response);
+
+ base::WeakPtrFactory<U2fSignOperation> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(U2fSignOperation);
+};
+
+} // namespace device
+
+#endif // DEVICE_FIDO_U2F_SIGN_OPERATION_H_
diff --git a/chromium/device/fido/u2f_sign_operation_unittest.cc b/chromium/device/fido/u2f_sign_operation_unittest.cc
new file mode 100644
index 00000000000..3c35a0d1f20
--- /dev/null
+++ b/chromium/device/fido/u2f_sign_operation_unittest.cc
@@ -0,0 +1,385 @@
+// 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/u2f_sign_operation.h"
+
+#include <string>
+#include <utility>
+
+#include "base/test/scoped_task_environment.h"
+#include "crypto/ec_private_key.h"
+#include "device/fido/authenticator_get_assertion_response.h"
+#include "device/fido/ctap_get_assertion_request.h"
+#include "device/fido/fido_constants.h"
+#include "device/fido/fido_parsing_utils.h"
+#include "device/fido/fido_test_data.h"
+#include "device/fido/mock_fido_device.h"
+#include "device/fido/test_callback_receiver.h"
+#include "device/fido/virtual_u2f_device.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device {
+
+using ::testing::_;
+using ::testing::InSequence;
+
+namespace {
+
+using TestSignCallback = ::device::test::StatusAndValueCallbackReceiver<
+ CtapDeviceResponseCode,
+ base::Optional<AuthenticatorGetAssertionResponse>>;
+
+} // namespace
+
+class U2fSignOperationTest : public ::testing::Test {
+ public:
+ CtapGetAssertionRequest CreateSignRequest(
+ std::vector<std::vector<uint8_t>> key_handles) {
+ CtapGetAssertionRequest request(test_data::kRelyingPartyId,
+ test_data::kClientDataHash);
+
+ std::vector<PublicKeyCredentialDescriptor> allowed_list;
+ for (auto& key_handle : key_handles) {
+ allowed_list.emplace_back(CredentialType::kPublicKey,
+ std::move(key_handle));
+ }
+
+ request.SetAllowList(std::move(allowed_list));
+ return request;
+ }
+
+ TestSignCallback& sign_callback_receiver() { return sign_callback_receiver_; }
+
+ protected:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ TestSignCallback sign_callback_receiver_;
+};
+
+TEST_F(U2fSignOperationTest, SignSuccess) {
+ auto request = CreateSignRequest(
+ {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
+
+ auto device = std::make_unique<MockFidoDevice>();
+ EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device"));
+ InSequence s;
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fCheckOnlySignCommandApdu,
+ test_data::kApduEncodedNoErrorSignResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fSignCommandApdu,
+ test_data::kApduEncodedNoErrorSignResponse);
+
+ auto u2f_sign = std::make_unique<U2fSignOperation>(
+ device.get(), std::move(request), sign_callback_receiver().callback());
+ u2f_sign->Start();
+
+ sign_callback_receiver().WaitForCallback();
+ EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
+ sign_callback_receiver().status());
+ EXPECT_THAT(sign_callback_receiver().value()->signature(),
+ ::testing::ElementsAreArray(test_data::kU2fSignature));
+ EXPECT_THAT(sign_callback_receiver().value()->raw_credential_id(),
+ ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
+}
+
+TEST_F(U2fSignOperationTest, SignSuccessWithFakeDevice) {
+ auto private_key = crypto::ECPrivateKey::Create();
+ std::string public_key;
+ private_key->ExportRawPublicKey(&public_key);
+ auto hash = fido_parsing_utils::CreateSHA256Hash(public_key);
+ std::vector<uint8_t> key_handle(hash.begin(), hash.end());
+ auto request = CreateSignRequest({key_handle});
+
+ auto device = std::make_unique<VirtualU2fDevice>();
+ device->mutable_state()->registrations.emplace(
+ key_handle, VirtualFidoDevice::RegistrationData(
+ std::move(private_key), test_data::kApplicationParameter,
+ 42 /* counter */));
+
+ auto u2f_sign = std::make_unique<U2fSignOperation>(
+ device.get(), std::move(request), sign_callback_receiver().callback());
+ u2f_sign->Start();
+
+ sign_callback_receiver().WaitForCallback();
+ EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
+ sign_callback_receiver().status());
+
+ // Just a sanity check, we don't verify the actual signature.
+ ASSERT_GE(32u + 1u + 4u + 8u, // Minimal ECDSA signature is 8 bytes
+ sign_callback_receiver()
+ .value()
+ ->auth_data()
+ .SerializeToByteArray()
+ .size());
+ EXPECT_EQ(0x01,
+ sign_callback_receiver()
+ .value()
+ ->auth_data()
+ .SerializeToByteArray()[32]); // UP flag
+ // Counter is incremented for every sign request. Since we conduct 2 sign
+ // requests (check only sign followed by a regular sign), counter should be
+ // incremented twice.
+ EXPECT_EQ(44, sign_callback_receiver()
+ .value()
+ ->auth_data()
+ .SerializeToByteArray()[36]); // counter
+}
+
+TEST_F(U2fSignOperationTest, DelayedSuccess) {
+ auto request = CreateSignRequest(
+ {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
+
+ // Simulates a device that times out waiting for user touch once before
+ // responding successfully.
+ auto device = std::make_unique<MockFidoDevice>();
+ EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
+
+ InSequence s;
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fCheckOnlySignCommandApdu,
+ test_data::kApduEncodedNoErrorSignResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fSignCommandApdu,
+ test_data::kU2fConditionNotSatisfiedApduResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fSignCommandApdu,
+ test_data::kApduEncodedNoErrorSignResponse);
+
+ auto u2f_sign = std::make_unique<U2fSignOperation>(
+ device.get(), std::move(request), sign_callback_receiver().callback());
+ u2f_sign->Start();
+
+ sign_callback_receiver().WaitForCallback();
+ EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
+ sign_callback_receiver().status());
+ EXPECT_THAT(sign_callback_receiver().value()->signature(),
+ ::testing::ElementsAreArray(test_data::kU2fSignature));
+ EXPECT_THAT(sign_callback_receiver().value()->raw_credential_id(),
+ ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
+}
+
+TEST_F(U2fSignOperationTest, MultipleHandles) {
+ // Two wrong keys followed by a correct key ensuring the wrong keys will be
+ // tested first.
+ auto request = CreateSignRequest(
+ {fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha),
+ fido_parsing_utils::Materialize(test_data::kKeyHandleBeta),
+ fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
+
+ auto device = std::make_unique<MockFidoDevice>();
+ EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
+ InSequence s;
+ // Wrong key would respond with SW_WRONG_DATA.
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fCheckOnlySignCommandApduWithKeyAlpha,
+ test_data::kU2fWrongDataApduResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fCheckOnlySignCommandApduWithKeyBeta,
+ test_data::kU2fWrongDataApduResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fCheckOnlySignCommandApdu,
+ test_data::kApduEncodedNoErrorSignResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fSignCommandApdu,
+ test_data::kApduEncodedNoErrorSignResponse);
+
+ auto u2f_sign = std::make_unique<U2fSignOperation>(
+ device.get(), std::move(request), sign_callback_receiver().callback());
+ u2f_sign->Start();
+
+ sign_callback_receiver().WaitForCallback();
+ EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
+ sign_callback_receiver().status());
+ EXPECT_THAT(sign_callback_receiver().value()->signature(),
+ ::testing::ElementsAreArray(test_data::kU2fSignature));
+ EXPECT_THAT(sign_callback_receiver().value()->raw_credential_id(),
+ ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
+}
+
+// Test that Fake U2F registration is invoked when no credentials in the allowed
+// list are recognized by the device.
+TEST_F(U2fSignOperationTest, FakeEnroll) {
+ auto request = CreateSignRequest(
+ {fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha),
+ fido_parsing_utils::Materialize(test_data::kKeyHandleBeta)});
+
+ auto device = std::make_unique<MockFidoDevice>();
+ InSequence s;
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fCheckOnlySignCommandApduWithKeyAlpha,
+ test_data::kU2fWrongDataApduResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fCheckOnlySignCommandApduWithKeyBeta,
+ test_data::kU2fWrongDataApduResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fFakeRegisterCommand,
+ test_data::kApduEncodedNoErrorRegisterResponse);
+
+ auto u2f_sign = std::make_unique<U2fSignOperation>(
+ device.get(), std::move(request), sign_callback_receiver().callback());
+ u2f_sign->Start();
+ sign_callback_receiver().WaitForCallback();
+
+ EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrNoCredentials,
+ sign_callback_receiver().status());
+ EXPECT_FALSE(sign_callback_receiver().value());
+}
+
+// Tests that U2F fake enrollment should be re-tried repeatedly if no
+// credentials are valid for the authenticator and user presence is not
+// obtained.
+TEST_F(U2fSignOperationTest, DelayedFakeEnrollment) {
+ auto request = CreateSignRequest(
+ {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
+
+ // Simulates a device that times out waiting for user presence during fake
+ // enrollment.
+ auto device = std::make_unique<MockFidoDevice>();
+ EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device0"));
+ InSequence s;
+ device->ExpectRequestAndRespondWith(test_data::kU2fCheckOnlySignCommandApdu,
+ test_data::kU2fWrongDataApduResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fFakeRegisterCommand,
+ test_data::kU2fConditionNotSatisfiedApduResponse);
+ device->ExpectRequestAndRespondWith(
+ test_data::kU2fFakeRegisterCommand,
+ test_data::kApduEncodedNoErrorRegisterResponse);
+
+ auto u2f_sign = std::make_unique<U2fSignOperation>(
+ device.get(), std::move(request), sign_callback_receiver().callback());
+ u2f_sign->Start();
+ sign_callback_receiver().WaitForCallback();
+
+ EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrNoCredentials,
+ sign_callback_receiver().status());
+ EXPECT_FALSE(sign_callback_receiver().value());
+}
+
+// Tests that request is dropped gracefully if device returns error on all
+// requests (including fake enrollment).
+TEST_F(U2fSignOperationTest, FakeEnrollErroringOut) {
+ auto request = CreateSignRequest(
+ {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
+
+ // Simulates a device that errors out on all requests (including the sign
+ // request and fake registration attempt). The device should then be abandoned
+ // to prevent the test from crashing or timing out.
+ auto device = std::make_unique<MockFidoDevice>();
+ EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device0"));
+ InSequence s;
+ device->ExpectRequestAndRespondWith(test_data::kU2fCheckOnlySignCommandApdu,
+ test_data::kU2fWrongDataApduResponse);
+ device->ExpectRequestAndRespondWith(test_data::kU2fFakeRegisterCommand,
+ test_data::kU2fWrongDataApduResponse);
+
+ auto u2f_sign = std::make_unique<U2fSignOperation>(
+ device.get(), std::move(request), sign_callback_receiver().callback());
+ u2f_sign->Start();
+ sign_callback_receiver().WaitForCallback();
+
+ EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
+ sign_callback_receiver().status());
+ EXPECT_FALSE(sign_callback_receiver().value());
+}
+
+// Tests the scenario where device returns success response, but the response is
+// unparse-able.
+TEST_F(U2fSignOperationTest, SignWithCorruptedResponse) {
+ auto request = CreateSignRequest(
+ {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
+
+ auto device = std::make_unique<MockFidoDevice>();
+ EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
+ InSequence s;
+ device->ExpectRequestAndRespondWith(test_data::kU2fCheckOnlySignCommandApdu,
+ test_data::kTestCorruptedU2fSignResponse);
+ device->ExpectRequestAndRespondWith(test_data::kU2fSignCommandApdu,
+ test_data::kTestCorruptedU2fSignResponse);
+
+ auto u2f_sign = std::make_unique<U2fSignOperation>(
+ device.get(), std::move(request), sign_callback_receiver().callback());
+ u2f_sign->Start();
+ sign_callback_receiver().WaitForCallback();
+
+ EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
+ sign_callback_receiver().status());
+ EXPECT_FALSE(sign_callback_receiver().value());
+}
+
+TEST_F(U2fSignOperationTest, AlternativeApplicationParameter) {
+ auto request = CreateSignRequest(
+ {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
+ request.SetAlternativeApplicationParameter(
+ test_data::kAlternativeApplicationParameter);
+
+ auto device = std::make_unique<MockFidoDevice>();
+ EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
+ InSequence s;
+ // The first 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 u2f_sign = std::make_unique<U2fSignOperation>(
+ device.get(), std::move(request), sign_callback_receiver().callback());
+ u2f_sign->Start();
+ sign_callback_receiver().WaitForCallback();
+
+ EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
+ sign_callback_receiver().status());
+ const auto& response_value = sign_callback_receiver().value();
+ EXPECT_THAT(response_value->signature(),
+ ::testing::ElementsAreArray(test_data::kU2fSignature));
+ EXPECT_THAT(response_value->raw_credential_id(),
+ ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
+ EXPECT_THAT(
+ response_value->GetRpIdHash(),
+ ::testing::ElementsAreArray(test_data::kAlternativeApplicationParameter));
+}
+
+// This is a regression test in response to https://crbug.com/833398.
+TEST_F(U2fSignOperationTest, AlternativeApplicationParameterRejection) {
+ auto request = CreateSignRequest(
+ {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
+ request.SetAlternativeApplicationParameter(
+ test_data::kAlternativeApplicationParameter);
+
+ auto device = std::make_unique<MockFidoDevice>();
+ EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
+ InSequence s;
+ // The first request will use the primary app_param, which will be rejected.
+ device->ExpectRequestAndRespondWith(test_data::kU2fCheckOnlySignCommandApdu,
+ test_data::kU2fWrongDataApduResponse);
+ // After the rejection, request with alternative application parameter should
+ // be tried, which will also be rejected.
+ device->ExpectRequestAndRespondWith(
+ test_data::
+ kU2fCheckOnlySignCommandApduWithAlternativeApplicationParameter,
+ test_data::kU2fWrongDataApduResponse);
+ // The second rejection will trigger a bogus register command. This will be
+ // rejected as well, triggering the device to be abandoned.
+ device->ExpectRequestAndRespondWith(test_data::kU2fFakeRegisterCommand,
+ test_data::kU2fWrongDataApduResponse);
+
+ auto u2f_sign = std::make_unique<U2fSignOperation>(
+ device.get(), std::move(request), sign_callback_receiver().callback());
+ u2f_sign->Start();
+ sign_callback_receiver().WaitForCallback();
+
+ EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
+ sign_callback_receiver().status());
+ EXPECT_FALSE(sign_callback_receiver().value());
+}
+
+} // namespace device
diff --git a/chromium/device/fido/u2f_sign_unittest.cc b/chromium/device/fido/u2f_sign_unittest.cc
deleted file mode 100644
index 23b972ddcc0..00000000000
--- a/chromium/device/fido/u2f_sign_unittest.cc
+++ /dev/null
@@ -1,490 +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/u2f_sign.h"
-
-#include <utility>
-
-#include "base/run_loop.h"
-#include "base/test/scoped_task_environment.h"
-#include "crypto/ec_private_key.h"
-#include "device/fido/authenticator_get_assertion_response.h"
-#include "device/fido/fake_fido_discovery.h"
-#include "device/fido/fido_constants.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/mock_fido_device.h"
-#include "device/fido/test_callback_receiver.h"
-#include "device/fido/virtual_u2f_device.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ::testing::_;
-
-namespace device {
-
-namespace {
-
-std::vector<uint8_t> GetTestCredentialRawIdBytes() {
- return fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle);
-}
-
-std::vector<uint8_t> GetU2fSignCommandWithCorrectCredential() {
- return fido_parsing_utils::Materialize(test_data::kU2fSignCommandApdu);
-}
-
-using TestSignCallback = ::device::test::StatusAndValueCallbackReceiver<
- FidoReturnCode,
- base::Optional<AuthenticatorGetAssertionResponse>>;
-
-} // namespace
-
-class U2fSignTest : public ::testing::Test {
- public:
- U2fSignTest()
- : scoped_task_environment_(
- base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
-
- void ForgeNextHidDiscovery() {
- discovery_ = scoped_fake_discovery_factory_.ForgeNextHidDiscovery();
- }
-
- std::unique_ptr<U2fSign> CreateSignRequest() {
- return CreateSignRequestWithKeys({GetTestCredentialRawIdBytes()});
- }
-
- std::unique_ptr<U2fSign> CreateSignRequestWithKeys(
- std::vector<std::vector<uint8_t>> registered_keys) {
- ForgeNextHidDiscovery();
- return std::make_unique<U2fSign>(
- nullptr /* connector */,
- base::flat_set<FidoTransportProtocol>(
- {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
- std::move(registered_keys),
- fido_parsing_utils::Materialize(test_data::kChallengeParameter),
- fido_parsing_utils::Materialize(test_data::kApplicationParameter),
- base::nullopt /* alt_application_parameter*/,
- sign_callback_receiver_.callback());
- }
-
- test::FakeFidoDiscovery* discovery() const { return discovery_; }
- TestSignCallback& sign_callback_receiver() { return sign_callback_receiver_; }
-
- protected:
- base::test::ScopedTaskEnvironment scoped_task_environment_;
- test::ScopedFakeFidoDiscoveryFactory scoped_fake_discovery_factory_;
- test::FakeFidoDiscovery* discovery_;
- TestSignCallback sign_callback_receiver_;
- base::flat_set<FidoTransportProtocol> protocols_;
-};
-
-TEST_F(U2fSignTest, TestSignSuccess) {
- auto request = CreateSignRequest();
- request->Start();
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device"));
- EXPECT_CALL(*device,
- DeviceTransactPtr(GetU2fSignCommandWithCorrectCredential(), _))
- .WillOnce(testing::Invoke(MockFidoDevice::NoErrorSign));
- EXPECT_CALL(*device, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device));
-
- sign_callback_receiver().WaitForCallback();
- EXPECT_EQ(FidoReturnCode::kSuccess, sign_callback_receiver().status());
-
- // Correct key was sent so a sign response is expected.
- EXPECT_THAT(sign_callback_receiver().value()->signature(),
- ::testing::ElementsAreArray(test_data::kU2fSignature));
-
- // Verify that we get the key handle used for signing.
- EXPECT_THAT(GetTestCredentialRawIdBytes(),
- ::testing::ElementsAreArray(
- sign_callback_receiver().value()->raw_credential_id()));
-}
-
-TEST_F(U2fSignTest, TestSignSuccessWithFake) {
- auto private_key = crypto::ECPrivateKey::Create();
- std::string public_key;
- private_key->ExportRawPublicKey(&public_key);
-
- auto key_handle = fido_parsing_utils::CreateSHA256Hash(public_key);
- std::vector<std::vector<uint8_t>> handles{key_handle};
- auto request = CreateSignRequestWithKeys(handles);
- request->Start();
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- auto device = std::make_unique<VirtualU2fDevice>();
- device->mutable_state()->registrations.emplace(
- key_handle,
- VirtualFidoDevice::RegistrationData(
- std::move(private_key),
- fido_parsing_utils::Materialize(test_data::kApplicationParameter),
- 42));
- discovery()->AddDevice(std::move(device));
-
- sign_callback_receiver().WaitForCallback();
- EXPECT_EQ(FidoReturnCode::kSuccess, sign_callback_receiver().status());
-
- // Just a sanity check, we don't verify the actual signature.
- ASSERT_GE(32u + 1u + 4u + 8u, // Minimal ECDSA signature is 8 bytes
- sign_callback_receiver()
- .value()
- ->auth_data()
- .SerializeToByteArray()
- .size());
- EXPECT_EQ(0x01,
- sign_callback_receiver()
- .value()
- ->auth_data()
- .SerializeToByteArray()[32]); // UP flag
- EXPECT_EQ(43, sign_callback_receiver()
- .value()
- ->auth_data()
- .SerializeToByteArray()[36]); // counter
-}
-
-TEST_F(U2fSignTest, TestDelayedSuccess) {
- auto request = CreateSignRequest();
- request->Start();
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- // Go through the state machine twice before success.
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
- EXPECT_CALL(*device,
- DeviceTransactPtr(GetU2fSignCommandWithCorrectCredential(), _))
- .WillOnce(::testing::Invoke(MockFidoDevice::NotSatisfied))
- .WillOnce(::testing::Invoke(MockFidoDevice::NoErrorSign));
- EXPECT_CALL(*device, TryWinkRef(_))
- .Times(2)
- .WillRepeatedly(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device));
-
- sign_callback_receiver().WaitForCallback();
- EXPECT_EQ(FidoReturnCode::kSuccess, sign_callback_receiver().status());
-
- // Correct key was sent so a sign response is expected.
- EXPECT_THAT(sign_callback_receiver().value()->signature(),
- ::testing::ElementsAreArray(test_data::kU2fSignature));
-
- // Verify that we get the key handle used for signing.
- EXPECT_THAT(GetTestCredentialRawIdBytes(),
- ::testing::ElementsAreArray(
- sign_callback_receiver().value()->raw_credential_id()));
-}
-
-TEST_F(U2fSignTest, TestMultipleHandles) {
- // Two wrong keys followed by a correct key ensuring the wrong keys will be
- // tested first.
- const auto correct_key_handle = GetTestCredentialRawIdBytes();
- auto request = CreateSignRequestWithKeys(
- {fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha),
- fido_parsing_utils::Materialize(test_data::kKeyHandleBeta),
- correct_key_handle});
- request->Start();
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- auto device = std::make_unique<MockFidoDevice>();
- // Wrong key would respond with SW_WRONG_DATA.
- EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
- EXPECT_CALL(*device,
- DeviceTransactPtr(fido_parsing_utils::Materialize(
- test_data::kU2fSignCommandApduWithKeyAlpha),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
- EXPECT_CALL(*device,
- DeviceTransactPtr(fido_parsing_utils::Materialize(
- test_data::kU2fSignCommandApduWithKeyBeta),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- EXPECT_CALL(*device,
- DeviceTransactPtr(GetU2fSignCommandWithCorrectCredential(), _))
- .WillOnce(::testing::Invoke(MockFidoDevice::NoErrorSign));
-
- // Only one wink expected per device.
- EXPECT_CALL(*device, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device));
-
- sign_callback_receiver().WaitForCallback();
- EXPECT_EQ(FidoReturnCode::kSuccess, sign_callback_receiver().status());
-
- // Correct key was sent so a sign response is expected.
- EXPECT_THAT(sign_callback_receiver().value()->signature(),
- ::testing::ElementsAreArray(test_data::kU2fSignature));
- // Verify that we get the key handle used for signing.
- EXPECT_EQ(correct_key_handle,
- sign_callback_receiver().value()->raw_credential_id());
-}
-
-TEST_F(U2fSignTest, TestMultipleDevices) {
- const auto correct_key_handle = GetTestCredentialRawIdBytes();
- auto request = CreateSignRequestWithKeys(
- {GetTestCredentialRawIdBytes(),
- fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha)});
- request->Start();
-
- auto device0 = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device0, GetId()).WillRepeatedly(::testing::Return("device0"));
- EXPECT_CALL(*device0,
- DeviceTransactPtr(GetU2fSignCommandWithCorrectCredential(), _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
- EXPECT_CALL(*device0,
- DeviceTransactPtr(fido_parsing_utils::Materialize(
- test_data::kU2fSignCommandApduWithKeyAlpha),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::NotSatisfied));
- // One wink per device.
- EXPECT_CALL(*device0, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device0));
-
- // Second device will have a successful touch.
- auto device1 = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device1, GetId()).WillRepeatedly(::testing::Return("device1"));
- EXPECT_CALL(*device1,
- DeviceTransactPtr(GetU2fSignCommandWithCorrectCredential(), _))
- .WillOnce(::testing::Invoke(MockFidoDevice::NoErrorSign));
-
- // One wink per device.
- EXPECT_CALL(*device1, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device1));
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- sign_callback_receiver().WaitForCallback();
- EXPECT_EQ(FidoReturnCode::kSuccess, sign_callback_receiver().status());
-
- // Correct key was sent so a sign response is expected.
- EXPECT_THAT(sign_callback_receiver().value()->signature(),
- ::testing::ElementsAreArray(test_data::kU2fSignature));
-
- // Verify that we get the key handle used for signing.
- EXPECT_EQ(correct_key_handle,
- sign_callback_receiver().value()->raw_credential_id());
-}
-
-TEST_F(U2fSignTest, TestFakeEnroll) {
- auto request = CreateSignRequestWithKeys(
- {fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha),
- fido_parsing_utils::Materialize(test_data::kKeyHandleBeta)});
- request->Start();
-
- auto device0 = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device0,
- DeviceTransactPtr(fido_parsing_utils::Materialize(
- test_data::kU2fSignCommandApduWithKeyAlpha),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- EXPECT_CALL(*device0,
- DeviceTransactPtr(fido_parsing_utils::Materialize(
- test_data::kU2fSignCommandApduWithKeyBeta),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::NotSatisfied));
- // One wink per device.
- EXPECT_CALL(*device0, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device0));
-
- // Second device will be have a successful touch.
- auto device1 = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device1, GetId()).WillRepeatedly(::testing::Return("device1"));
- // Both keys will be tried, when both fail, register is tried on that device.
- EXPECT_CALL(*device1,
- DeviceTransactPtr(fido_parsing_utils::Materialize(
- test_data::kU2fSignCommandApduWithKeyAlpha),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
- EXPECT_CALL(*device1,
- DeviceTransactPtr(fido_parsing_utils::Materialize(
- test_data::kU2fSignCommandApduWithKeyBeta),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
- EXPECT_CALL(*device1,
- DeviceTransactPtr(fido_parsing_utils::Materialize(
- test_data::kU2fFakeRegisterCommand),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::NoErrorRegister));
-
- // One wink per device.
- EXPECT_CALL(*device1, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device1));
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- sign_callback_receiver().WaitForCallback();
- // Device that responded had no correct keys.
- EXPECT_EQ(FidoReturnCode::kUserConsentButCredentialNotRecognized,
- sign_callback_receiver().status());
- EXPECT_FALSE(sign_callback_receiver().value());
-}
-
-TEST_F(U2fSignTest, TestFakeEnrollErroringOut) {
- auto request = CreateSignRequest();
- request->Start();
- // First device errors out on all requests (including the sign request and
- // fake registration attempt). The device should then be abandoned to prevent
- // the test from crashing or timing out.
- auto device0 = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device0, GetId()).WillRepeatedly(::testing::Return("device0"));
- EXPECT_CALL(*device0,
- DeviceTransactPtr(GetU2fSignCommandWithCorrectCredential(), _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
- EXPECT_CALL(*device0,
- DeviceTransactPtr(fido_parsing_utils::Materialize(
- test_data::kU2fFakeRegisterCommand),
- _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
- // One wink per device.
- EXPECT_CALL(*device0, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device0));
-
- // Second device will have a successful touch and sign on the first attempt.
- auto device1 = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device1, GetId()).WillRepeatedly(::testing::Return("device1"));
- EXPECT_CALL(*device1,
- DeviceTransactPtr(GetU2fSignCommandWithCorrectCredential(), _))
- .WillOnce(::testing::Invoke(MockFidoDevice::NoErrorSign));
- // One wink per device.
- EXPECT_CALL(*device1, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device1));
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- // Correct key was sent so a sign response is expected.
- sign_callback_receiver().WaitForCallback();
- EXPECT_THAT(sign_callback_receiver().value()->signature(),
- ::testing::ElementsAreArray(test_data::kU2fSignature));
-}
-
-// Device returns success, but the response is unparse-able.
-TEST_F(U2fSignTest, TestSignWithCorruptedResponse) {
- auto request = CreateSignRequest();
- request->Start();
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
- EXPECT_CALL(*device,
- DeviceTransactPtr(GetU2fSignCommandWithCorrectCredential(), _))
- .WillOnce(::testing::Invoke(MockFidoDevice::SignWithCorruptedResponse));
- EXPECT_CALL(*device, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device));
-
- sign_callback_receiver().WaitForCallback();
- EXPECT_EQ(FidoReturnCode::kAuthenticatorResponseInvalid,
- sign_callback_receiver().status());
- EXPECT_FALSE(sign_callback_receiver().value());
-}
-
-MATCHER_P(WithApplicationParameter, expected, "") {
- // See
- // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html#request-message-framing
- // and
- // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html#authentication-request-message---u2f_authenticate
- constexpr size_t kAppParamOffset = 4 /* CLA + INS + P1 + P2 */ +
- 3 /* Extended Lc */ +
- 32 /* Challenge Parameter */;
- constexpr size_t kAppParamLength = 32;
- if (arg.size() < kAppParamOffset + kAppParamLength) {
- return false;
- }
-
- auto application_parameter =
- base::make_span(arg).subspan(kAppParamOffset, kAppParamLength);
-
- return std::equal(application_parameter.begin(), application_parameter.end(),
- expected.begin(), expected.end());
-}
-
-TEST_F(U2fSignTest, TestAlternativeApplicationParameter) {
- const std::vector<uint8_t> signing_key_handle(32, 0x0A);
- const std::vector<uint8_t> primary_app_param(32, 1);
- const std::vector<uint8_t> alt_app_param(32, 2);
-
- ForgeNextHidDiscovery();
- auto request = std::make_unique<U2fSign>(
- nullptr /* connector */,
- base::flat_set<FidoTransportProtocol>(
- {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
- std::vector<std::vector<uint8_t>>({signing_key_handle}),
- std::vector<uint8_t>(32), primary_app_param, alt_app_param,
- sign_callback_receiver_.callback());
- request->Start();
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
- // The first request will use the primary app_param, which will be rejected.
- EXPECT_CALL(*device,
- DeviceTransactPtr(WithApplicationParameter(primary_app_param), _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
- // After the rejection, the alternative should be tried.
- EXPECT_CALL(*device,
- DeviceTransactPtr(WithApplicationParameter(alt_app_param), _))
- .WillOnce(::testing::Invoke(MockFidoDevice::NoErrorSign));
- EXPECT_CALL(*device, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
- discovery()->AddDevice(std::move(device));
-
- sign_callback_receiver().WaitForCallback();
- EXPECT_EQ(FidoReturnCode::kSuccess, sign_callback_receiver().status());
-
- EXPECT_THAT(sign_callback_receiver().value()->signature(),
- ::testing::ElementsAreArray(test_data::kU2fSignature));
- EXPECT_EQ(signing_key_handle,
- sign_callback_receiver().value()->raw_credential_id());
-}
-
-// This is a regression test in response to https://crbug.com/833398.
-TEST_F(U2fSignTest, TestAlternativeApplicationParameterRejection) {
- const std::vector<uint8_t> signing_key_handle(32, 0x0A);
- const std::vector<uint8_t> primary_app_param(32, 1);
- const std::vector<uint8_t> alt_app_param(32, 2);
-
- ForgeNextHidDiscovery();
- auto request = std::make_unique<U2fSign>(
- nullptr /* connector */,
- base::flat_set<FidoTransportProtocol>(
- {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
- std::vector<std::vector<uint8_t>>({signing_key_handle}),
- std::vector<uint8_t>(32), primary_app_param, alt_app_param,
- sign_callback_receiver_.callback());
- request->Start();
- discovery()->WaitForCallToStartAndSimulateSuccess();
-
- auto device = std::make_unique<MockFidoDevice>();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
- EXPECT_CALL(*device, TryWinkRef(_))
- .WillOnce(::testing::Invoke(MockFidoDevice::WinkDoNothing));
-
- // The first request will use the primary app_param, which will be rejected.
- EXPECT_CALL(*device,
- DeviceTransactPtr(WithApplicationParameter(primary_app_param), _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- // After the rejection, the alternative should be tried, which will also be
- // rejected.
- EXPECT_CALL(*device,
- DeviceTransactPtr(WithApplicationParameter(alt_app_param), _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- // The second rejection will trigger a bogus register command. This will be
- // rejected as well, triggering the device to be abandoned.
- EXPECT_CALL(*device,
- DeviceTransactPtr(WithApplicationParameter(kBogusAppParam), _))
- .WillOnce(::testing::Invoke(MockFidoDevice::WrongData));
-
- discovery()->AddDevice(std::move(device));
-}
-
-} // namespace device
diff --git a/chromium/device/fido/virtual_ctap2_device.cc b/chromium/device/fido/virtual_ctap2_device.cc
new file mode 100644
index 00000000000..60d4579038c
--- /dev/null
+++ b/chromium/device/fido/virtual_ctap2_device.cc
@@ -0,0 +1,375 @@
+// 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/virtual_ctap2_device.h"
+
+#include <array>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/cbor/cbor_writer.h"
+#include "crypto/ec_private_key.h"
+#include "device/fido/authenticator_get_assertion_response.h"
+#include "device/fido/authenticator_make_credential_response.h"
+#include "device/fido/ctap_get_assertion_request.h"
+#include "device/fido/ctap_make_credential_request.h"
+#include "device/fido/ec_public_key.h"
+#include "device/fido/fido_constants.h"
+#include "device/fido/fido_parsing_utils.h"
+#include "device/fido/opaque_attestation_statement.h"
+
+namespace device {
+
+namespace {
+
+constexpr std::array<uint8_t, kAaguidLength> kDeviceAaguid = {
+ {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08}};
+
+std::vector<uint8_t> ConstructResponse(CtapDeviceResponseCode response_code,
+ base::span<const uint8_t> data) {
+ std::vector<uint8_t> response{base::strict_cast<uint8_t>(response_code)};
+ fido_parsing_utils::Append(&response, data);
+ return response;
+}
+
+void ReturnCtap2Response(
+ FidoDevice::DeviceCallback cb,
+ CtapDeviceResponseCode response_code,
+ base::Optional<base::span<const uint8_t>> data = base::nullopt) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(cb),
+ ConstructResponse(response_code,
+ data.value_or(std::vector<uint8_t>{}))));
+}
+
+bool AreMakeCredentialOptionsValid(const AuthenticatorSupportedOptions& options,
+ const CtapMakeCredentialRequest& request) {
+ if (request.resident_key_supported() && !options.supports_resident_key())
+ return false;
+
+ return !request.user_verification_required() ||
+ options.user_verification_availability() ==
+ AuthenticatorSupportedOptions::UserVerificationAvailability::
+ kSupportedAndConfigured;
+}
+
+bool AreGetAssertionOptionsValid(const AuthenticatorSupportedOptions& options,
+ const CtapGetAssertionRequest& request) {
+ if (request.user_presence_required() && !options.user_presence_required())
+ return false;
+
+ return request.user_verification() !=
+ UserVerificationRequirement::kRequired ||
+ options.user_verification_availability() ==
+ AuthenticatorSupportedOptions::UserVerificationAvailability::
+ kSupportedAndConfigured;
+}
+
+// Checks that whether the received MakeCredential request includes EA256
+// algorithm in publicKeyCredParam.
+bool AreMakeCredentialParamsValid(const CtapMakeCredentialRequest& request) {
+ const auto& params =
+ request.public_key_credential_params().public_key_credential_params();
+ return std::any_of(
+ params.begin(), params.end(), [](const auto& credential_info) {
+ return credential_info.algorithm ==
+ base::strict_cast<int>(CoseAlgorithmIdentifier::kCoseEs256);
+ });
+}
+
+std::unique_ptr<ECPublicKey> ConstructECPublicKey(
+ std::string public_key_string) {
+ DCHECK_EQ(64u, public_key_string.size());
+
+ const auto public_key_x_coordinate =
+ base::as_bytes(base::make_span(public_key_string)).first(32);
+ const auto public_key_y_coordinate =
+ base::as_bytes(base::make_span(public_key_string)).last(32);
+ return std::make_unique<ECPublicKey>(
+ fido_parsing_utils::kEs256,
+ fido_parsing_utils::Materialize(public_key_x_coordinate),
+ fido_parsing_utils::Materialize(public_key_y_coordinate));
+}
+
+std::vector<uint8_t> ConstructSignatureBuffer(
+ const AuthenticatorData& authenticator_data,
+ base::span<const uint8_t, kClientDataHashLength> client_data_hash) {
+ std::vector<uint8_t> signature_buffer;
+ fido_parsing_utils::Append(&signature_buffer,
+ authenticator_data.SerializeToByteArray());
+ fido_parsing_utils::Append(&signature_buffer, client_data_hash);
+ return signature_buffer;
+}
+
+std::vector<uint8_t> ConstructMakeCredentialResponse(
+ base::span<const 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));
+ AuthenticatorMakeCredentialResponse make_credential_response(
+ AttestationObject(
+ std::move(authenticator_data),
+ std::make_unique<OpaqueAttestationStatement>(
+ "packed", cbor::CBORValue(std::move(attestation_map)))));
+ return GetSerializedCtapDeviceResponse(make_credential_response);
+}
+
+std::vector<uint8_t> ConstructGetAssertionResponse(
+ AuthenticatorData authenticator_data,
+ base::span<const uint8_t> signature,
+ base::span<const uint8_t> key_handle) {
+ AuthenticatorGetAssertionResponse response(
+ std::move(authenticator_data),
+ fido_parsing_utils::Materialize(signature));
+
+ response.SetCredential({CredentialType::kPublicKey,
+ fido_parsing_utils::Materialize(key_handle)});
+ response.SetNumCredentials(1);
+ return GetSerializedCtapDeviceResponse(response);
+}
+
+} // namespace
+
+VirtualCtap2Device::VirtualCtap2Device()
+ : VirtualFidoDevice(),
+ device_info_(AuthenticatorGetInfoResponse({ProtocolVersion::kCtap},
+ kDeviceAaguid)),
+ weak_factory_(this) {}
+
+VirtualCtap2Device::VirtualCtap2Device(scoped_refptr<State> state)
+ : VirtualFidoDevice(std::move(state)),
+ device_info_(AuthenticatorGetInfoResponse({ProtocolVersion::kCtap},
+ kDeviceAaguid)),
+ weak_factory_(this) {}
+
+VirtualCtap2Device::~VirtualCtap2Device() = default;
+
+// As all operations for VirtualCtap2Device are synchronous and we do not wait
+// for user touch, Cancel command is no-op.
+void VirtualCtap2Device::Cancel() {}
+
+void VirtualCtap2Device::DeviceTransact(std::vector<uint8_t> command,
+ DeviceCallback cb) {
+ if (command.empty()) {
+ ReturnCtap2Response(std::move(cb), CtapDeviceResponseCode::kCtap2ErrOther);
+ return;
+ }
+
+ auto cmd_type = command[0];
+ const auto request_bytes = base::make_span(command).subspan(1);
+ CtapDeviceResponseCode response_code = CtapDeviceResponseCode::kCtap2ErrOther;
+ std::vector<uint8_t> response_data;
+
+ switch (static_cast<CtapRequestCommand>(cmd_type)) {
+ case CtapRequestCommand::kAuthenticatorGetInfo:
+ if (!request_bytes.empty()) {
+ ReturnCtap2Response(std::move(cb),
+ CtapDeviceResponseCode::kCtap2ErrOther);
+ return;
+ }
+
+ response_code = OnAuthenticatorGetInfo(&response_data);
+ break;
+ case CtapRequestCommand::kAuthenticatorMakeCredential:
+ response_code = OnMakeCredential(request_bytes, &response_data);
+ break;
+ case CtapRequestCommand::kAuthenticatorGetAssertion:
+ response_code = OnGetAssertion(request_bytes, &response_data);
+ break;
+ default:
+ break;
+ }
+
+ // Call |callback| via the |MessageLoop| because |AuthenticatorImpl| doesn't
+ // support callback hairpinning.
+ ReturnCtap2Response(std::move(cb), response_code, std::move(response_data));
+}
+
+base::WeakPtr<FidoDevice> VirtualCtap2Device::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
+void VirtualCtap2Device::SetAuthenticatorSupportedOptions(
+ AuthenticatorSupportedOptions options) {
+ device_info_.SetOptions(std::move(options));
+}
+
+CtapDeviceResponseCode VirtualCtap2Device::OnMakeCredential(
+ base::span<const uint8_t> request_bytes,
+ std::vector<uint8_t>* response) {
+ auto request = ParseCtapMakeCredentialRequest(request_bytes);
+ if (!request) {
+ DLOG(ERROR) << "Incorrectly formatted MakeCredential request.";
+ return CtapDeviceResponseCode::kCtap2ErrOther;
+ }
+
+ if (!AreMakeCredentialOptionsValid(device_info_.options(), *request) ||
+ !AreMakeCredentialParamsValid(*request)) {
+ DLOG(ERROR) << "Virtual CTAP2 device does not support options required by "
+ "the request.";
+ return CtapDeviceResponseCode::kCtap2ErrOther;
+ }
+
+ // Client pin is not supported.
+ if (request->pin_auth()) {
+ DLOG(ERROR) << "Virtual CTAP2 device does not support client pin.";
+ return CtapDeviceResponseCode::kCtap2ErrPinInvalid;
+ }
+
+ // Check for already registered credentials.
+ const auto rp_id_hash =
+ fido_parsing_utils::CreateSHA256Hash(request->rp().rp_id());
+ if (request->exclude_list()) {
+ for (const auto& excluded_credential : *request->exclude_list()) {
+ if (FindRegistrationData(excluded_credential.id(), rp_id_hash))
+ return CtapDeviceResponseCode::kCtap2ErrCredentialExcluded;
+ }
+ }
+
+ // Create key to register.
+ // Note: Non-deterministic, you need to mock this out if you rely on
+ // deterministic behavior.
+ auto private_key = crypto::ECPrivateKey::Create();
+ std::string public_key;
+ bool status = private_key->ExportRawPublicKey(&public_key);
+ DCHECK(status);
+
+ // Our key handles are simple hashes of the public key.
+ auto hash = fido_parsing_utils::CreateSHA256Hash(public_key);
+ std::vector<uint8_t> key_handle(hash.begin(), hash.end());
+ std::array<uint8_t, 2> sha256_length = {0, crypto::kSHA256Length};
+
+ AttestedCredentialData attested_credential_data(
+ kDeviceAaguid, sha256_length, key_handle,
+ ConstructECPublicKey(public_key));
+
+ auto authenticator_data = ConstructAuthenticatorData(
+ rp_id_hash, 01ul, std::move(attested_credential_data));
+ auto sign_buffer =
+ ConstructSignatureBuffer(authenticator_data, request->client_data_hash());
+
+ // Sign with attestation key.
+ // Note: Non-deterministic, you need to mock this out if you rely on
+ // deterministic behavior.
+ std::vector<uint8_t> sig;
+ std::unique_ptr<crypto::ECPrivateKey> attestation_private_key =
+ crypto::ECPrivateKey::CreateFromPrivateKeyInfo(GetAttestationKey());
+ 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;
+ }
+
+ *response = ConstructMakeCredentialResponse(std::move(*attestation_cert), sig,
+ std::move(authenticator_data));
+
+ StoreNewKey(rp_id_hash, key_handle, std::move(private_key));
+ return CtapDeviceResponseCode::kSuccess;
+}
+
+CtapDeviceResponseCode VirtualCtap2Device::OnGetAssertion(
+ base::span<const uint8_t> request_bytes,
+ std::vector<uint8_t>* response) {
+ auto request = ParseCtapGetAssertionRequest(request_bytes);
+ if (!request) {
+ DLOG(ERROR) << "Incorrectly formatted GetAssertion request.";
+ return CtapDeviceResponseCode::kCtap2ErrOther;
+ }
+
+ // Resident keys are not supported.
+ if (!request->allow_list() || request->allow_list()->empty()) {
+ DLOG(ERROR) << "Allowed credential list is empty, but Virtual CTAP2 device "
+ "does not support resident keys.";
+ return CtapDeviceResponseCode::kCtap2ErrNoCredentials;
+ }
+
+ // Client pin option is not supported.
+ if (request->pin_auth()) {
+ DLOG(ERROR) << "Virtual CTAP2 device does not support client pin.";
+ return CtapDeviceResponseCode::kCtap2ErrOther;
+ }
+
+ if (!AreGetAssertionOptionsValid(device_info_.options(), *request)) {
+ DLOG(ERROR) << "Unsupported options required from the request.";
+ return CtapDeviceResponseCode::kCtap2ErrOther;
+ }
+
+ const auto rp_id_hash =
+ fido_parsing_utils::CreateSHA256Hash(request->rp_id());
+
+ RegistrationData* found_data = nullptr;
+ base::span<const uint8_t> credential_id;
+ for (const auto& allowed_credential : *request->allow_list()) {
+ if ((found_data =
+ FindRegistrationData(allowed_credential.id(), rp_id_hash))) {
+ credential_id = allowed_credential.id();
+ break;
+ }
+ }
+
+ if (!found_data)
+ return CtapDeviceResponseCode::kCtap2ErrNoCredentials;
+
+ found_data->counter++;
+ auto authenticator_data =
+ ConstructAuthenticatorData(rp_id_hash, found_data->counter);
+ auto signature_buffer =
+ ConstructSignatureBuffer(authenticator_data, request->client_data_hash());
+
+ // Sign with the private key of the received key handle.
+ std::vector<uint8_t> sig;
+ auto* private_key = found_data->private_key.get();
+ bool status = Sign(private_key, std::move(signature_buffer), &sig);
+ DCHECK(status);
+
+ *response = ConstructGetAssertionResponse(std::move(authenticator_data),
+ std::move(sig), credential_id);
+ return CtapDeviceResponseCode::kSuccess;
+}
+
+CtapDeviceResponseCode VirtualCtap2Device::OnAuthenticatorGetInfo(
+ std::vector<uint8_t>* response) const {
+ *response = EncodeToCBOR(device_info_);
+ return CtapDeviceResponseCode::kSuccess;
+}
+
+AuthenticatorData VirtualCtap2Device::ConstructAuthenticatorData(
+ base::span<const uint8_t, kRpIdHashLength> rp_id_hash,
+ uint32_t current_signature_count,
+ base::Optional<AttestedCredentialData> attested_credential_data) {
+ uint8_t flag =
+ base::strict_cast<uint8_t>(AuthenticatorData::Flag::kTestOfUserPresence);
+ std::array<uint8_t, kSignCounterLength> signature_counter;
+
+ // Constructing AuthenticatorData for registration operation.
+ if (attested_credential_data)
+ flag |= base::strict_cast<uint8_t>(AuthenticatorData::Flag::kAttestation);
+
+ signature_counter[0] = (current_signature_count >> 24) & 0xff;
+ signature_counter[1] = (current_signature_count >> 16) & 0xff;
+ signature_counter[2] = (current_signature_count >> 8) & 0xff;
+ signature_counter[3] = (current_signature_count)&0xff;
+
+ return AuthenticatorData(rp_id_hash, flag, signature_counter,
+ std::move(attested_credential_data));
+}
+
+} // namespace device
diff --git a/chromium/device/fido/virtual_ctap2_device.h b/chromium/device/fido/virtual_ctap2_device.h
new file mode 100644
index 00000000000..fa84a9c0425
--- /dev/null
+++ b/chromium/device/fido/virtual_ctap2_device.h
@@ -0,0 +1,64 @@
+// 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_VIRTUAL_CTAP2_DEVICE_H_
+#define DEVICE_FIDO_VIRTUAL_CTAP2_DEVICE_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/component_export.h"
+#include "base/containers/span.h"
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/optional.h"
+#include "device/fido/attested_credential_data.h"
+#include "device/fido/authenticator_data.h"
+#include "device/fido/authenticator_supported_options.h"
+#include "device/fido/fido_constants.h"
+#include "device/fido/virtual_fido_device.h"
+
+namespace device {
+
+class COMPONENT_EXPORT(DEVICE_FIDO) VirtualCtap2Device
+ : public VirtualFidoDevice {
+ public:
+ VirtualCtap2Device();
+ explicit VirtualCtap2Device(scoped_refptr<State> state);
+ ~VirtualCtap2Device() override;
+
+ // FidoDevice:
+ void Cancel() override;
+ void DeviceTransact(std::vector<uint8_t> command, DeviceCallback cb) override;
+ base::WeakPtr<FidoDevice> GetWeakPtr() override;
+
+ void SetAuthenticatorSupportedOptions(AuthenticatorSupportedOptions options);
+
+ private:
+ CtapDeviceResponseCode OnMakeCredential(base::span<const uint8_t> request,
+ std::vector<uint8_t>* response);
+
+ CtapDeviceResponseCode OnGetAssertion(base::span<const uint8_t> request,
+ std::vector<uint8_t>* response);
+
+ CtapDeviceResponseCode OnAuthenticatorGetInfo(
+ std::vector<uint8_t>* response) const;
+
+ AuthenticatorData ConstructAuthenticatorData(
+ base::span<const uint8_t, kRpIdHashLength> rp_id_hash,
+ uint32_t current_signature_count,
+ base::Optional<AttestedCredentialData> attested_credential_data =
+ base::nullopt);
+
+ AuthenticatorGetInfoResponse device_info_;
+ base::WeakPtrFactory<FidoDevice> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(VirtualCtap2Device);
+};
+
+} // namespace device
+
+#endif // DEVICE_FIDO_VIRTUAL_CTAP2_DEVICE_H_
diff --git a/chromium/device/fido/virtual_fido_device.cc b/chromium/device/fido/virtual_fido_device.cc
index 38a6112ef44..06c5dac3074 100644
--- a/chromium/device/fido/virtual_fido_device.cc
+++ b/chromium/device/fido/virtual_fido_device.cc
@@ -7,7 +7,6 @@
#include <tuple>
#include <utility>
-#include "crypto/ec_private_key.h"
#include "crypto/ec_signature_creator.h"
#include "device/fido/fido_parsing_utils.h"
@@ -40,10 +39,11 @@ constexpr uint8_t kAttestationKey[]{
VirtualFidoDevice::RegistrationData::RegistrationData() = default;
VirtualFidoDevice::RegistrationData::RegistrationData(
std::unique_ptr<crypto::ECPrivateKey> private_key,
- std::vector<uint8_t> application_parameter,
+ base::span<const uint8_t, kRpIdHashLength> application_parameter,
uint32_t counter)
: private_key(std::move(private_key)),
- application_parameter(std::move(application_parameter)),
+ application_parameter(
+ fido_parsing_utils::Materialize(application_parameter)),
counter(counter) {}
VirtualFidoDevice::RegistrationData::RegistrationData(RegistrationData&& data) =
default;
@@ -121,6 +121,34 @@ VirtualFidoDevice::GenerateAttestationCertificate(
return std::vector<uint8_t>(attestation_cert.begin(), attestation_cert.end());
}
+void VirtualFidoDevice::StoreNewKey(
+ base::span<const uint8_t, kRpIdHashLength> application_parameter,
+ base::span<const uint8_t> key_handle,
+ std::unique_ptr<crypto::ECPrivateKey> private_key) {
+ // Store the registration. Because the key handle is the hashed public key we
+ // just generated, no way this should already be registered.
+ bool did_insert = false;
+ std::tie(std::ignore, did_insert) = mutable_state()->registrations.emplace(
+ fido_parsing_utils::Materialize(key_handle),
+ RegistrationData(std::move(private_key), application_parameter, 1));
+ DCHECK(did_insert);
+}
+
+VirtualFidoDevice::RegistrationData* VirtualFidoDevice::FindRegistrationData(
+ base::span<const uint8_t> key_handle,
+ base::span<const uint8_t, kRpIdHashLength> application_parameter) {
+ // Check if this is our key_handle and it's for this appId.
+ auto it = mutable_state()->registrations.find(key_handle);
+ if (it == mutable_state()->registrations.end())
+ return nullptr;
+
+ if (application_parameter !=
+ base::make_span(it->second.application_parameter))
+ return nullptr;
+
+ return &(it->second);
+}
+
void VirtualFidoDevice::TryWink(WinkCallback cb) {
std::move(cb).Run();
}
diff --git a/chromium/device/fido/virtual_fido_device.h b/chromium/device/fido/virtual_fido_device.h
index 66bc161ffb0..92ad511ecd3 100644
--- a/chromium/device/fido/virtual_fido_device.h
+++ b/chromium/device/fido/virtual_fido_device.h
@@ -18,6 +18,8 @@
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
+#include "crypto/ec_private_key.h"
+#include "device/fido/fido_constants.h"
#include "device/fido/fido_device.h"
#include "device/fido/fido_parsing_utils.h"
#include "net/cert/x509_util.h"
@@ -34,9 +36,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) VirtualFidoDevice : public FidoDevice {
// authenticator device.
struct COMPONENT_EXPORT(DEVICE_FIDO) RegistrationData {
RegistrationData();
- RegistrationData(std::unique_ptr<crypto::ECPrivateKey> private_key,
- std::vector<uint8_t> application_parameter,
- uint32_t counter);
+ RegistrationData(
+ std::unique_ptr<crypto::ECPrivateKey> private_key,
+ base::span<const uint8_t, kRpIdHashLength> application_parameter,
+ uint32_t counter);
RegistrationData(RegistrationData&& data);
RegistrationData& operator=(RegistrationData&& other);
@@ -44,7 +47,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) VirtualFidoDevice : public FidoDevice {
~RegistrationData();
std::unique_ptr<crypto::ECPrivateKey> private_key;
- std::vector<uint8_t> application_parameter;
+ std::array<uint8_t, kRpIdHashLength> application_parameter;
uint32_t counter = 0;
DISALLOW_COPY_AND_ASSIGN(RegistrationData);
@@ -118,6 +121,15 @@ class COMPONENT_EXPORT(DEVICE_FIDO) VirtualFidoDevice : public FidoDevice {
base::Optional<std::vector<uint8_t>> GenerateAttestationCertificate(
bool individual_attestation_requested) const;
+ void StoreNewKey(
+ base::span<const uint8_t, kRpIdHashLength> application_parameter,
+ base::span<const uint8_t> key_handle,
+ std::unique_ptr<crypto::ECPrivateKey> private_key);
+
+ RegistrationData* FindRegistrationData(
+ base::span<const uint8_t> key_handle,
+ base::span<const uint8_t, kRpIdHashLength> application_parameter);
+
// FidoDevice:
void TryWink(WinkCallback cb) override;
std::string GetId() const override;
diff --git a/chromium/device/fido/virtual_u2f_device.cc b/chromium/device/fido/virtual_u2f_device.cc
index fa38b41b215..be3f07c0088 100644
--- a/chromium/device/fido/virtual_u2f_device.cc
+++ b/chromium/device/fido/virtual_u2f_device.cc
@@ -120,8 +120,8 @@ base::Optional<std::vector<uint8_t>> VirtualU2fDevice::DoRegister(
mutable_state()->simulate_press_callback.Run();
}
- auto challenge_param = data.first(32);
- auto application_parameter = data.last(32);
+ auto challenge_param = data.first<32>();
+ auto application_parameter = data.last<32>();
// Create key to register.
// Note: Non-deterministic, you need to mock this out if you rely on
@@ -134,7 +134,8 @@ base::Optional<std::vector<uint8_t>> VirtualU2fDevice::DoRegister(
DCHECK_EQ(public_key.size(), 65ul);
// Our key handles are simple hashes of the public key.
- auto key_handle = fido_parsing_utils::CreateSHA256Hash(public_key);
+ auto hash = fido_parsing_utils::CreateSHA256Hash(public_key);
+ std::vector<uint8_t> key_handle(hash.begin(), hash.end());
// Data to be signed.
std::vector<uint8_t> sign_buffer;
@@ -175,16 +176,7 @@ base::Optional<std::vector<uint8_t>> VirtualU2fDevice::DoRegister(
Append(&response, *attestation_cert);
Append(&response, sig);
- // Store the registration. Because the key handle is the hashed public key we
- // just generated, no way this should already be registered.
- bool did_insert = false;
- std::tie(std::ignore, did_insert) = mutable_state()->registrations.emplace(
- std::move(key_handle),
- RegistrationData(std::move(private_key),
- fido_parsing_utils::Materialize(application_parameter),
- 1));
- DCHECK(did_insert);
-
+ StoreNewKey(application_parameter, key_handle, std::move(private_key));
return apdu::ApduResponse(std::move(response),
apdu::ApduResponse::Status::SW_NO_ERROR)
.GetEncodedResponse();
@@ -205,38 +197,29 @@ base::Optional<std::vector<uint8_t>> VirtualU2fDevice::DoSign(
mutable_state()->simulate_press_callback.Run();
}
- auto challenge_param = data.first(32);
- auto application_parameter = data.subspan(32, 32);
- size_t key_handle_length = data[64];
- if (data.size() != 32 + 32 + 1 + key_handle_length) {
+ if (data.size() < 32 + 32 + 1)
return ErrorStatus(apdu::ApduResponse::Status::SW_WRONG_LENGTH);
- }
- auto key_handle = data.last(key_handle_length);
- // Check if this is our key_handle and it's for this appId.
- auto it = mutable_state()->registrations.find(key_handle);
- if (it == mutable_state()->registrations.end()) {
- return ErrorStatus(apdu::ApduResponse::Status::SW_WRONG_DATA);
- }
+ auto challenge_param = data.first<32>();
+ auto application_parameter = data.subspan<32, 32>();
+ size_t key_handle_length = data[64];
+ if (data.size() != 32 + 32 + 1 + key_handle_length)
+ return ErrorStatus(apdu::ApduResponse::Status::SW_WRONG_LENGTH);
- base::span<const uint8_t> registered_app_id_hash =
- base::make_span(it->second.application_parameter);
- if (application_parameter != registered_app_id_hash) {
- // It's important this error looks identical to the previous one, as
- // tokens should not reveal the existence of keyHandles to unrelated appIds.
+ auto key_handle = data.last(key_handle_length);
+ auto* registration = FindRegistrationData(key_handle, application_parameter);
+ if (!registration)
return ErrorStatus(apdu::ApduResponse::Status::SW_WRONG_DATA);
- }
- auto& registration = it->second;
- ++registration.counter;
+ ++registration->counter;
// First create the part of the response that gets signed over.
std::vector<uint8_t> response;
response.push_back(0x01); // Always pretend we got a touch.
- response.push_back(registration.counter >> 24);
- response.push_back(registration.counter >> 16);
- response.push_back(registration.counter >> 8);
- response.push_back(registration.counter);
+ response.push_back(registration->counter >> 24);
+ response.push_back(registration->counter >> 16);
+ response.push_back(registration->counter >> 8);
+ response.push_back(registration->counter);
std::vector<uint8_t> sign_buffer;
sign_buffer.reserve(application_parameter.size() + response.size() +
@@ -247,7 +230,7 @@ base::Optional<std::vector<uint8_t>> VirtualU2fDevice::DoSign(
// Sign with credential key.
std::vector<uint8_t> sig;
- bool status = Sign(registration.private_key.get(), sign_buffer, &sig);
+ bool status = Sign(registration->private_key.get(), sign_buffer, &sig);
DCHECK(status);
// Add signature for full response.
diff --git a/chromium/device/gamepad/BUILD.gn b/chromium/device/gamepad/BUILD.gn
index eb1509e9eb7..f011fcecc5c 100644
--- a/chromium/device/gamepad/BUILD.gn
+++ b/chromium/device/gamepad/BUILD.gn
@@ -89,6 +89,7 @@ component("gamepad") {
"//base/third_party/dynamic_annotations",
"//device/base/synchronization",
"//device/gamepad/public/cpp:shared_with_blink",
+ "//device/gamepad/public/cpp:switches",
"//device/gamepad/public/mojom",
"//mojo/public/cpp/system",
"//third_party/blink/public:blink_headers",
diff --git a/chromium/device/gamepad/game_controller_data_fetcher_mac.mm b/chromium/device/gamepad/game_controller_data_fetcher_mac.mm
index aa13fb85cc7..79b66a2e447 100644
--- a/chromium/device/gamepad/game_controller_data_fetcher_mac.mm
+++ b/chromium/device/gamepad/game_controller_data_fetcher_mac.mm
@@ -9,7 +9,6 @@
#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/time/time.h"
#include "device/gamepad/gamepad_standard_mappings.h"
#import <GameController/GameController.h>
@@ -113,7 +112,7 @@ void GameControllerDataFetcherMac::GetGamepadData(bool) {
#endif
}
- pad.timestamp = base::TimeTicks::Now().ToInternalValue();
+ pad.timestamp = CurrentTimeInMicroseconds();
pad.axes[AXIS_INDEX_LEFT_STICK_X] =
[[[extended_gamepad leftThumbstick] xAxis] value];
diff --git a/chromium/device/gamepad/gamepad_data_fetcher.cc b/chromium/device/gamepad/gamepad_data_fetcher.cc
index d7a87639b43..630a66f943e 100644
--- a/chromium/device/gamepad/gamepad_data_fetcher.cc
+++ b/chromium/device/gamepad/gamepad_data_fetcher.cc
@@ -4,6 +4,8 @@
#include "device/gamepad/gamepad_data_fetcher.h"
+#include "base/time/time.h"
+
namespace device {
GamepadDataFetcher::GamepadDataFetcher() : provider_(nullptr) {}
@@ -31,6 +33,11 @@ void GamepadDataFetcher::ResetVibration(
mojom::GamepadHapticsResult::GamepadHapticsResultError);
}
+// static
+int64_t GamepadDataFetcher::CurrentTimeInMicroseconds() {
+ return base::TimeTicks::Now().since_origin().InMicroseconds();
+}
+
GamepadDataFetcherFactory::GamepadDataFetcherFactory() = default;
} // namespace device
diff --git a/chromium/device/gamepad/gamepad_data_fetcher.h b/chromium/device/gamepad/gamepad_data_fetcher.h
index 6f9f08058af..12ec5eb41bd 100644
--- a/chromium/device/gamepad/gamepad_data_fetcher.h
+++ b/chromium/device/gamepad/gamepad_data_fetcher.h
@@ -40,6 +40,10 @@ class DEVICE_GAMEPAD_EXPORT GamepadDataFetcher {
return provider_->GetPadState(source(), source_id);
}
+ // Returns the current time value in microseconds. Data fetchers should use
+ // the value returned by this method to update the |timestamp| gamepad member.
+ static int64_t CurrentTimeInMicroseconds();
+
protected:
friend GamepadPadStateProvider;
diff --git a/chromium/device/gamepad/gamepad_device_linux.cc b/chromium/device/gamepad/gamepad_device_linux.cc
index 2a7c896bd85..83923134cab 100644
--- a/chromium/device/gamepad/gamepad_device_linux.cc
+++ b/chromium/device/gamepad/gamepad_device_linux.cc
@@ -12,8 +12,10 @@
#include <sys/ioctl.h>
#include "base/posix/eintr_wrapper.h"
+#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
+#include "device/gamepad/gamepad_data_fetcher.h"
#include "device/udev_linux/udev_linux.h"
namespace device {
@@ -27,6 +29,19 @@ const float kMaxLinuxAxisValue = 32767.0;
const int kInvalidEffectId = -1;
const uint16_t kRumbleMagnitudeMax = 0xffff;
+const size_t kSpecialKeys[] = {
+ // Xbox One S pre-FW update reports Xbox button as SystemMainMenu over BT.
+ KEY_MENU,
+ // Power is used for the Guide button on the Nvidia Shield 2015 gamepad.
+ KEY_POWER,
+ // Search is used for the Guide button on the Nvidia Shield 2015 gamepad.
+ KEY_SEARCH,
+ // Start, Back, and Guide buttons are often reported as Consumer Home or
+ // Back.
+ KEY_HOMEPAGE, KEY_BACK,
+};
+const size_t kSpecialKeysLen = base::size(kSpecialKeys);
+
#define LONG_BITS (CHAR_BIT * sizeof(long))
#define BITS_TO_LONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
@@ -61,6 +76,37 @@ bool HasRumbleCapability(int fd) {
return test_bit(FF_RUMBLE, ffbit);
}
+// Check an evdev device for key codes which sometimes appear on gamepads but
+// aren't reported by joydev. If a special key is found, the corresponding entry
+// of the |has_special_key| vector is set to true. Returns the number of
+// special keys found.
+size_t CheckSpecialKeys(int fd, std::vector<bool>* has_special_key) {
+ DCHECK(has_special_key);
+ unsigned long evbit[BITS_TO_LONGS(EV_MAX)];
+ unsigned long keybit[BITS_TO_LONGS(KEY_MAX)];
+ size_t found_special_keys = 0;
+
+ has_special_key->clear();
+ if (HANDLE_EINTR(ioctl(fd, EVIOCGBIT(0, EV_MAX), evbit)) < 0 ||
+ HANDLE_EINTR(ioctl(fd, EVIOCGBIT(EV_KEY, KEY_MAX), keybit)) < 0) {
+ return 0;
+ }
+
+ if (!test_bit(EV_KEY, evbit)) {
+ return 0;
+ }
+
+ has_special_key->resize(kSpecialKeysLen, false);
+ for (size_t special_index = 0; special_index < kSpecialKeysLen;
+ ++special_index) {
+ (*has_special_key)[special_index] =
+ test_bit(kSpecialKeys[special_index], keybit);
+ ++found_special_keys;
+ }
+
+ return found_special_keys;
+}
+
bool GetHidrawDevinfo(int fd,
GamepadBusType* bus_type,
uint16_t* vendor_id,
@@ -122,11 +168,7 @@ bool StartOrStopEffect(int fd, int effect_id, bool do_start) {
GamepadDeviceLinux::GamepadDeviceLinux(const std::string& syspath_prefix)
: syspath_prefix_(syspath_prefix),
- joydev_fd_(-1),
- joydev_index_(-1),
- evdev_fd_(-1),
- effect_id_(kInvalidEffectId),
- hidraw_fd_(-1) {}
+ button_indices_used_(Gamepad::kButtonsLengthCap, false) {}
GamepadDeviceLinux::~GamepadDeviceLinux() = default;
@@ -152,7 +194,7 @@ bool GamepadDeviceLinux::SupportsVibration() const {
return supports_force_feedback_ && evdev_fd_ >= 0;
}
-void GamepadDeviceLinux::ReadPadState(Gamepad* pad) const {
+void GamepadDeviceLinux::ReadPadState(Gamepad* pad) {
if (switch_pro_ && bus_type_ == GAMEPAD_BUS_USB) {
// When connected over USB, the Switch Pro controller does not correctly
// report its state over USB HID. Instead, fetch the state using the
@@ -163,6 +205,33 @@ void GamepadDeviceLinux::ReadPadState(Gamepad* pad) const {
DCHECK_GE(joydev_fd_, 0);
+ // Read button and axis events from the joydev device.
+ bool pad_updated = ReadJoydevState(pad);
+
+ // Evdev special buttons must be initialized after we have read from joydev
+ // at least once to ensure we do not assign a button index already in use by
+ // joydev.
+ if (!evdev_special_keys_initialized_)
+ InitializeEvdevSpecialKeys();
+
+ // Read button events from the evdev device.
+ if (!special_button_map_.empty()) {
+ if (ReadEvdevSpecialKeys(pad))
+ pad_updated = true;
+ }
+
+ if (pad_updated)
+ pad->timestamp = GamepadDataFetcher::CurrentTimeInMicroseconds();
+}
+
+bool GamepadDeviceLinux::ReadJoydevState(Gamepad* pad) {
+ DCHECK(pad);
+
+ if (joydev_fd_ < 0)
+ return false;
+
+ // Read button and axis events from the joydev device.
+ bool pad_updated = false;
js_event event;
while (HANDLE_EINTR(read(joydev_fd_, &event, sizeof(struct js_event))) > 0) {
size_t item = event.number;
@@ -174,6 +243,7 @@ void GamepadDeviceLinux::ReadPadState(Gamepad* pad) const {
if (item >= pad->axes_length)
pad->axes_length = item + 1;
+ pad_updated = true;
} else if (event.type & JS_EVENT_BUTTON) {
if (item >= Gamepad::kButtonsLengthCap)
continue;
@@ -181,11 +251,90 @@ void GamepadDeviceLinux::ReadPadState(Gamepad* pad) const {
pad->buttons[item].pressed = event.value;
pad->buttons[item].value = event.value ? 1.0 : 0.0;
+ // When a joydev device is opened, synthetic events are generated for
+ // each joystick button and axis with the JS_EVENT_INIT flag set on the
+ // event type. Use this signal to mark these button indices as used.
+ if (event.type & JS_EVENT_INIT)
+ button_indices_used_[item] = true;
+
if (item >= pad->buttons_length)
pad->buttons_length = item + 1;
+ pad_updated = true;
+ }
+ }
+ return pad_updated;
+}
+
+void GamepadDeviceLinux::InitializeEvdevSpecialKeys() {
+ if (evdev_fd_ < 0)
+ return;
+
+ // Do some one-time initialization to decide indices for the evdev special
+ // buttons.
+ evdev_special_keys_initialized_ = true;
+ std::vector<bool> special_key_present;
+ size_t unmapped_button_count =
+ CheckSpecialKeys(evdev_fd_, &special_key_present);
+
+ special_button_map_.clear();
+ if (unmapped_button_count > 0) {
+ // Insert special buttons at unused button indices.
+ special_button_map_.resize(kSpecialKeysLen, -1);
+ size_t button_index = 0;
+ for (size_t special_index = 0; special_index < kSpecialKeysLen;
+ ++special_index) {
+ if (!special_key_present[special_index])
+ continue;
+
+ // Advance to the next unused button index.
+ while (button_indices_used_[button_index] &&
+ button_index < Gamepad::kButtonsLengthCap) {
+ ++button_index;
+ }
+ if (button_index >= Gamepad::kButtonsLengthCap)
+ break;
+
+ special_button_map_[special_index] = button_index;
+ button_indices_used_[button_index] = true;
+ ++button_index;
+
+ if (--unmapped_button_count == 0)
+ break;
+ }
+ }
+}
+
+bool GamepadDeviceLinux::ReadEvdevSpecialKeys(Gamepad* pad) {
+ DCHECK(pad);
+
+ if (evdev_fd_ < 0)
+ return false;
+
+ // Read special button events through evdev.
+ bool pad_updated = false;
+ input_event ev;
+ ssize_t bytes_read;
+ while ((bytes_read =
+ HANDLE_EINTR(read(evdev_fd_, &ev, sizeof(input_event)))) > 0) {
+ if (size_t{bytes_read} < sizeof(input_event))
+ break;
+ if (ev.type != EV_KEY)
+ continue;
+
+ for (size_t special_index = 0; special_index < kSpecialKeysLen;
+ ++special_index) {
+ int button_index = special_button_map_[special_index];
+ if (button_index < 0)
+ continue;
+ if (ev.code == kSpecialKeys[special_index]) {
+ pad->buttons[button_index].pressed = ev.value;
+ pad->buttons[button_index].value = ev.value ? 1.0 : 0.0;
+ pad_updated = true;
+ }
}
- pad->timestamp = event.time;
}
+
+ return pad_updated;
}
GamepadStandardMappingFunction GamepadDeviceLinux::GetMappingFunction() const {
@@ -271,6 +420,11 @@ void GamepadDeviceLinux::CloseJoydevNode() {
product_id_.clear();
version_number_.clear();
name_.clear();
+
+ // Button indices must be recomputed once the joydev node is closed.
+ button_indices_used_.clear();
+ special_button_map_.clear();
+ evdev_special_keys_initialized_ = false;
}
bool GamepadDeviceLinux::OpenEvdevNode(const UdevGamepadLinux& pad_info) {
@@ -298,6 +452,16 @@ void GamepadDeviceLinux::CloseEvdevNode() {
evdev_fd_ = -1;
}
supports_force_feedback_ = false;
+
+ // Clear any entries in |button_indices_used_| that were taken by evdev.
+ if (!special_button_map_.empty()) {
+ for (int button_index : special_button_map_) {
+ if (button_index >= 0)
+ button_indices_used_[button_index] = false;
+ }
+ }
+ special_button_map_.clear();
+ evdev_special_keys_initialized_ = false;
}
bool GamepadDeviceLinux::OpenHidrawNode(const UdevGamepadLinux& pad_info) {
diff --git a/chromium/device/gamepad/gamepad_device_linux.h b/chromium/device/gamepad/gamepad_device_linux.h
index e5a0979d3a2..f34df9efdeb 100644
--- a/chromium/device/gamepad/gamepad_device_linux.h
+++ b/chromium/device/gamepad/gamepad_device_linux.h
@@ -5,6 +5,10 @@
#ifndef DEVICE_GAMEPAD_GAMEPAD_DEVICE_LINUX_
#define DEVICE_GAMEPAD_GAMEPAD_DEVICE_LINUX_
+#include <memory>
+#include <string>
+#include <vector>
+
#include "device/gamepad/abstract_haptic_gamepad.h"
#include "device/gamepad/dualshock4_controller_linux.h"
#include "device/gamepad/gamepad_standard_mappings.h"
@@ -48,7 +52,19 @@ class GamepadDeviceLinux : public AbstractHapticGamepad {
bool SupportsVibration() const;
// Reads the current gamepad state into |pad|.
- void ReadPadState(Gamepad* pad) const;
+ void ReadPadState(Gamepad* pad);
+
+ // Reads the state of gamepad buttons and axes using joydev. Returns true if
+ // |pad| was updated.
+ bool ReadJoydevState(Gamepad* pad);
+
+ // Discovers and assigns button indices for key codes that are outside the
+ // normal gamepad button range.
+ void InitializeEvdevSpecialKeys();
+
+ // Reads the state of keys outside the normal button range using evdev.
+ // Returns true if |pad| was updated.
+ bool ReadEvdevSpecialKeys(Gamepad* pad);
// Returns true if |pad_info| describes this device.
bool IsSameDevice(const UdevGamepadLinux& pad_info);
@@ -90,13 +106,17 @@ class GamepadDeviceLinux : public AbstractHapticGamepad {
// The file descriptor for the device's joydev node, or -1 if no joydev node
// is associated with this device.
- int joydev_fd_;
+ int joydev_fd_ = -1;
// The index of the device's joydev node, or -1 if unknown.
// The joydev index is the integer at the end of the joydev node path and is
// used to assign the gamepad to a slot. For example, a device with path
// /dev/input/js2 has index 2 and will be assigned to the 3rd gamepad slot.
- int joydev_index_;
+ int joydev_index_ = -1;
+
+ // Maps from indices in the Gamepad buttons array to a boolean value
+ // indicating whether the button index is already mapped.
+ std::vector<bool> button_indices_used_;
// The vendor ID of the device.
std::string vendor_id_;
@@ -112,17 +132,25 @@ class GamepadDeviceLinux : public AbstractHapticGamepad {
// The file descriptor for the device's evdev node, or -1 if no evdev node is
// associated with this device.
- int evdev_fd_;
+ int evdev_fd_ = -1;
// The ID of the haptic effect stored on the device, or -1 if none is stored.
- int effect_id_;
+ int effect_id_ = -1;
// True if the device supports rumble effects through the evdev device node.
- bool supports_force_feedback_;
+ bool supports_force_feedback_ = false;
+
+ // Set to true once the evdev button capabilities have been checked.
+ bool evdev_special_keys_initialized_ = false;
+
+ // Mapping from "special" index (an index within the kSpecialKeys table) to
+ // button index (an index within the Gamepad buttons array), or -1 if the
+ // button is not mapped. Empty if no special buttons are mapped.
+ std::vector<int> special_button_map_;
// The file descriptor for the device's hidraw node, or -1 if no hidraw node
// is associated with this device.
- int hidraw_fd_;
+ int hidraw_fd_ = -1;
// The type of the bus through which the device is connected, or
// GAMEPAD_BUS_UNKNOWN if the bus type could not be determined.
diff --git a/chromium/device/gamepad/gamepad_device_mac.h b/chromium/device/gamepad/gamepad_device_mac.h
index 4ec2ea428c6..7c8bc08e305 100644
--- a/chromium/device/gamepad/gamepad_device_mac.h
+++ b/chromium/device/gamepad/gamepad_device_mac.h
@@ -58,6 +58,12 @@ class GamepadDeviceMac : public AbstractHapticGamepad {
// Stop vibration and release held resources.
void DoShutdown() override;
+ // Initialize button capabilities for |gamepad|.
+ bool AddButtons(Gamepad* gamepad);
+
+ // Initialize axis capabilities for |gamepad|.
+ bool AddAxes(Gamepad* gamepad);
+
// Return true if this element has a parent collection with a usage page that
// suggests it could be a gamepad.
static bool CheckCollection(IOHIDElementRef element);
diff --git a/chromium/device/gamepad/gamepad_device_mac.mm b/chromium/device/gamepad/gamepad_device_mac.mm
index e99b89629c6..e22246f3235 100644
--- a/chromium/device/gamepad/gamepad_device_mac.mm
+++ b/chromium/device/gamepad/gamepad_device_mac.mm
@@ -6,22 +6,48 @@
#include "base/mac/foundation_util.h"
#include "base/mac/scoped_cftyperef.h"
+#include "base/stl_util.h"
+#include "device/gamepad/gamepad_data_fetcher.h"
#import <Foundation/Foundation.h>
namespace {
// http://www.usb.org/developers/hidpage
-const uint32_t kGenericDesktopUsagePage = 0x01;
-const uint32_t kGameControlsUsagePage = 0x05;
-const uint32_t kButtonUsagePage = 0x09;
-const uint32_t kJoystickUsageNumber = 0x04;
-const uint32_t kGameUsageNumber = 0x05;
-const uint32_t kMultiAxisUsageNumber = 0x08;
-const uint32_t kAxisMinimumUsageNumber = 0x30;
+const uint16_t kGenericDesktopUsagePage = 0x01;
+const uint16_t kGameControlsUsagePage = 0x05;
+const uint16_t kButtonUsagePage = 0x09;
+const uint16_t kConsumerUsagePage = 0x0c;
+
+const uint16_t kJoystickUsageNumber = 0x04;
+const uint16_t kGameUsageNumber = 0x05;
+const uint16_t kMultiAxisUsageNumber = 0x08;
+const uint16_t kAxisMinimumUsageNumber = 0x30;
+const uint16_t kSystemMainMenuUsageNumber = 0x85;
+const uint16_t kPowerUsageNumber = 0x30;
+const uint16_t kSearchUsageNumber = 0x0221;
+const uint16_t kHomeUsageNumber = 0x0223;
+const uint16_t kBackUsageNumber = 0x0224;
const int kRumbleMagnitudeMax = 10000;
+struct SpecialUsages {
+ const uint16_t usage_page;
+ const uint16_t usage;
+} kSpecialUsages[] = {
+ // Xbox One S pre-FW update reports Xbox button as SystemMainMenu over BT.
+ {kGenericDesktopUsagePage, kSystemMainMenuUsageNumber},
+ // Power is used for the Guide button on the Nvidia Shield 2015 gamepad.
+ {kConsumerUsagePage, kPowerUsageNumber},
+ // Search is used for the Guide button on the Nvidia Shield 2017 gamepad.
+ {kConsumerUsagePage, kSearchUsageNumber},
+ // Start, Back, and Guide buttons are often reported as Consumer Home or
+ // Back.
+ {kConsumerUsagePage, kHomeUsageNumber},
+ {kConsumerUsagePage, kBackUsageNumber},
+};
+const size_t kSpecialUsagesLen = base::size(kSpecialUsages);
+
float NormalizeAxis(CFIndex value, CFIndex min, CFIndex max) {
return (2.f * (value - min) / static_cast<float>(max - min)) - 1.f;
}
@@ -50,14 +76,6 @@ GamepadDeviceMac::GamepadDeviceMac(int location_id,
device_ref_(device_ref),
ff_device_ref_(nullptr),
ff_effect_ref_(nullptr) {
- std::fill(button_elements_, button_elements_ + Gamepad::kButtonsLengthCap,
- nullptr);
- std::fill(axis_elements_, axis_elements_ + Gamepad::kAxesLengthCap, nullptr);
- std::fill(axis_minimums_, axis_minimums_ + Gamepad::kAxesLengthCap, 0);
- std::fill(axis_maximums_, axis_maximums_ + Gamepad::kAxesLengthCap, 0);
- std::fill(axis_report_sizes_, axis_report_sizes_ + Gamepad::kAxesLengthCap,
- 0);
-
if (Dualshock4ControllerMac::IsDualshock4(vendor_id, product_id)) {
dualshock4_ = std::make_unique<Dualshock4ControllerMac>(device_ref);
} else if (device_ref) {
@@ -104,19 +122,25 @@ bool GamepadDeviceMac::CheckCollection(IOHIDElementRef element) {
}
bool GamepadDeviceMac::AddButtonsAndAxes(Gamepad* gamepad) {
+ bool has_buttons = AddButtons(gamepad);
+ bool has_axes = AddAxes(gamepad);
+ gamepad->timestamp = GamepadDataFetcher::CurrentTimeInMicroseconds();
+ return (has_buttons || has_axes);
+}
+
+bool GamepadDeviceMac::AddButtons(Gamepad* gamepad) {
base::ScopedCFTypeRef<CFArrayRef> elements_cf(IOHIDDeviceCopyMatchingElements(
device_ref_, nullptr, kIOHIDOptionsTypeNone));
NSArray* elements = base::mac::CFToNSCast(elements_cf);
DCHECK(elements);
DCHECK(gamepad);
- gamepad->axes_length = 0;
- gamepad->buttons_length = 0;
- gamepad->timestamp = 0;
- memset(gamepad->axes, 0, sizeof(gamepad->axes));
memset(gamepad->buttons, 0, sizeof(gamepad->buttons));
+ std::fill(button_elements_, button_elements_ + Gamepad::kButtonsLengthCap,
+ nullptr);
- bool mapped_all_axes = true;
-
+ std::vector<IOHIDElementRef> special_element(kSpecialUsagesLen, nullptr);
+ size_t button_count = 0;
+ size_t unmapped_button_count = 0;
for (id elem in elements) {
IOHIDElementRef element = reinterpret_cast<IOHIDElementRef>(elem);
if (!CheckCollection(element))
@@ -124,28 +148,111 @@ bool GamepadDeviceMac::AddButtonsAndAxes(Gamepad* gamepad) {
uint32_t usage_page = IOHIDElementGetUsagePage(element);
uint32_t usage = IOHIDElementGetUsage(element);
- if (IOHIDElementGetType(element) == kIOHIDElementTypeInput_Button &&
- usage_page == kButtonUsagePage) {
- uint32_t button_index = usage - 1;
- if (button_index < Gamepad::kButtonsLengthCap) {
+ if (IOHIDElementGetType(element) == kIOHIDElementTypeInput_Button) {
+ if (usage_page == kButtonUsagePage && usage > 0) {
+ size_t button_index = size_t{usage - 1};
+
+ // Ignore buttons with large usage values.
+ if (button_index >= Gamepad::kButtonsLengthCap)
+ continue;
+
+ // Button index already assigned, ignore.
+ if (button_elements_[button_index])
+ continue;
+
button_elements_[button_index] = element;
- gamepad->buttons_length =
- std::max(gamepad->buttons_length, button_index + 1);
- }
- } else if (IOHIDElementGetType(element) == kIOHIDElementTypeInput_Misc) {
- uint32_t axis_index = usage - kAxisMinimumUsageNumber;
- if (axis_index < Gamepad::kAxesLengthCap && !axis_elements_[axis_index]) {
- axis_elements_[axis_index] = element;
- gamepad->axes_length = std::max(gamepad->axes_length, axis_index + 1);
+ button_count = std::max(button_count, button_index + 1);
} else {
- mapped_all_axes = false;
+ // Check for common gamepad buttons that are not on the Button usage
+ // page. Button indices are assigned in a second pass.
+ for (size_t special_index = 0; special_index < kSpecialUsagesLen;
+ ++special_index) {
+ const auto& special = kSpecialUsages[special_index];
+ if (usage_page == special.usage_page && usage == special.usage) {
+ special_element[special_index] = element;
+ ++unmapped_button_count;
+ }
+ }
}
}
}
- if (!mapped_all_axes) {
- // For axes whose usage puts them outside the standard axesLengthCap range.
- uint32_t next_index = 0;
+ if (unmapped_button_count > 0) {
+ // Insert unmapped buttons at unused button indices.
+ size_t button_index = 0;
+ for (size_t special_index = 0; special_index < kSpecialUsagesLen;
+ ++special_index) {
+ if (!special_element[special_index])
+ continue;
+
+ // Advance to the next unused button index.
+ while (button_index < Gamepad::kButtonsLengthCap &&
+ button_elements_[button_index]) {
+ ++button_index;
+ }
+ if (button_index >= Gamepad::kButtonsLengthCap)
+ break;
+
+ button_elements_[button_index] = special_element[special_index];
+ button_count = std::max(button_count, button_index + 1);
+
+ if (--unmapped_button_count == 0)
+ break;
+ }
+ }
+
+ gamepad->buttons_length = button_count;
+ return gamepad->buttons_length > 0;
+}
+
+bool GamepadDeviceMac::AddAxes(Gamepad* gamepad) {
+ base::ScopedCFTypeRef<CFArrayRef> elements_cf(IOHIDDeviceCopyMatchingElements(
+ device_ref_, nullptr, kIOHIDOptionsTypeNone));
+ NSArray* elements = base::mac::CFToNSCast(elements_cf);
+ DCHECK(elements);
+ DCHECK(gamepad);
+ memset(gamepad->axes, 0, sizeof(gamepad->axes));
+ std::fill(axis_elements_, axis_elements_ + Gamepad::kAxesLengthCap, nullptr);
+ std::fill(axis_minimums_, axis_minimums_ + Gamepad::kAxesLengthCap, 0);
+ std::fill(axis_maximums_, axis_maximums_ + Gamepad::kAxesLengthCap, 0);
+ std::fill(axis_report_sizes_, axis_report_sizes_ + Gamepad::kAxesLengthCap,
+ 0);
+
+ // Most axes are mapped so that their index in the Gamepad axes array
+ // corresponds to the usage ID. However, this is not possible when the usage
+ // ID would cause the axis index to exceed the bounds of the axes array.
+ // Axes with large usage IDs are mapped in a second pass.
+ size_t axis_count = 0;
+ size_t unmapped_axis_count = 0;
+
+ for (id elem in elements) {
+ IOHIDElementRef element = reinterpret_cast<IOHIDElementRef>(elem);
+ if (!CheckCollection(element))
+ continue;
+
+ uint32_t usage_page = IOHIDElementGetUsagePage(element);
+ uint32_t usage = IOHIDElementGetUsage(element);
+ if (IOHIDElementGetType(element) != kIOHIDElementTypeInput_Misc ||
+ usage < kAxisMinimumUsageNumber) {
+ continue;
+ }
+
+ size_t axis_index = size_t{usage - kAxisMinimumUsageNumber};
+ if (axis_index < Gamepad::kAxesLengthCap) {
+ // Axis index already assigned, ignore.
+ if (axis_elements_[axis_index])
+ continue;
+ axis_elements_[axis_index] = element;
+ axis_count = std::max(axis_count, axis_index + 1);
+ } else if (usage_page <= kGameControlsUsagePage) {
+ // Assign an index for this axis in the second pass.
+ ++unmapped_axis_count;
+ }
+ }
+
+ if (unmapped_axis_count > 0) {
+ // Insert unmapped axes at unused axis indices.
+ size_t axis_index = 0;
for (id elem in elements) {
IOHIDElementRef element = reinterpret_cast<IOHIDElementRef>(elem);
if (!CheckCollection(element))
@@ -153,26 +260,35 @@ bool GamepadDeviceMac::AddButtonsAndAxes(Gamepad* gamepad) {
uint32_t usage_page = IOHIDElementGetUsagePage(element);
uint32_t usage = IOHIDElementGetUsage(element);
- if (IOHIDElementGetType(element) == kIOHIDElementTypeInput_Misc &&
- usage - kAxisMinimumUsageNumber >= Gamepad::kAxesLengthCap &&
- usage_page <= kGameControlsUsagePage) {
- for (; next_index < Gamepad::kAxesLengthCap; ++next_index) {
- if (axis_elements_[next_index] == NULL)
- break;
- }
- if (next_index < Gamepad::kAxesLengthCap) {
- axis_elements_[next_index] = element;
- gamepad->axes_length = std::max(gamepad->axes_length, next_index + 1);
- }
+ if (IOHIDElementGetType(element) != kIOHIDElementTypeInput_Misc ||
+ usage < kAxisMinimumUsageNumber ||
+ usage_page > kGameControlsUsagePage) {
+ continue;
}
- if (next_index >= Gamepad::kAxesLengthCap)
+ // Ignore axes with small usage IDs that should have been mapped in the
+ // initial pass.
+ if (size_t{usage - kAxisMinimumUsageNumber} < Gamepad::kAxesLengthCap)
+ continue;
+
+ // Advance to the next unused axis index.
+ while (axis_index < Gamepad::kAxesLengthCap &&
+ axis_elements_[axis_index]) {
+ ++axis_index;
+ }
+ if (axis_index >= Gamepad::kAxesLengthCap)
+ break;
+
+ axis_elements_[axis_index] = element;
+ axis_count = std::max(axis_count, axis_index + 1);
+
+ if (--unmapped_axis_count == 0)
break;
}
}
- for (uint32_t axis_index = 0; axis_index < gamepad->axes_length;
- ++axis_index) {
+ // Fetch the logical range and report size for each axis.
+ for (size_t axis_index = 0; axis_index < axis_count; ++axis_index) {
IOHIDElementRef element = axis_elements_[axis_index];
if (element != NULL) {
CFIndex axis_min = IOHIDElementGetLogicalMin(element);
@@ -190,7 +306,9 @@ bool GamepadDeviceMac::AddButtonsAndAxes(Gamepad* gamepad) {
axis_report_sizes_[axis_index] = IOHIDElementGetReportSize(element);
}
}
- return (gamepad->axes_length > 0 || gamepad->buttons_length > 0);
+
+ gamepad->axes_length = axis_count;
+ return gamepad->axes_length > 0;
}
void GamepadDeviceMac::UpdateGamepadForValue(IOHIDValueRef value,
@@ -210,8 +328,7 @@ void GamepadDeviceMac::UpdateGamepadForValue(IOHIDValueRef value,
bool pressed = IOHIDValueGetIntegerValue(value);
gamepad->buttons[i].pressed = pressed;
gamepad->buttons[i].value = pressed ? 1.f : 0.f;
- gamepad->timestamp =
- std::max(gamepad->timestamp, IOHIDValueGetTimeStamp(value));
+ gamepad->timestamp = GamepadDataFetcher::CurrentTimeInMicroseconds();
return;
}
}
@@ -242,9 +359,7 @@ void GamepadDeviceMac::UpdateGamepadForValue(IOHIDValueRef value,
} else {
gamepad->axes[i] = NormalizeAxis(axis_value, axis_min, axis_max);
}
-
- gamepad->timestamp =
- std::max(gamepad->timestamp, IOHIDValueGetTimeStamp(value));
+ gamepad->timestamp = GamepadDataFetcher::CurrentTimeInMicroseconds();
return;
}
}
diff --git a/chromium/device/gamepad/gamepad_monitor.cc b/chromium/device/gamepad/gamepad_monitor.cc
index f15ee2080bb..27ba975651e 100644
--- a/chromium/device/gamepad/gamepad_monitor.cc
+++ b/chromium/device/gamepad/gamepad_monitor.cc
@@ -45,7 +45,7 @@ void GamepadMonitor::GamepadStartPolling(GamepadStartPollingCallback callback) {
GamepadService* service = GamepadService::GetInstance();
service->ConsumerBecameActive(this);
- std::move(callback).Run(service->GetSharedBufferHandle());
+ std::move(callback).Run(service->DuplicateSharedMemoryRegion());
}
void GamepadMonitor::GamepadStopPolling(GamepadStopPollingCallback callback) {
diff --git a/chromium/device/gamepad/gamepad_platform_data_fetcher_android.cc b/chromium/device/gamepad/gamepad_platform_data_fetcher_android.cc
index d36be007d3c..355292a0f8f 100644
--- a/chromium/device/gamepad/gamepad_platform_data_fetcher_android.cc
+++ b/chromium/device/gamepad/gamepad_platform_data_fetcher_android.cc
@@ -111,7 +111,7 @@ static void JNI_GamepadList_SetGamepadData(
}
pad.connected = true;
- pad.timestamp = timestamp;
+ pad.timestamp = GamepadDataFetcher::CurrentTimeInMicroseconds();
std::vector<float> axes;
base::android::JavaFloatArrayToFloatVector(env, jaxes, &axes);
diff --git a/chromium/device/gamepad/gamepad_platform_data_fetcher_win.cc b/chromium/device/gamepad/gamepad_platform_data_fetcher_win.cc
index c99fa2c990c..93ec6038eaa 100644
--- a/chromium/device/gamepad/gamepad_platform_data_fetcher_win.cc
+++ b/chromium/device/gamepad/gamepad_platform_data_fetcher_win.cc
@@ -188,7 +188,7 @@ void GamepadPlatformDataFetcherWin::GetXInputPadData(int i) {
TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i);
if (dwResult == ERROR_SUCCESS) {
- pad.timestamp = state.dwPacketNumber;
+ pad.timestamp = CurrentTimeInMicroseconds();
pad.buttons_length = 0;
WORD val = state.Gamepad.wButtons;
#define ADD(b) \
diff --git a/chromium/device/gamepad/gamepad_provider.cc b/chromium/device/gamepad/gamepad_provider.cc
index f2d844f8845..ed7fcfa8ef2 100644
--- a/chromium/device/gamepad/gamepad_provider.cc
+++ b/chromium/device/gamepad/gamepad_provider.cc
@@ -15,6 +15,7 @@
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
@@ -23,6 +24,7 @@
#include "device/gamepad/gamepad_data_fetcher.h"
#include "device/gamepad/gamepad_data_fetcher_manager.h"
#include "device/gamepad/gamepad_user_gesture.h"
+#include "device/gamepad/public/cpp/gamepad_features.h"
#include "mojo/public/cpp/system/platform_handle.h"
namespace device {
@@ -83,17 +85,9 @@ GamepadProvider::~GamepadProvider() {
DCHECK(data_fetchers_.empty());
}
-base::SharedMemoryHandle GamepadProvider::DuplicateSharedMemoryHandle() {
- return gamepad_shared_buffer_->shared_memory()->handle().Duplicate();
-}
-
-mojo::ScopedSharedBufferHandle GamepadProvider::GetSharedBufferHandle() {
- // TODO(heke): Use mojo::SharedBuffer rather than base::SharedMemory in
- // GamepadSharedBuffer. See crbug.com/670655 for details
- return mojo::WrapSharedMemoryHandle(
- gamepad_shared_buffer_->shared_memory()->GetReadOnlyHandle(),
- sizeof(GamepadHardwareBuffer),
- mojo::UnwrappedSharedMemoryHandleProtection::kReadOnly);
+base::ReadOnlySharedMemoryRegion
+GamepadProvider::DuplicateSharedMemoryRegion() {
+ return gamepad_shared_buffer_->DuplicateSharedMemoryRegion();
}
void GamepadProvider::GetCurrentGamepadData(Gamepads* data) {
@@ -185,6 +179,9 @@ void GamepadProvider::OnDevicesChanged(base::SystemMonitor::DeviceType type) {
}
void GamepadProvider::Initialize(std::unique_ptr<GamepadDataFetcher> fetcher) {
+ sampling_interval_delta_ =
+ base::TimeDelta::FromMilliseconds(features::GetGamepadPollingInterval());
+
base::SystemMonitor* monitor = base::SystemMonitor::Get();
if (monitor)
monitor->AddDevicesChangedObserver(this);
@@ -372,7 +369,7 @@ void GamepadProvider::ScheduleDoPoll() {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, base::BindOnce(&GamepadProvider::DoPoll, Unretained(this)),
- base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs));
+ sampling_interval_delta_);
have_scheduled_do_poll_ = true;
}
diff --git a/chromium/device/gamepad/gamepad_provider.h b/chromium/device/gamepad/gamepad_provider.h
index 51a744904ce..03e31de56c7 100644
--- a/chromium/device/gamepad/gamepad_provider.h
+++ b/chromium/device/gamepad/gamepad_provider.h
@@ -11,10 +11,11 @@
#include "base/callback_forward.h"
#include "base/macros.h"
+#include "base/memory/read_only_shared_memory_region.h"
#include "base/memory/ref_counted.h"
-#include "base/memory/shared_memory.h"
#include "base/synchronization/lock.h"
#include "base/system_monitor/system_monitor.h"
+#include "base/time/time.h"
#include "device/gamepad/gamepad_export.h"
#include "device/gamepad/gamepad_pad_state_provider.h"
#include "device/gamepad/gamepad_shared_buffer.h"
@@ -52,11 +53,8 @@ class DEVICE_GAMEPAD_EXPORT GamepadProvider
~GamepadProvider() override;
- // Returns a duplicate of the shared memory handle of the gamepad data.
- base::SharedMemoryHandle DuplicateSharedMemoryHandle();
-
- // Returns a new mojo::ScopedSharedBufferHandle of the gamepad data.
- mojo::ScopedSharedBufferHandle GetSharedBufferHandle();
+ // Returns a duplicate of the shared memory region of the gamepad data.
+ base::ReadOnlySharedMemoryRegion DuplicateSharedMemoryRegion();
void GetCurrentGamepadData(Gamepads* data);
@@ -114,7 +112,8 @@ class DEVICE_GAMEPAD_EXPORT GamepadProvider
// true if any user gesture observers were notified.
bool CheckForUserGesture();
- enum { kDesiredSamplingIntervalMs = 16 };
+ // The duration of the delay between iterations of DoPoll.
+ base::TimeDelta sampling_interval_delta_;
// Keeps track of when the background thread is paused. Access to is_paused_
// must be guarded by is_paused_lock_.
diff --git a/chromium/device/gamepad/gamepad_provider_unittest.cc b/chromium/device/gamepad/gamepad_provider_unittest.cc
index 6da39ada5f8..ad1cceea621 100644
--- a/chromium/device/gamepad/gamepad_provider_unittest.cc
+++ b/chromium/device/gamepad/gamepad_provider_unittest.cc
@@ -53,7 +53,7 @@ class GamepadProviderTest : public testing::Test, public GamepadTestHelper {
// gamepad fetchers. The buffer will report an odd value for the version if
// the buffer is not in a consistent state, so we also require that the value
// is even before continuing.
- void WaitForData(GamepadHardwareBuffer* buffer) {
+ void WaitForData(const GamepadHardwareBuffer* buffer) {
const base::subtle::Atomic32 initial_version = buffer->seqlock.ReadBegin();
base::subtle::Atomic32 current_version;
do {
@@ -65,12 +65,12 @@ class GamepadProviderTest : public testing::Test, public GamepadTestHelper {
// The provider polls the data on the background thread and then issues
// the callback on the client thread. Waiting for it to poll twice ensures
// that it was able to issue callbacks for the first poll.
- void WaitForDataAndCallbacksIssued(GamepadHardwareBuffer* buffer) {
+ void WaitForDataAndCallbacksIssued(const GamepadHardwareBuffer* buffer) {
WaitForData(buffer);
WaitForData(buffer);
}
- void ReadGamepadHardwareBuffer(GamepadHardwareBuffer* buffer,
+ void ReadGamepadHardwareBuffer(const GamepadHardwareBuffer* buffer,
Gamepads* output) {
memset(output, 0, sizeof(Gamepads));
base::subtle::Atomic32 version;
@@ -110,13 +110,13 @@ TEST_F(GamepadProviderTest, PollingAccess) {
base::RunLoop().RunUntilIdle();
// Renderer-side, pull data out of poll buffer.
- base::SharedMemoryHandle handle = provider->DuplicateSharedMemoryHandle();
- std::unique_ptr<base::SharedMemory> shared_memory(
- new base::SharedMemory(handle, true));
- EXPECT_TRUE(shared_memory->Map(sizeof(GamepadHardwareBuffer)));
+ base::ReadOnlySharedMemoryRegion region =
+ provider->DuplicateSharedMemoryRegion();
+ base::ReadOnlySharedMemoryMapping mapping = region.Map();
+ EXPECT_TRUE(mapping.IsValid());
- GamepadHardwareBuffer* buffer =
- static_cast<GamepadHardwareBuffer*>(shared_memory->memory());
+ const GamepadHardwareBuffer* buffer =
+ static_cast<const GamepadHardwareBuffer*>(mapping.memory());
// Wait until the shared memory buffer has been written at least once.
WaitForData(buffer);
@@ -160,13 +160,13 @@ TEST_F(GamepadProviderTest, ConnectDisconnectMultiple) {
base::RunLoop().RunUntilIdle();
// Renderer-side, pull data out of poll buffer.
- base::SharedMemoryHandle handle = provider->DuplicateSharedMemoryHandle();
- std::unique_ptr<base::SharedMemory> shared_memory(
- new base::SharedMemory(handle, true));
- EXPECT_TRUE(shared_memory->Map(sizeof(GamepadHardwareBuffer)));
+ base::ReadOnlySharedMemoryRegion region =
+ provider->DuplicateSharedMemoryRegion();
+ base::ReadOnlySharedMemoryMapping mapping = region.Map();
+ EXPECT_TRUE(mapping.IsValid());
- GamepadHardwareBuffer* buffer =
- static_cast<GamepadHardwareBuffer*>(shared_memory->memory());
+ const GamepadHardwareBuffer* buffer =
+ static_cast<const GamepadHardwareBuffer*>(mapping.memory());
// Wait until the shared memory buffer has been written at least once.
WaitForData(buffer);
@@ -219,13 +219,13 @@ TEST_F(GamepadProviderTest, UserGesture) {
base::RunLoop().RunUntilIdle();
// Renderer-side, pull data out of poll buffer.
- base::SharedMemoryHandle handle = provider->DuplicateSharedMemoryHandle();
- std::unique_ptr<base::SharedMemory> shared_memory(
- new base::SharedMemory(handle, true));
- EXPECT_TRUE(shared_memory->Map(sizeof(GamepadHardwareBuffer)));
+ base::ReadOnlySharedMemoryRegion region =
+ provider->DuplicateSharedMemoryRegion();
+ base::ReadOnlySharedMemoryMapping mapping = region.Map();
+ EXPECT_TRUE(mapping.IsValid());
- GamepadHardwareBuffer* buffer =
- static_cast<GamepadHardwareBuffer*>(shared_memory->memory());
+ const GamepadHardwareBuffer* buffer =
+ static_cast<const GamepadHardwareBuffer*>(mapping.memory());
// Wait until the shared memory buffer has been written at least once.
WaitForData(buffer);
@@ -273,13 +273,13 @@ TEST_F(GamepadProviderTest, Sanitization) {
base::RunLoop().RunUntilIdle();
// Renderer-side, pull data out of poll buffer.
- base::SharedMemoryHandle handle = provider->DuplicateSharedMemoryHandle();
- std::unique_ptr<base::SharedMemory> shared_memory(
- new base::SharedMemory(handle, true));
- EXPECT_TRUE(shared_memory->Map(sizeof(GamepadHardwareBuffer)));
+ base::ReadOnlySharedMemoryRegion region =
+ provider->DuplicateSharedMemoryRegion();
+ base::ReadOnlySharedMemoryMapping mapping = region.Map();
+ EXPECT_TRUE(mapping.IsValid());
- GamepadHardwareBuffer* buffer =
- static_cast<GamepadHardwareBuffer*>(shared_memory->memory());
+ const GamepadHardwareBuffer* buffer =
+ static_cast<const GamepadHardwareBuffer*>(mapping.memory());
// Wait until the shared memory buffer has been written at least once.
WaitForData(buffer);
diff --git a/chromium/device/gamepad/gamepad_service.cc b/chromium/device/gamepad/gamepad_service.cc
index f8511732c40..5eab297246d 100644
--- a/chromium/device/gamepad/gamepad_service.cc
+++ b/chromium/device/gamepad/gamepad_service.cc
@@ -200,14 +200,9 @@ void GamepadService::ResetVibrationActuator(
provider_->ResetVibrationActuator(pad_index, std::move(callback));
}
-base::SharedMemoryHandle GamepadService::DuplicateSharedMemoryHandle() {
+base::ReadOnlySharedMemoryRegion GamepadService::DuplicateSharedMemoryRegion() {
DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
- return provider_->DuplicateSharedMemoryHandle();
-}
-
-mojo::ScopedSharedBufferHandle GamepadService::GetSharedBufferHandle() {
- DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
- return provider_->GetSharedBufferHandle();
+ return provider_->DuplicateSharedMemoryRegion();
}
void GamepadService::OnUserGesture() {
diff --git a/chromium/device/gamepad/gamepad_service.h b/chromium/device/gamepad/gamepad_service.h
index 0d01419b3e6..74857864674 100644
--- a/chromium/device/gamepad/gamepad_service.h
+++ b/chromium/device/gamepad/gamepad_service.h
@@ -70,11 +70,8 @@ class DEVICE_GAMEPAD_EXPORT GamepadService
// while a consumer is active.
void RegisterForUserGesture(const base::Closure& closure);
- // Returns a duplicate of the shared memory handle of the gamepad data.
- base::SharedMemoryHandle DuplicateSharedMemoryHandle();
-
- // Returns a new mojo::ScopedSharedBuffer handle of the gamepad data.
- mojo::ScopedSharedBufferHandle GetSharedBufferHandle();
+ // Returns a duplicate of the shared memory region of the gamepad data.
+ base::ReadOnlySharedMemoryRegion DuplicateSharedMemoryRegion();
// Stop/join with the background thread in GamepadProvider |provider_|.
void Terminate();
diff --git a/chromium/device/gamepad/gamepad_shared_buffer.cc b/chromium/device/gamepad/gamepad_shared_buffer.cc
index 7d686acba9c..d5cbe96a9d6 100644
--- a/chromium/device/gamepad/gamepad_shared_buffer.cc
+++ b/chromium/device/gamepad/gamepad_shared_buffer.cc
@@ -7,13 +7,13 @@
namespace device {
GamepadSharedBuffer::GamepadSharedBuffer() {
- base::SharedMemoryCreateOptions options;
- options.size = sizeof(GamepadHardwareBuffer);
- options.share_read_only = true;
- bool res = shared_memory_.Create(options) && shared_memory_.Map(options.size);
- CHECK(res);
+ base::MappedReadOnlyRegion mapped_region =
+ base::ReadOnlySharedMemoryRegion::Create(sizeof(GamepadHardwareBuffer));
+ CHECK(mapped_region.IsValid());
+ shared_memory_region_ = std::move(mapped_region.region);
+ shared_memory_mapping_ = std::move(mapped_region.mapping);
- void* mem = shared_memory_.memory();
+ void* mem = shared_memory_mapping_.memory();
DCHECK(mem);
hardware_buffer_ = new (mem) GamepadHardwareBuffer();
memset(&(hardware_buffer_->data), 0, sizeof(Gamepads));
@@ -21,8 +21,9 @@ GamepadSharedBuffer::GamepadSharedBuffer() {
GamepadSharedBuffer::~GamepadSharedBuffer() = default;
-base::SharedMemory* GamepadSharedBuffer::shared_memory() {
- return &shared_memory_;
+base::ReadOnlySharedMemoryRegion
+GamepadSharedBuffer::DuplicateSharedMemoryRegion() {
+ return shared_memory_region_.Duplicate();
}
Gamepads* GamepadSharedBuffer::buffer() {
diff --git a/chromium/device/gamepad/gamepad_shared_buffer.h b/chromium/device/gamepad/gamepad_shared_buffer.h
index 1eb7b933795..d17643a29f0 100644
--- a/chromium/device/gamepad/gamepad_shared_buffer.h
+++ b/chromium/device/gamepad/gamepad_shared_buffer.h
@@ -5,10 +5,10 @@
#ifndef DEVICE_GAMEPAD_SHARED_BUFFER_H_
#define DEVICE_GAMEPAD_SHARED_BUFFER_H_
-#include "base/memory/shared_memory.h"
-#include "device/base/synchronization/shared_memory_seqlock_buffer.h"
+#include "base/memory/read_only_shared_memory_region.h"
#include "device/gamepad/gamepad_export.h"
#include "device/gamepad/public/cpp/gamepads.h"
+#include "device/gamepad/public/mojom/gamepad_hardware_buffer.h"
namespace device {
@@ -25,14 +25,12 @@ namespace device {
*/
-typedef SharedMemorySeqLockBuffer<Gamepads> GamepadHardwareBuffer;
-
class DEVICE_GAMEPAD_EXPORT GamepadSharedBuffer {
public:
GamepadSharedBuffer();
~GamepadSharedBuffer();
- base::SharedMemory* shared_memory();
+ base::ReadOnlySharedMemoryRegion DuplicateSharedMemoryRegion();
Gamepads* buffer();
GamepadHardwareBuffer* hardware_buffer();
@@ -40,7 +38,8 @@ class DEVICE_GAMEPAD_EXPORT GamepadSharedBuffer {
void WriteEnd();
private:
- base::SharedMemory shared_memory_;
+ base::ReadOnlySharedMemoryRegion shared_memory_region_;
+ base::WritableSharedMemoryMapping shared_memory_mapping_;
GamepadHardwareBuffer* hardware_buffer_;
};
diff --git a/chromium/device/gamepad/gamepad_standard_mappings_linux.cc b/chromium/device/gamepad/gamepad_standard_mappings_linux.cc
index 7077b6cca00..552f7901484 100644
--- a/chromium/device/gamepad/gamepad_standard_mappings_linux.cc
+++ b/chromium/device/gamepad/gamepad_standard_mappings_linux.cc
@@ -45,7 +45,7 @@ void MapperXInputStyleGamepad(const Gamepad& input, Gamepad* mapped) {
mapped->axes_length = AXIS_INDEX_COUNT;
}
-void MapperXboxOneS2016Firmware(const Gamepad& input, Gamepad* mapped) {
+void MapperXboxOneS(const Gamepad& input, Gamepad* mapped) {
*mapped = input;
mapped->buttons[BUTTON_INDEX_PRIMARY] = input.buttons[0];
mapped->buttons[BUTTON_INDEX_SECONDARY] = input.buttons[1];
@@ -64,13 +64,14 @@ void MapperXboxOneS2016Firmware(const Gamepad& input, Gamepad* mapped) {
mapped->buttons[BUTTON_INDEX_DPAD_LEFT] = AxisNegativeAsButton(input.axes[6]);
mapped->buttons[BUTTON_INDEX_DPAD_RIGHT] =
AxisPositiveAsButton(input.axes[6]);
+ mapped->buttons[BUTTON_INDEX_META] = input.buttons[10];
mapped->axes[AXIS_INDEX_RIGHT_STICK_X] = input.axes[3];
mapped->axes[AXIS_INDEX_RIGHT_STICK_Y] = input.axes[4];
- mapped->buttons_length = BUTTON_INDEX_COUNT - 1; /* no meta */
+ mapped->buttons_length = BUTTON_INDEX_COUNT;
mapped->axes_length = AXIS_INDEX_COUNT;
}
-void MapperXboxOneS(const Gamepad& input, Gamepad* mapped) {
+void MapperXboxOneS2016Firmware(const Gamepad& input, Gamepad* mapped) {
*mapped = input;
mapped->buttons[BUTTON_INDEX_PRIMARY] = input.buttons[0];
@@ -81,7 +82,7 @@ void MapperXboxOneS(const Gamepad& input, Gamepad* mapped) {
mapped->buttons[BUTTON_INDEX_RIGHT_SHOULDER] = input.buttons[7];
mapped->buttons[BUTTON_INDEX_LEFT_TRIGGER] = AxisToButton(input.axes[5]);
mapped->buttons[BUTTON_INDEX_RIGHT_TRIGGER] = AxisToButton(input.axes[4]);
- mapped->buttons[BUTTON_INDEX_BACK_SELECT] = NullButton();
+ mapped->buttons[BUTTON_INDEX_BACK_SELECT] = input.buttons[16];
mapped->buttons[BUTTON_INDEX_START] = input.buttons[11];
mapped->buttons[BUTTON_INDEX_LEFT_THUMBSTICK] = input.buttons[13];
mapped->buttons[BUTTON_INDEX_RIGHT_THUMBSTICK] = input.buttons[14];
@@ -90,9 +91,10 @@ void MapperXboxOneS(const Gamepad& input, Gamepad* mapped) {
mapped->buttons[BUTTON_INDEX_DPAD_LEFT] = AxisNegativeAsButton(input.axes[6]);
mapped->buttons[BUTTON_INDEX_DPAD_RIGHT] =
AxisPositiveAsButton(input.axes[6]);
+ mapped->buttons[BUTTON_INDEX_META] = input.buttons[15];
mapped->axes[AXIS_INDEX_RIGHT_STICK_Y] = input.axes[3];
- mapped->buttons_length = BUTTON_INDEX_COUNT - 1; /* no meta */
+ mapped->buttons_length = BUTTON_INDEX_COUNT;
mapped->axes_length = AXIS_INDEX_COUNT;
}
@@ -339,6 +341,30 @@ void MapperNvShield(const Gamepad& input, Gamepad* mapped) {
mapped->axes_length = AXIS_INDEX_COUNT;
}
+void MapperNvShield2017(const Gamepad& input, Gamepad* mapped) {
+ enum Shield2017Buttons {
+ SHIELD2017_BUTTON_PLAYPAUSE = BUTTON_INDEX_COUNT,
+ SHIELD2017_BUTTON_COUNT
+ };
+ *mapped = input;
+ mapped->buttons[BUTTON_INDEX_LEFT_TRIGGER] = AxisToButton(input.axes[5]);
+ mapped->buttons[BUTTON_INDEX_RIGHT_TRIGGER] = AxisToButton(input.axes[4]);
+ mapped->buttons[BUTTON_INDEX_BACK_SELECT] = input.buttons[14];
+ mapped->buttons[BUTTON_INDEX_START] = input.buttons[13];
+ mapped->buttons[BUTTON_INDEX_LEFT_THUMBSTICK] = input.buttons[7];
+ mapped->buttons[BUTTON_INDEX_RIGHT_THUMBSTICK] = input.buttons[8];
+ mapped->buttons[BUTTON_INDEX_DPAD_UP] = AxisNegativeAsButton(input.axes[7]);
+ mapped->buttons[BUTTON_INDEX_DPAD_DOWN] = AxisPositiveAsButton(input.axes[7]);
+ mapped->buttons[BUTTON_INDEX_DPAD_LEFT] = AxisNegativeAsButton(input.axes[6]);
+ mapped->buttons[BUTTON_INDEX_DPAD_RIGHT] =
+ AxisPositiveAsButton(input.axes[6]);
+ mapped->buttons[BUTTON_INDEX_META] = input.buttons[12];
+ mapped->buttons[SHIELD2017_BUTTON_PLAYPAUSE] = input.buttons[6];
+
+ mapped->buttons_length = SHIELD2017_BUTTON_COUNT;
+ mapped->axes_length = AXIS_INDEX_COUNT;
+}
+
void MapperOUYA(const Gamepad& input, Gamepad* mapped) {
*mapped = input;
mapped->buttons[BUTTON_INDEX_PRIMARY] = input.buttons[0];
@@ -521,10 +547,10 @@ struct MappingData {
{"045e", "0291", MapperXInputStyleGamepad}, // Xbox 360 Wireless
{"045e", "02d1", MapperXInputStyleGamepad}, // Xbox One Wired
{"045e", "02dd", MapperXInputStyleGamepad}, // Xbox One Wired (2015 FW)
- {"045e", "02e0", MapperXboxOneS2016Firmware}, // Xbox One S (Bluetooth)
+ {"045e", "02e0", MapperXboxOneS}, // Xbox One S (Bluetooth)
{"045e", "02e3", MapperXInputStyleGamepad}, // Xbox One Elite Wired
{"045e", "02ea", MapperXInputStyleGamepad}, // Xbox One S (USB)
- {"045e", "02fd", MapperXboxOneS}, // Xbox One S (Bluetooth)
+ {"045e", "02fd", MapperXboxOneS2016Firmware}, // Xbox One S (Bluetooth)
{"045e", "0719", MapperXInputStyleGamepad}, // Xbox 360 Wireless
{"046d", "c216", MapperLogitechDInput}, // Logitech F310 D-mode
{"046d", "c218", MapperLogitechDInput}, // Logitech F510 D-mode
@@ -541,7 +567,8 @@ struct MappingData {
{"0583", "2060", MapperIBuffalo}, // iBuffalo Classic
{"0925", "0005", MapperLakeviewResearch}, // SmartJoy PLUS Adapter
{"0925", "8866", MapperLakeviewResearch}, // WiseGroup MP-8866
- {"0955", "7210", MapperNvShield}, // Nvidia Shield gamepad
+ {"0955", "7210", MapperNvShield}, // Nvidia Shield gamepad (2015)
+ {"0955", "7214", MapperNvShield2017}, // Nvidia Shield gamepad (2017)
{"0b05", "4500", MapperADT1}, // Nexus Player Controller
{"0e8f", "0003", MapperXGEAR}, // XFXforce XGEAR PS2 Controller
{"1038", "1412", MapperSteelSeries}, // Zeemote: SteelSeries FREE
diff --git a/chromium/device/gamepad/gamepad_standard_mappings_mac.mm b/chromium/device/gamepad/gamepad_standard_mappings_mac.mm
index e1bad56af06..aa7d290c7eb 100644
--- a/chromium/device/gamepad/gamepad_standard_mappings_mac.mm
+++ b/chromium/device/gamepad/gamepad_standard_mappings_mac.mm
@@ -30,7 +30,7 @@ void MapperXbox360Gamepad(const Gamepad& input, Gamepad* mapped) {
mapped->axes_length = AXIS_INDEX_COUNT;
}
-void MapperXboxOneS2016Firmware(const Gamepad& input, Gamepad* mapped) {
+void MapperXboxOneS(const Gamepad& input, Gamepad* mapped) {
*mapped = input;
mapped->buttons[BUTTON_INDEX_PRIMARY] = input.buttons[0];
mapped->buttons[BUTTON_INDEX_SECONDARY] = input.buttons[1];
@@ -44,15 +44,16 @@ void MapperXboxOneS2016Firmware(const Gamepad& input, Gamepad* mapped) {
mapped->buttons[BUTTON_INDEX_START] = input.buttons[7];
mapped->buttons[BUTTON_INDEX_LEFT_THUMBSTICK] = input.buttons[8];
mapped->buttons[BUTTON_INDEX_RIGHT_THUMBSTICK] = input.buttons[9];
+ mapped->buttons[BUTTON_INDEX_META] = input.buttons[10];
mapped->axes[AXIS_INDEX_RIGHT_STICK_X] = input.axes[3];
mapped->axes[AXIS_INDEX_RIGHT_STICK_Y] = input.axes[4];
DpadFromAxis(mapped, input.axes[9]);
- mapped->buttons_length = BUTTON_INDEX_COUNT - 1; /* no meta */
+ mapped->buttons_length = BUTTON_INDEX_COUNT;
mapped->axes_length = AXIS_INDEX_COUNT;
}
-void MapperXboxOneS(const Gamepad& input, Gamepad* mapped) {
+void MapperXboxOneS2016Firmware(const Gamepad& input, Gamepad* mapped) {
*mapped = input;
mapped->buttons[BUTTON_INDEX_PRIMARY] = input.buttons[0];
@@ -63,14 +64,15 @@ void MapperXboxOneS(const Gamepad& input, Gamepad* mapped) {
mapped->buttons[BUTTON_INDEX_RIGHT_SHOULDER] = input.buttons[7];
mapped->buttons[BUTTON_INDEX_LEFT_TRIGGER] = AxisToButton(input.axes[3]);
mapped->buttons[BUTTON_INDEX_RIGHT_TRIGGER] = AxisToButton(input.axes[4]);
- mapped->buttons[BUTTON_INDEX_BACK_SELECT] = NullButton();
+ mapped->buttons[BUTTON_INDEX_BACK_SELECT] = input.buttons[16];
mapped->buttons[BUTTON_INDEX_START] = input.buttons[11];
mapped->buttons[BUTTON_INDEX_LEFT_THUMBSTICK] = input.buttons[13];
mapped->buttons[BUTTON_INDEX_RIGHT_THUMBSTICK] = input.buttons[14];
+ mapped->buttons[BUTTON_INDEX_META] = input.buttons[15];
mapped->axes[AXIS_INDEX_RIGHT_STICK_Y] = input.axes[5];
DpadFromAxis(mapped, input.axes[9]);
- mapped->buttons_length = BUTTON_INDEX_COUNT - 1; /* no meta */
+ mapped->buttons_length = BUTTON_INDEX_COUNT;
mapped->axes_length = AXIS_INDEX_COUNT;
}
@@ -296,6 +298,10 @@ void MapperADT1(const Gamepad& input, Gamepad* mapped) {
}
void MapperNvShield(const Gamepad& input, Gamepad* mapped) {
+ enum ShieldButtons {
+ SHIELD_BUTTON_CIRCLE = BUTTON_INDEX_COUNT,
+ SHIELD_BUTTON_COUNT
+ };
*mapped = input;
mapped->buttons[BUTTON_INDEX_PRIMARY] = input.buttons[0];
mapped->buttons[BUTTON_INDEX_SECONDARY] = input.buttons[1];
@@ -305,15 +311,16 @@ void MapperNvShield(const Gamepad& input, Gamepad* mapped) {
mapped->buttons[BUTTON_INDEX_RIGHT_SHOULDER] = input.buttons[7];
mapped->buttons[BUTTON_INDEX_LEFT_TRIGGER] = AxisToButton(input.axes[3]);
mapped->buttons[BUTTON_INDEX_RIGHT_TRIGGER] = AxisToButton(input.axes[4]);
- mapped->buttons[BUTTON_INDEX_BACK_SELECT] = NullButton();
+ mapped->buttons[BUTTON_INDEX_BACK_SELECT] = input.buttons[9];
mapped->buttons[BUTTON_INDEX_START] = input.buttons[11];
mapped->buttons[BUTTON_INDEX_LEFT_THUMBSTICK] = input.buttons[13];
mapped->buttons[BUTTON_INDEX_RIGHT_THUMBSTICK] = input.buttons[14];
- mapped->buttons[BUTTON_INDEX_META] = input.buttons[8];
+ mapped->buttons[BUTTON_INDEX_META] = input.buttons[2];
+ mapped->buttons[SHIELD_BUTTON_CIRCLE] = input.buttons[5];
mapped->axes[AXIS_INDEX_RIGHT_STICK_Y] = input.axes[5];
DpadFromAxis(mapped, input.axes[9]);
- mapped->buttons_length = BUTTON_INDEX_COUNT;
+ mapped->buttons_length = SHIELD_BUTTON_COUNT;
mapped->axes_length = AXIS_INDEX_COUNT;
}
@@ -396,10 +403,10 @@ struct MappingData {
{"045e", "028f", MapperXbox360Gamepad}, // Xbox 360 Wireless
{"045e", "02d1", MapperXbox360Gamepad}, // Xbox One Wired
{"045e", "02dd", MapperXbox360Gamepad}, // Xbox One Wired (2015 FW)
- {"045e", "02e0", MapperXboxOneS2016Firmware}, // Xbox One S (Bluetooth)
+ {"045e", "02e0", MapperXboxOneS}, // Xbox One S (Bluetooth)
{"045e", "02e3", MapperXbox360Gamepad}, // Xbox One Elite Wired
{"045e", "02ea", MapperXbox360Gamepad}, // Xbox One S (USB)
- {"045e", "02fd", MapperXboxOneS}, // Xbox One S (Bluetooth)
+ {"045e", "02fd", MapperXboxOneS2016Firmware}, // Xbox One S (Bluetooth)
{"045e", "0719", MapperXbox360Gamepad}, // Xbox 360 Wireless
{"046d", "c216", MapperDirectInputStyle}, // Logitech F310, D mode
{"046d", "c218", MapperDirectInputStyle}, // Logitech F510, D mode
@@ -410,7 +417,7 @@ struct MappingData {
{"054c", "0ba0", MapperDualshock4}, // Dualshock 4 USB receiver
{"0583", "2060", MapperIBuffalo}, // iBuffalo Classic
{"0925", "0005", MapperSmartJoyPLUS}, // SmartJoy PLUS Adapter
- {"0955", "7210", MapperNvShield}, // Nvidia Shield gamepad
+ {"0955", "7210", MapperNvShield}, // Nvidia Shield gamepad (2015)
{"0b05", "4500", MapperADT1}, // Nexus Player Controller
{"0e8f", "0003", MapperXGEAR}, // XFXforce XGEAR PS2 Controller
{"1532", "0900", MapperRazerServal}, // Razer Serval Controller
diff --git a/chromium/device/gamepad/gamepad_standard_mappings_win.cc b/chromium/device/gamepad/gamepad_standard_mappings_win.cc
index 6d4a33e4e35..c265ca86cda 100644
--- a/chromium/device/gamepad/gamepad_standard_mappings_win.cc
+++ b/chromium/device/gamepad/gamepad_standard_mappings_win.cc
@@ -141,6 +141,35 @@ void MapperADT1(const Gamepad& input, Gamepad* mapped) {
}
void MapperNvShield(const Gamepad& input, Gamepad* mapped) {
+ enum ShieldButtons {
+ SHIELD_BUTTON_CIRCLE = BUTTON_INDEX_COUNT,
+ SHIELD_BUTTON_COUNT
+ };
+ *mapped = input;
+ mapped->buttons[BUTTON_INDEX_TERTIARY] = input.buttons[3];
+ mapped->buttons[BUTTON_INDEX_QUATERNARY] = input.buttons[4];
+ mapped->buttons[BUTTON_INDEX_LEFT_SHOULDER] = input.buttons[6];
+ mapped->buttons[BUTTON_INDEX_RIGHT_SHOULDER] = input.buttons[7];
+ mapped->buttons[BUTTON_INDEX_LEFT_TRIGGER] = AxisToButton(input.axes[4]);
+ mapped->buttons[BUTTON_INDEX_RIGHT_TRIGGER] = AxisToButton(input.axes[3]);
+ mapped->buttons[BUTTON_INDEX_BACK_SELECT] = input.buttons[9];
+ mapped->buttons[BUTTON_INDEX_START] = input.buttons[11];
+ mapped->buttons[BUTTON_INDEX_LEFT_THUMBSTICK] = input.buttons[13];
+ mapped->buttons[BUTTON_INDEX_RIGHT_THUMBSTICK] = input.buttons[14];
+ mapped->buttons[BUTTON_INDEX_META] = input.buttons[2];
+ mapped->buttons[SHIELD_BUTTON_CIRCLE] = input.buttons[5];
+ mapped->axes[AXIS_INDEX_RIGHT_STICK_Y] = input.axes[5];
+ DpadFromAxis(mapped, input.axes[9]);
+
+ mapped->buttons_length = SHIELD_BUTTON_COUNT;
+ mapped->axes_length = AXIS_INDEX_COUNT;
+}
+
+void MapperNvShield2017(const Gamepad& input, Gamepad* mapped) {
+ enum Shield2017Buttons {
+ SHIELD2017_BUTTON_PLAYPAUSE = BUTTON_INDEX_COUNT,
+ SHIELD2017_BUTTON_COUNT
+ };
*mapped = input;
mapped->buttons[BUTTON_INDEX_PRIMARY] = input.buttons[0];
mapped->buttons[BUTTON_INDEX_SECONDARY] = input.buttons[1];
@@ -150,15 +179,16 @@ void MapperNvShield(const Gamepad& input, Gamepad* mapped) {
mapped->buttons[BUTTON_INDEX_RIGHT_SHOULDER] = input.buttons[7];
mapped->buttons[BUTTON_INDEX_LEFT_TRIGGER] = AxisToButton(input.axes[4]);
mapped->buttons[BUTTON_INDEX_RIGHT_TRIGGER] = AxisToButton(input.axes[3]);
- mapped->buttons[BUTTON_INDEX_BACK_SELECT] = NullButton();
- mapped->buttons[BUTTON_INDEX_START] = input.buttons[11];
+ mapped->buttons[BUTTON_INDEX_BACK_SELECT] = input.buttons[8];
+ mapped->buttons[BUTTON_INDEX_START] = input.buttons[5];
mapped->buttons[BUTTON_INDEX_LEFT_THUMBSTICK] = input.buttons[13];
mapped->buttons[BUTTON_INDEX_RIGHT_THUMBSTICK] = input.buttons[14];
- mapped->buttons[BUTTON_INDEX_META] = input.buttons[8];
+ mapped->buttons[BUTTON_INDEX_META] = input.buttons[2];
+ mapped->buttons[SHIELD2017_BUTTON_PLAYPAUSE] = input.buttons[11];
mapped->axes[AXIS_INDEX_RIGHT_STICK_Y] = input.axes[5];
DpadFromAxis(mapped, input.axes[9]);
- mapped->buttons_length = BUTTON_INDEX_COUNT;
+ mapped->buttons_length = SHIELD2017_BUTTON_COUNT;
mapped->axes_length = AXIS_INDEX_COUNT;
}
@@ -244,7 +274,8 @@ struct MappingData {
{"054c", "09cc", MapperDualshock4}, // Dualshock 4 (PS4 Slim)
{"054c", "0ba0", MapperDualshock4}, // Dualshock 4 USB receiver
{"0583", "2060", MapperIBuffalo}, // iBuffalo Classic
- {"0955", "7210", MapperNvShield}, // Nvidia Shield gamepad
+ {"0955", "7210", MapperNvShield}, // Nvidia Shield gamepad (2015)
+ {"0955", "7214", MapperNvShield2017}, // Nvidia Shield gamepad (2017)
{"0b05", "4500", MapperADT1}, // Nexus Player Controller
{"1532", "0900", MapperRazerServal}, // Razer Serval Controller
{"18d1", "2c40", MapperADT1}, // ADT-1 Controller
diff --git a/chromium/device/gamepad/gamepad_user_gesture.cc b/chromium/device/gamepad/gamepad_user_gesture.cc
index b4da5e099c4..6ae7a31421b 100644
--- a/chromium/device/gamepad/gamepad_user_gesture.cc
+++ b/chromium/device/gamepad/gamepad_user_gesture.cc
@@ -24,6 +24,15 @@ bool GamepadsHaveUserGesture(const Gamepads& gamepads) {
// If the device is physically connected, then check the buttons and axes
// to see if there is currently an intentional user action.
if (pad.connected) {
+ // Only VR Controllers have a display id, and are only reported as
+ // connected during WebVR presentation, so the user is definitely
+ // expecting their controller to be used. Note that this will also
+ // satisfy the gesture requirement for all other connected controllers,
+ // exposing them too. This is unfortunate, but worth the tradeoff and will
+ // go away in the future when WebVR is fully replaced with WebXR.
+ if (pad.display_id != 0)
+ return true;
+
for (unsigned int button_index = 0; button_index < pad.buttons_length;
button_index++) {
if (pad.buttons[button_index].pressed)
diff --git a/chromium/device/gamepad/public/cpp/BUILD.gn b/chromium/device/gamepad/public/cpp/BUILD.gn
index fdec85b8d42..26af5f7d3fd 100644
--- a/chromium/device/gamepad/public/cpp/BUILD.gn
+++ b/chromium/device/gamepad/public/cpp/BUILD.gn
@@ -21,3 +21,36 @@ source_set("shared_with_blink") {
]
# Do not add deps here per the above comment.
}
+
+# Normally typemap traits sources should be build directly into mojom targets
+# via the typemap file. This target is for typemapped types whose
+# traits are shared between chromium and blink variants.
+component("shared_typemap_traits") {
+ output_name = "gamepad_shared_typemap_traits"
+
+ sources = [
+ "gamepad_mojom_traits.cc",
+ "gamepad_mojom_traits.h",
+ ]
+
+ defines = [ "IS_GAMEPAD_SHARED_TRAITS_IMPL" ]
+
+ public_deps = [
+ ":shared_with_blink",
+ "//base",
+ "//device/gamepad/public/mojom:mojom_shared",
+ ]
+}
+
+static_library("switches") {
+ sources = [
+ "gamepad_features.cc",
+ "gamepad_features.h",
+ "gamepad_switches.cc",
+ "gamepad_switches.h",
+ ]
+
+ public_deps = [
+ "//base",
+ ]
+}
diff --git a/chromium/device/gamepad/public/cpp/OWNERS b/chromium/device/gamepad/public/cpp/OWNERS
new file mode 100644
index 00000000000..7aebc8abbf8
--- /dev/null
+++ b/chromium/device/gamepad/public/cpp/OWNERS
@@ -0,0 +1,4 @@
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/chromium/device/gamepad/public/cpp/gamepad.h b/chromium/device/gamepad/public/cpp/gamepad.h
index 055bc2d7732..93af42e0892 100644
--- a/chromium/device/gamepad/public/cpp/gamepad.h
+++ b/chromium/device/gamepad/public/cpp/gamepad.h
@@ -6,6 +6,7 @@
#define DEVICE_GAMEPAD_PUBLIC_CPP_GAMEPAD_H_
#include <stddef.h>
+#include <cstdint>
namespace device {
@@ -112,9 +113,9 @@ class Gamepad {
// Device identifier (based on manufacturer, model, etc.).
UChar id[kIdLengthCap];
- // Monotonically increasing value referring to when the data were last
- // updated.
- unsigned long long timestamp;
+ // Time value representing the last time the data for this gamepad was
+ // updated. Measured as TimeTicks::Now().since_origin().InMicroseconds().
+ int64_t timestamp;
// Number of valid entries in the axes array.
unsigned axes_length;
diff --git a/chromium/device/gamepad/public/mojom/gamepad.typemap b/chromium/device/gamepad/public/cpp/gamepad.typemap
index 866878a47ed..c6db1b9b8c6 100644
--- a/chromium/device/gamepad/public/mojom/gamepad.typemap
+++ b/chromium/device/gamepad/public/cpp/gamepad.typemap
@@ -4,16 +4,17 @@
mojom = "//device/gamepad/public/mojom/gamepad.mojom"
public_headers = [ "//device/gamepad/public/cpp/gamepad.h" ]
-traits_headers = [ "//device/gamepad/public/mojom/gamepad_mojom_traits.h" ]
-sources = [
- "//device/gamepad/public/mojom/gamepad_mojom_traits.cc",
-]
+traits_headers = [ "//device/gamepad/public/cpp/gamepad_mojom_traits.h" ]
deps = [
"//device/gamepad/public/cpp:shared_with_blink",
"//mojo/public/cpp/bindings",
"//third_party/blink/public:blink_headers",
]
+public_deps = [
+ "//device/gamepad/public/cpp:shared_typemap_traits",
+]
+
# Use typemaps for device::Gamepad and its member types. Gamepad data is
# intentionally POD and fixed size so that it can be stored in shared memory
# between hardware polling threads and the rest of the browser.
diff --git a/chromium/device/gamepad/public/cpp/gamepad_features.cc b/chromium/device/gamepad/public/cpp/gamepad_features.cc
new file mode 100644
index 00000000000..75dfa028628
--- /dev/null
+++ b/chromium/device/gamepad/public/cpp/gamepad_features.cc
@@ -0,0 +1,63 @@
+// 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/gamepad_features.h"
+
+#include <algorithm>
+
+#include "base/command_line.h"
+#include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "device/gamepad/public/cpp/gamepad_switches.h"
+
+namespace features {
+
+namespace {
+
+const size_t kPollingIntervalMillisecondsMin = 4; // ~250 Hz
+const size_t kPollingIntervalMillisecondsMax = 16; // ~62.5 Hz
+
+size_t OverrideIntervalIfValid(base::StringPiece param_value,
+ size_t default_interval) {
+ size_t interval;
+ if (param_value.empty() || !base::StringToSizeT(param_value, &interval))
+ return default_interval;
+ // Clamp interval duration to valid range.
+ interval = std::max(interval, kPollingIntervalMillisecondsMin);
+ interval = std::min(interval, kPollingIntervalMillisecondsMax);
+ return interval;
+}
+
+} // namespace
+
+const base::Feature kGamepadPollingInterval{"GamepadPollingInterval",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+const char kGamepadPollingIntervalParamKey[] = "interval-ms";
+
+size_t GetGamepadPollingInterval() {
+ size_t polling_interval = kPollingIntervalMillisecondsMax;
+
+ // Check if the polling interval is overridden by a field trial.
+ if (base::FeatureList::IsEnabled(kGamepadPollingInterval)) {
+ std::string param_value = base::GetFieldTrialParamValueByFeature(
+ kGamepadPollingInterval, kGamepadPollingIntervalParamKey);
+ polling_interval = OverrideIntervalIfValid(param_value, polling_interval);
+ }
+
+ // Check if the polling interval is overridden by a command-line flag.
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ if (command_line &&
+ command_line->HasSwitch(switches::kGamepadPollingInterval)) {
+ std::string switch_value =
+ command_line->GetSwitchValueASCII(switches::kGamepadPollingInterval);
+ polling_interval = OverrideIntervalIfValid(switch_value, polling_interval);
+ }
+
+ return polling_interval;
+}
+
+} // namespace features
diff --git a/chromium/device/gamepad/public/cpp/gamepad_features.h b/chromium/device/gamepad/public/cpp/gamepad_features.h
new file mode 100644
index 00000000000..462c501ec6d
--- /dev/null
+++ b/chromium/device/gamepad/public/cpp/gamepad_features.h
@@ -0,0 +1,19 @@
+// 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_GAMEPAD_PUBLIC_CPP_GAMEPAD_FEATURES_H_
+#define DEVICE_GAMEPAD_PUBLIC_CPP_GAMEPAD_FEATURES_H_
+
+#include "base/feature_list.h"
+
+namespace features {
+
+extern const base::Feature kGamepadPollingInterval;
+extern const char kGamepadPollingIntervalParamKey[];
+
+size_t GetGamepadPollingInterval();
+
+} // namespace features
+
+#endif // DEVICE_GAMEPAD_PUBLIC_CPP_GAMEPAD_FEATURES_H_
diff --git a/chromium/device/gamepad/public/mojom/gamepad_mojom_traits.cc b/chromium/device/gamepad/public/cpp/gamepad_mojom_traits.cc
index dacde6ebf1a..0431aae9c99 100644
--- a/chromium/device/gamepad/public/mojom/gamepad_mojom_traits.cc
+++ b/chromium/device/gamepad/public/cpp/gamepad_mojom_traits.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/gamepad/public/mojom/gamepad_mojom_traits.h"
+#include "device/gamepad/public/cpp/gamepad_mojom_traits.h"
#include "base/containers/span.h"
diff --git a/chromium/device/gamepad/public/mojom/gamepad_mojom_traits.h b/chromium/device/gamepad/public/cpp/gamepad_mojom_traits.h
index 234dbd959b7..3092b1c1539 100644
--- a/chromium/device/gamepad/public/mojom/gamepad_mojom_traits.h
+++ b/chromium/device/gamepad/public/cpp/gamepad_mojom_traits.h
@@ -2,22 +2,24 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef DEVICE_GAMEPAD_PUBLIC_MOJOM_GAMEPAD_MOJOM_TRAITS_H_
-#define DEVICE_GAMEPAD_PUBLIC_MOJOM_GAMEPAD_MOJOM_TRAITS_H_
+#ifndef DEVICE_GAMEPAD_PUBLIC_CPP_GAMEPAD_MOJOM_TRAITS_H_
+#define DEVICE_GAMEPAD_PUBLIC_CPP_GAMEPAD_MOJOM_TRAITS_H_
#include <stddef.h>
+#include "base/component_export.h"
#include "base/containers/span.h"
#include "device/gamepad/public/cpp/gamepad.h"
-#include "device/gamepad/public/mojom/gamepad.mojom.h"
+#include "device/gamepad/public/mojom/gamepad.mojom-shared.h"
#include "mojo/public/cpp/bindings/array_traits_span.h"
#include "mojo/public/cpp/bindings/struct_traits.h"
namespace mojo {
template <>
-struct StructTraits<device::mojom::GamepadQuaternionDataView,
- device::GamepadQuaternion> {
+struct COMPONENT_EXPORT(GAMEPAD_SHARED_TRAITS)
+ StructTraits<device::mojom::GamepadQuaternionDataView,
+ device::GamepadQuaternion> {
static bool IsNull(const device::GamepadQuaternion& r) { return !r.not_null; }
static void SetToNull(device::GamepadQuaternion* out);
static float x(const device::GamepadQuaternion& r) { return r.x; }
@@ -29,8 +31,8 @@ struct StructTraits<device::mojom::GamepadQuaternionDataView,
};
template <>
-struct StructTraits<device::mojom::GamepadVectorDataView,
- device::GamepadVector> {
+struct COMPONENT_EXPORT(GAMEPAD_SHARED_TRAITS)
+ StructTraits<device::mojom::GamepadVectorDataView, device::GamepadVector> {
static bool IsNull(const device::GamepadVector& r) { return !r.not_null; }
static void SetToNull(device::GamepadVector* out);
static float x(const device::GamepadVector& r) { return r.x; }
@@ -41,8 +43,8 @@ struct StructTraits<device::mojom::GamepadVectorDataView,
};
template <>
-struct StructTraits<device::mojom::GamepadButtonDataView,
- device::GamepadButton> {
+struct COMPONENT_EXPORT(GAMEPAD_SHARED_TRAITS)
+ StructTraits<device::mojom::GamepadButtonDataView, device::GamepadButton> {
static bool pressed(const device::GamepadButton& r) { return r.pressed; }
static bool touched(const device::GamepadButton& r) { return r.touched; }
static double value(const device::GamepadButton& r) { return r.value; }
@@ -51,8 +53,9 @@ struct StructTraits<device::mojom::GamepadButtonDataView,
};
template <>
-struct EnumTraits<device::mojom::GamepadHapticActuatorType,
- device::GamepadHapticActuatorType> {
+struct COMPONENT_EXPORT(GAMEPAD_SHARED_TRAITS)
+ EnumTraits<device::mojom::GamepadHapticActuatorType,
+ device::GamepadHapticActuatorType> {
static device::mojom::GamepadHapticActuatorType ToMojom(
device::GamepadHapticActuatorType input);
static bool FromMojom(device::mojom::GamepadHapticActuatorType input,
@@ -60,8 +63,9 @@ struct EnumTraits<device::mojom::GamepadHapticActuatorType,
};
template <>
-struct StructTraits<device::mojom::GamepadHapticActuatorDataView,
- device::GamepadHapticActuator> {
+struct COMPONENT_EXPORT(GAMEPAD_SHARED_TRAITS)
+ StructTraits<device::mojom::GamepadHapticActuatorDataView,
+ device::GamepadHapticActuator> {
static bool IsNull(const device::GamepadHapticActuator& r) {
return !r.not_null;
}
@@ -75,7 +79,8 @@ struct StructTraits<device::mojom::GamepadHapticActuatorDataView,
};
template <>
-struct StructTraits<device::mojom::GamepadPoseDataView, device::GamepadPose> {
+struct COMPONENT_EXPORT(GAMEPAD_SHARED_TRAITS)
+ StructTraits<device::mojom::GamepadPoseDataView, device::GamepadPose> {
static bool IsNull(const device::GamepadPose& r) { return !r.not_null; }
static void SetToNull(device::GamepadPose* out);
static const device::GamepadQuaternion& orientation(
@@ -106,16 +111,18 @@ struct StructTraits<device::mojom::GamepadPoseDataView, device::GamepadPose> {
};
template <>
-struct EnumTraits<device::mojom::GamepadHand, device::GamepadHand> {
+struct COMPONENT_EXPORT(GAMEPAD_SHARED_TRAITS)
+ EnumTraits<device::mojom::GamepadHand, device::GamepadHand> {
static device::mojom::GamepadHand ToMojom(device::GamepadHand input);
static bool FromMojom(device::mojom::GamepadHand input,
device::GamepadHand* output);
};
template <>
-struct StructTraits<device::mojom::GamepadDataView, device::Gamepad> {
+struct COMPONENT_EXPORT(GAMEPAD_SHARED_TRAITS)
+ StructTraits<device::mojom::GamepadDataView, device::Gamepad> {
static bool connected(const device::Gamepad& r) { return r.connected; }
- static uint64_t timestamp(const device::Gamepad& r) { return r.timestamp; }
+ static int64_t timestamp(const device::Gamepad& r) { return r.timestamp; }
static base::span<const double> axes(const device::Gamepad& r) {
return base::make_span(r.axes, r.axes_length);
}
@@ -142,4 +149,4 @@ struct StructTraits<device::mojom::GamepadDataView, device::Gamepad> {
} // namespace mojo
-#endif // DEVICE_GAMEPAD_PUBLIC_MOJOM_GAMEPAD_MOJOM_TRAITS_H_
+#endif // DEVICE_GAMEPAD_PUBLIC_CPP_GAMEPAD_MOJOM_TRAITS_H_
diff --git a/chromium/device/gamepad/public/cpp/gamepad_switches.cc b/chromium/device/gamepad/public/cpp/gamepad_switches.cc
new file mode 100644
index 00000000000..1dd28a0717b
--- /dev/null
+++ b/chromium/device/gamepad/public/cpp/gamepad_switches.cc
@@ -0,0 +1,14 @@
+// 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/gamepad_switches.h"
+
+namespace switches {
+
+// 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.
+const char kGamepadPollingInterval[] = "gamepad-polling-interval";
+
+} // namespace switches
diff --git a/chromium/device/gamepad/public/cpp/gamepad_switches.h b/chromium/device/gamepad/public/cpp/gamepad_switches.h
new file mode 100644
index 00000000000..18460ec70f1
--- /dev/null
+++ b/chromium/device/gamepad/public/cpp/gamepad_switches.h
@@ -0,0 +1,16 @@
+// 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_GAMEPAD_PUBLIC_CPP_GAMEPAD_SWITCHES_H_
+#define DEVICE_GAMEPAD_PUBLIC_CPP_GAMEPAD_SWITCHES_H_
+
+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 kGamepadPollingInterval[];
+
+} // namespace switches
+
+#endif // DEVICE_GAMEPAD_PUBLIC_CPP_GAMEPAD_SWITCHES_H_
diff --git a/chromium/device/gamepad/public/mojom/typemaps.gni b/chromium/device/gamepad/public/cpp/typemaps.gni
index 0a07fa1009b..acdc8729aa9 100644
--- a/chromium/device/gamepad/public/mojom/typemaps.gni
+++ b/chromium/device/gamepad/public/cpp/typemaps.gni
@@ -2,4 +2,4 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-typemaps = [ "//device/gamepad/public/mojom/gamepad.typemap" ]
+typemaps = [ "//device/gamepad/public/cpp/gamepad.typemap" ]
diff --git a/chromium/device/gamepad/public/mojom/BUILD.gn b/chromium/device/gamepad/public/mojom/BUILD.gn
index 9147390f0c2..a44ae8db51a 100644
--- a/chromium/device/gamepad/public/mojom/BUILD.gn
+++ b/chromium/device/gamepad/public/mojom/BUILD.gn
@@ -4,10 +4,17 @@
import("//mojo/public/tools/bindings/mojom.gni")
-mojom("mojom") {
+mojom_component("mojom") {
sources = [
"gamepad.mojom",
]
+
+ deps = [
+ "//mojo/public/mojom/base",
+ ]
+
+ output_prefix = "gamepad_mojom"
+ macro_prefix = "GAMEPAD_MOJOM"
}
mojom("gamepad_mojom_traits_test") {
diff --git a/chromium/device/gamepad/public/mojom/OWNERS b/chromium/device/gamepad/public/mojom/OWNERS
index 356244dfff8..5e4aaef9535 100644
--- a/chromium/device/gamepad/public/mojom/OWNERS
+++ b/chromium/device/gamepad/public/mojom/OWNERS
@@ -2,7 +2,3 @@
# introducing new sandbox escapes.
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
-per-file *_mojom_traits*.*=set noparent
-per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
-per-file *.typemap=set noparent
-per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/chromium/device/gamepad/public/mojom/gamepad.mojom b/chromium/device/gamepad/public/mojom/gamepad.mojom
index 465fb1008be..415395f788c 100644
--- a/chromium/device/gamepad/public/mojom/gamepad.mojom
+++ b/chromium/device/gamepad/public/mojom/gamepad.mojom
@@ -4,6 +4,8 @@
module device.mojom;
+import "mojo/public/mojom/base/shared_memory.mojom";
+
struct GamepadQuaternion {
float x;
float y;
@@ -54,7 +56,7 @@ struct GamepadHapticActuator {
struct Gamepad {
bool connected;
array<uint16> id;
- uint64 timestamp;
+ int64 timestamp;
array<double> axes;
array<GamepadButton> buttons;
GamepadHapticActuator? vibration_actuator;
@@ -70,12 +72,13 @@ interface GamepadObserver {
};
// Asks the browser process to start polling, and return a shared memory
-// handle that will hold the data from the hardware. See
+// region that will hold the data from the hardware. See
// gamepad_shared_buffer.h for a description of how synchronization is
// handled. The number of Starts should match the number of Stops.
interface GamepadMonitor {
[Sync]
- GamepadStartPolling() => (handle<shared_buffer> memory_handle);
+ GamepadStartPolling()
+ => (mojo_base.mojom.ReadOnlySharedMemoryRegion memory_region);
[Sync]
GamepadStopPolling() => ();
diff --git a/chromium/device/gamepad/public/mojom/gamepad_hardware_buffer.h b/chromium/device/gamepad/public/mojom/gamepad_hardware_buffer.h
new file mode 100644
index 00000000000..8e7b5a117d5
--- /dev/null
+++ b/chromium/device/gamepad/public/mojom/gamepad_hardware_buffer.h
@@ -0,0 +1,17 @@
+// 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_GAMEPAD_PUBLIC_MOJOM_GAMEPAD_HARDWARE_BUFFER_H_
+#define DEVICE_GAMEPAD_PUBLIC_MOJOM_GAMEPAD_HARDWARE_BUFFER_H_
+
+#include "device/base/synchronization/shared_memory_seqlock_buffer.h"
+#include "device/gamepad/public/cpp/gamepads.h"
+
+namespace device {
+
+typedef SharedMemorySeqLockBuffer<Gamepads> GamepadHardwareBuffer;
+
+} // namespace device
+
+#endif // DEVICE_GAMEPAD_PUBLIC_MOJOM_GAMEPAD_HARDWARE_BUFFER_H_
diff --git a/chromium/device/gamepad/public/mojom/gamepad_mojom_traits_unittest.cc b/chromium/device/gamepad/public/mojom/gamepad_mojom_traits_unittest.cc
index 03b1f9f50d8..fc951a3bb9b 100644
--- a/chromium/device/gamepad/public/mojom/gamepad_mojom_traits_unittest.cc
+++ b/chromium/device/gamepad/public/mojom/gamepad_mojom_traits_unittest.cc
@@ -6,6 +6,7 @@
#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"
@@ -68,7 +69,7 @@ Gamepad GetWebGamepadInstance(GamepadTestDataType type) {
for (size_t i = 0; i < Gamepad::kMappingLengthCap; i++) {
send.id[i] = send.mapping[i] = wch[i];
}
- send.timestamp = 1234567890123456789ULL;
+ send.timestamp = base::TimeTicks::Now().since_origin().InMicroseconds();
send.axes_length = 0U;
for (size_t i = 0; i < Gamepad::kAxesLengthCap; i++) {
send.axes_length++;
diff --git a/chromium/device/gamepad/raw_input_gamepad_device_win.cc b/chromium/device/gamepad/raw_input_gamepad_device_win.cc
index 392169f7b6e..d434c8d766a 100644
--- a/chromium/device/gamepad/raw_input_gamepad_device_win.cc
+++ b/chromium/device/gamepad/raw_input_gamepad_device_win.cc
@@ -4,6 +4,9 @@
#include "raw_input_gamepad_device_win.h"
+#include "base/stl_util.h"
+#include "device/gamepad/gamepad_data_fetcher.h"
+
namespace device {
namespace {
@@ -16,14 +19,41 @@ unsigned long GetBitmask(unsigned short bits) {
return (1 << bits) - 1;
}
-const uint32_t kAxisMinimumUsageNumber = 0x30;
+const uint32_t kGenericDesktopUsagePage = 0x01;
const uint32_t kGameControlsUsagePage = 0x05;
const uint32_t kButtonUsagePage = 0x09;
+const uint32_t kConsumerUsagePage = 0x0c;
+
+const uint32_t kAxisMinimumUsageNumber = 0x30;
+const uint32_t kSystemMainMenuUsageNumber = 0x85;
+const uint32_t kPowerUsageNumber = 0x30;
+const uint32_t kSearchUsageNumber = 0x0221;
+const uint32_t kHomeUsageNumber = 0x0223;
+const uint32_t kBackUsageNumber = 0x0224;
// Blacklisted vendor IDs.
const uint32_t kVendorOculus = 0x2833;
const uint32_t kVendorBlue = 0xb58e;
+// The fetcher will collect all HID usages from the Button usage page and any
+// additional usages listed below.
+struct SpecialUsages {
+ const uint16_t usage_page;
+ const uint16_t usage;
+} kSpecialUsages[] = {
+ // Xbox One S pre-FW update reports Xbox button as SystemMainMenu over BT.
+ {kGenericDesktopUsagePage, kSystemMainMenuUsageNumber},
+ // Power is used for the Guide button on the Nvidia Shield 2015 gamepad.
+ {kConsumerUsagePage, kPowerUsageNumber},
+ // Search is used for the Guide button on the Nvidia Shield 2017 gamepad.
+ {kConsumerUsagePage, kSearchUsageNumber},
+ // Start, Back, and Guide buttons are often reported as Consumer Home or
+ // Back.
+ {kConsumerUsagePage, kHomeUsageNumber},
+ {kConsumerUsagePage, kBackUsageNumber},
+};
+const size_t kSpecialUsagesLen = base::size(kSpecialUsages);
+
} // namespace
RawInputGamepadDeviceWin::RawInputGamepadDeviceWin(
@@ -32,6 +62,7 @@ RawInputGamepadDeviceWin::RawInputGamepadDeviceWin(
HidDllFunctionsWin* hid_functions)
: handle_(device_handle),
source_id_(source_id),
+ last_update_timestamp_(GamepadDataFetcher::CurrentTimeInMicroseconds()),
hid_functions_(hid_functions) {
::ZeroMemory(buttons_, sizeof(buttons_));
::ZeroMemory(axes_, sizeof(axes_));
@@ -85,11 +116,24 @@ void RawInputGamepadDeviceWin::UpdateGamepad(RAWINPUT* input) {
if (status == HIDP_STATUS_SUCCESS) {
// Set each reported button to true.
- for (uint32_t j = 0; j < buttons_length; j++) {
- int32_t button_index = usages[j].Usage - 1;
- if (usages[j].UsagePage == kButtonUsagePage && button_index >= 0 &&
- button_index < static_cast<int>(Gamepad::kButtonsLengthCap)) {
- buttons_[button_index] = true;
+ for (size_t j = 0; j < buttons_length; j++) {
+ uint16_t usage_page = usages[j].UsagePage;
+ uint16_t usage = usages[j].Usage;
+ if (usage_page == kButtonUsagePage && usage > 0) {
+ size_t button_index = size_t{usage - 1};
+ if (button_index < Gamepad::kButtonsLengthCap)
+ buttons_[button_index] = true;
+ } else if (usage_page != kButtonUsagePage &&
+ !special_button_map_.empty()) {
+ for (size_t special_index = 0; special_index < kSpecialUsagesLen;
+ ++special_index) {
+ int button_index = special_button_map_[special_index];
+ if (button_index < 0)
+ continue;
+ const auto& special = kSpecialUsages[special_index];
+ if (usage_page == special.usage_page && usage == special.usage)
+ buttons_[button_index] = true;
+ }
}
}
}
@@ -126,12 +170,14 @@ void RawInputGamepadDeviceWin::UpdateGamepad(RAWINPUT* input) {
}
}
}
+
+ last_update_timestamp_ = GamepadDataFetcher::CurrentTimeInMicroseconds();
}
void RawInputGamepadDeviceWin::ReadPadState(Gamepad* pad) const {
DCHECK(pad);
- pad->timestamp = report_id_;
+ pad->timestamp = last_update_timestamp_;
pad->buttons_length = buttons_length_;
pad->axes_length = axes_length_;
@@ -303,16 +349,98 @@ void RawInputGamepadDeviceWin::QueryButtonCapabilities(uint16_t button_count) {
HidP_Input, button_caps.get(), &button_count, preparsed_data_);
DCHECK_EQ(HIDP_STATUS_SUCCESS, status);
- for (size_t i = 0; i < button_count; ++i) {
- if (button_caps[i].Range.UsageMin <= Gamepad::kButtonsLengthCap &&
- button_caps[i].UsagePage == kButtonUsagePage) {
- size_t max_index =
- std::min(Gamepad::kButtonsLengthCap,
- static_cast<size_t>(button_caps[i].Range.UsageMax));
- buttons_length_ = std::max(buttons_length_, max_index);
+ // Keep track of which button indices are in use.
+ std::vector<bool> button_indices_used(Gamepad::kButtonsLengthCap, false);
+
+ // Collect all inputs from the Button usage page.
+ QueryNormalButtonCapabilities(button_caps.get(), button_count,
+ &button_indices_used);
+
+ // Check for common gamepad buttons that are not on the Button usage page.
+ QuerySpecialButtonCapabilities(button_caps.get(), button_count,
+ &button_indices_used);
+ }
+}
+
+void RawInputGamepadDeviceWin::QueryNormalButtonCapabilities(
+ HIDP_BUTTON_CAPS button_caps[],
+ uint16_t button_count,
+ std::vector<bool>* button_indices_used) {
+ DCHECK(button_caps);
+ DCHECK(button_indices_used);
+
+ // Collect all inputs from the Button usage page and assign button indices
+ // based on the usage value.
+ for (size_t i = 0; i < button_count; ++i) {
+ uint16_t usage_page = button_caps[i].UsagePage;
+ uint16_t usage_min = button_caps[i].Range.UsageMin;
+ uint16_t usage_max = button_caps[i].Range.UsageMax;
+ if (usage_min == 0 || usage_max == 0)
+ continue;
+ size_t button_index_min = size_t{usage_min - 1};
+ size_t button_index_max = size_t{usage_max - 1};
+ if (usage_page == kButtonUsagePage &&
+ button_index_min < Gamepad::kButtonsLengthCap) {
+ button_index_max =
+ std::min(Gamepad::kButtonsLengthCap - 1, button_index_max);
+ buttons_length_ = std::max(buttons_length_, button_index_max + 1);
+ for (size_t j = button_index_min; j <= button_index_max; ++j)
+ (*button_indices_used)[j] = true;
+ }
+ }
+}
+
+void RawInputGamepadDeviceWin::QuerySpecialButtonCapabilities(
+ HIDP_BUTTON_CAPS button_caps[],
+ uint16_t button_count,
+ std::vector<bool>* button_indices_used) {
+ DCHECK(button_caps);
+ DCHECK(button_indices_used);
+
+ // Check for common gamepad buttons that are not on the Button usage page.
+ std::vector<bool> has_special_usage(kSpecialUsagesLen, false);
+ size_t unmapped_button_count = 0;
+ for (size_t i = 0; i < button_count; ++i) {
+ uint16_t usage_page = button_caps[i].UsagePage;
+ uint16_t usage_min = button_caps[i].Range.UsageMin;
+ uint16_t usage_max = button_caps[i].Range.UsageMax;
+ for (size_t special_index = 0; special_index < kSpecialUsagesLen;
+ ++special_index) {
+ const auto& special = kSpecialUsages[special_index];
+ if (usage_page == special.usage_page && usage_min <= special.usage &&
+ usage_max >= special.usage) {
+ has_special_usage[special_index] = true;
+ ++unmapped_button_count;
}
}
}
+
+ special_button_map_.clear();
+ if (unmapped_button_count > 0) {
+ // Insert special buttons at unused button indices.
+ special_button_map_.resize(kSpecialUsagesLen, -1);
+ size_t button_index = 0;
+ for (size_t special_index = 0; special_index < kSpecialUsagesLen;
+ ++special_index) {
+ if (!has_special_usage[special_index])
+ continue;
+
+ // Advance to the next unused button index.
+ while (button_index < Gamepad::kButtonsLengthCap &&
+ (*button_indices_used)[button_index]) {
+ ++button_index;
+ }
+ if (button_index >= Gamepad::kButtonsLengthCap)
+ break;
+
+ special_button_map_[special_index] = button_index;
+ (*button_indices_used)[button_index] = true;
+ ++button_index;
+
+ if (--unmapped_button_count == 0)
+ break;
+ }
+ }
}
void RawInputGamepadDeviceWin::QueryAxisCapabilities(uint16_t axis_count) {
diff --git a/chromium/device/gamepad/raw_input_gamepad_device_win.h b/chromium/device/gamepad/raw_input_gamepad_device_win.h
index 20a9781c1a0..a23b232548f 100644
--- a/chromium/device/gamepad/raw_input_gamepad_device_win.h
+++ b/chromium/device/gamepad/raw_input_gamepad_device_win.h
@@ -13,6 +13,7 @@
#include <windows.h>
#include <memory>
+#include <vector>
#include "device/gamepad/abstract_haptic_gamepad.h"
#include "device/gamepad/dualshock4_controller_win.h"
@@ -87,6 +88,12 @@ class RawInputGamepadDeviceWin : public AbstractHapticGamepad {
// on the device.
bool QueryDeviceCapabilities();
void QueryButtonCapabilities(uint16_t button_count);
+ void QueryNormalButtonCapabilities(HIDP_BUTTON_CAPS button_caps[],
+ uint16_t button_count,
+ std::vector<bool>* button_indices_used);
+ void QuerySpecialButtonCapabilities(HIDP_BUTTON_CAPS button_caps[],
+ uint16_t button_count,
+ std::vector<bool>* button_indices_used);
void QueryAxisCapabilities(uint16_t axis_count);
// True if the device described by this object is a valid RawInput gamepad.
@@ -98,6 +105,9 @@ class RawInputGamepadDeviceWin : public AbstractHapticGamepad {
// The index assigned to this gamepad by the data fetcher.
int source_id_ = 0;
+ // The last time the pad state was updated.
+ int64_t last_update_timestamp_;
+
// Functions loaded from hid.dll. Not owned.
HidDllFunctionsWin* hid_functions_ = nullptr;
@@ -115,6 +125,11 @@ class RawInputGamepadDeviceWin : public AbstractHapticGamepad {
size_t buttons_length_ = 0;
bool buttons_[Gamepad::kButtonsLengthCap];
+ // Mapping from "Special" usage index (defined by the kSpecialUsages table)
+ // to an index within the |buttons_| array, or -1 if the special usage is not
+ // mapped for this device.
+ std::vector<int> special_button_map_;
+
size_t axes_length_ = 0;
RawGamepadAxis axes_[Gamepad::kAxesLengthCap];
diff --git a/chromium/device/gamepad/switch_pro_controller_base.cc b/chromium/device/gamepad/switch_pro_controller_base.cc
index 58d186474c3..7cc5096bd7d 100644
--- a/chromium/device/gamepad/switch_pro_controller_base.cc
+++ b/chromium/device/gamepad/switch_pro_controller_base.cc
@@ -6,6 +6,7 @@
#include <limits>
+#include "device/gamepad/gamepad_data_fetcher.h"
#include "device/gamepad/gamepad_standard_mappings.h"
namespace {
@@ -234,7 +235,7 @@ void SwitchProControllerBase::HandleInputReport(void* report,
ControllerDataReport* controller_data =
reinterpret_cast<ControllerDataReport*>(report);
UpdatePadStateFromControllerData(*controller_data, pad);
- pad->timestamp = ++report_id_;
+ pad->timestamp = GamepadDataFetcher::CurrentTimeInMicroseconds();
break;
}
default:
diff --git a/chromium/device/gamepad/switch_pro_controller_base.h b/chromium/device/gamepad/switch_pro_controller_base.h
index c0e165ba192..535a6875095 100644
--- a/chromium/device/gamepad/switch_pro_controller_base.h
+++ b/chromium/device/gamepad/switch_pro_controller_base.h
@@ -35,7 +35,6 @@ class SwitchProControllerBase : public AbstractHapticGamepad {
private:
uint32_t counter_ = 0;
- uint32_t report_id_ = 0;
bool force_usb_hid_ = false;
bool sent_handshake_ = false;
};
diff --git a/chromium/device/gamepad/xbox_controller_mac.h b/chromium/device/gamepad/xbox_controller_mac.h
index 4bd9c07cfda..edc13443c70 100644
--- a/chromium/device/gamepad/xbox_controller_mac.h
+++ b/chromium/device/gamepad/xbox_controller_mac.h
@@ -150,6 +150,7 @@ class XboxControllerMac {
void WriteXbox360Rumble(uint8_t strong_magnitude, uint8_t weak_magnitude);
void WriteXboxOneInit();
void WriteXboxOneRumble(uint8_t strong_magnitude, uint8_t weak_magnitude);
+ void WriteXboxOneAckGuide(uint8_t sequence_number);
// Handle for the USB device. IOUSBDeviceStruct320 is the latest version of
// the device API that is supported on Mac OS 10.6.
diff --git a/chromium/device/gamepad/xbox_controller_mac.mm b/chromium/device/gamepad/xbox_controller_mac.mm
index bcbf6e321e6..89745ffbb74 100644
--- a/chromium/device/gamepad/xbox_controller_mac.mm
+++ b/chromium/device/gamepad/xbox_controller_mac.mm
@@ -37,6 +37,9 @@ const int kXboxOneControlEndpoint = 1;
const double kXboxOneMaxEffectDurationMillis = 2500; // 2.5 seconds
+const size_t kXbox360HeaderBytes = 2;
+const size_t kXboxOneHeaderBytes = 4;
+
enum {
STATUS_MESSAGE_BUTTONS = 0,
STATUS_MESSAGE_LED = 1,
@@ -720,7 +723,7 @@ void XboxControllerMac::GotData(void* context, IOReturn result, void* arg0) {
}
void XboxControllerMac::ProcessXbox360Packet(size_t length) {
- if (length < 2)
+ if (length < kXbox360HeaderBytes)
return;
DCHECK(length <= read_buffer_size_);
if (length > read_buffer_size_) {
@@ -734,8 +737,8 @@ void XboxControllerMac::ProcessXbox360Packet(size_t length) {
return;
uint8_t type = buffer[0];
- buffer += 2;
- length -= 2;
+ buffer += kXbox360HeaderBytes;
+ length -= kXbox360HeaderBytes;
switch (type) {
case STATUS_MESSAGE_BUTTONS: {
if (length != sizeof(Xbox360ButtonData))
@@ -762,7 +765,7 @@ void XboxControllerMac::ProcessXbox360Packet(size_t length) {
}
void XboxControllerMac::ProcessXboxOnePacket(size_t length) {
- if (length < 2)
+ if (length < kXboxOneHeaderBytes)
return;
DCHECK(length <= read_buffer_size_);
if (length > read_buffer_size_) {
@@ -770,10 +773,12 @@ void XboxControllerMac::ProcessXboxOnePacket(size_t length) {
return;
}
uint8_t* buffer = read_buffer_.get();
-
uint8_t type = buffer[0];
- buffer += 4;
- length -= 4;
+ bool needs_ack = (buffer[1] == 0x30);
+ uint8_t sequence_number = buffer[2];
+
+ buffer += kXboxOneHeaderBytes;
+ length -= kXboxOneHeaderBytes;
switch (type) {
case XBOX_ONE_STATUS_MESSAGE_BUTTONS: {
if (length != sizeof(XboxOneButtonData) &&
@@ -791,6 +796,9 @@ void XboxControllerMac::ProcessXboxOnePacket(size_t length) {
return;
XboxOneGuideData* data = reinterpret_cast<XboxOneGuideData*>(buffer);
delegate_->XboxControllerGotGuideData(this, data->down);
+ // The Xbox One S controller requires these reports to be acked.
+ if (needs_ack)
+ WriteXboxOneAckGuide(sequence_number);
break;
}
default:
@@ -899,6 +907,38 @@ void XboxControllerMac::WriteXboxOneRumble(uint8_t strong_magnitude,
}
}
+// Ack the guide report. The contents of the ack report are modeled after
+// xpad's xboxone_ack_mode_report. See the mode_report_ack buffer defined in
+// this commit:
+// https://github.com/torvalds/linux/commit/57b8443d3e5bd046a519ff714ca31c64c7f04309
+void XboxControllerMac::WriteXboxOneAckGuide(uint8_t sequence_number) {
+ const UInt8 length = 13;
+
+ UInt8* buffer = new UInt8[length];
+ buffer[0] = 0x01;
+ buffer[1] = 0x20;
+ buffer[2] = sequence_number;
+ buffer[3] = 0x09;
+ buffer[4] = 0x00;
+ buffer[5] = 0x07;
+ buffer[6] = 0x20;
+ buffer[7] = 0x02;
+ buffer[8] = 0x00;
+ buffer[9] = 0x00;
+ buffer[10] = 0x00;
+ buffer[11] = 0x00;
+ buffer[12] = 0x00;
+ kern_return_t kr =
+ (*interface_)
+ ->WritePipeAsync(interface_, control_endpoint_, buffer,
+ (UInt32)length, WriteComplete, buffer);
+ if (kr != KERN_SUCCESS) {
+ delete[] buffer;
+ IOError();
+ return;
+ }
+}
+
void XboxControllerMac::RunCallbackOnMojoThread(
mojom::GamepadHapticsResult result) {
if (playing_effect_task_runner_->RunsTasksInCurrentSequence()) {
diff --git a/chromium/device/gamepad/xbox_data_fetcher_mac.cc b/chromium/device/gamepad/xbox_data_fetcher_mac.cc
index 79077d7bb64..6ebb99d80ad 100644
--- a/chromium/device/gamepad/xbox_data_fetcher_mac.cc
+++ b/chromium/device/gamepad/xbox_data_fetcher_mac.cc
@@ -317,7 +317,7 @@ void XboxDataFetcher::AddController(XboxControllerMac* controller) {
state->data.connected = true;
state->data.axes_length = 4;
state->data.buttons_length = 17;
- state->data.timestamp = 0;
+ state->data.timestamp = CurrentTimeInMicroseconds();
state->mapper = 0;
state->axis_mask = 0;
state->button_mask = 0;
@@ -375,7 +375,7 @@ void XboxDataFetcher::XboxControllerGotData(
pad.axes[i] = data.axes[i];
}
- pad.timestamp = (base::TimeTicks::Now() - base::TimeTicks()).InMicroseconds();
+ pad.timestamp = CurrentTimeInMicroseconds();
}
void XboxDataFetcher::XboxControllerGotGuideData(XboxControllerMac* controller,
@@ -389,7 +389,7 @@ void XboxDataFetcher::XboxControllerGotGuideData(XboxControllerMac* controller,
pad.buttons[16].pressed = guide;
pad.buttons[16].value = guide ? 1.0f : 0.0f;
- pad.timestamp = (base::TimeTicks::Now() - base::TimeTicks()).InMicroseconds();
+ pad.timestamp = CurrentTimeInMicroseconds();
}
void XboxDataFetcher::XboxControllerError(XboxControllerMac* controller) {
diff --git a/chromium/device/geolocation/BUILD.gn b/chromium/device/geolocation/BUILD.gn
deleted file mode 100644
index a69729c616a..00000000000
--- a/chromium/device/geolocation/BUILD.gn
+++ /dev/null
@@ -1,212 +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.
-
-import("//build/config/features.gni")
-
-if (is_android) {
- import("//build/config/android/config.gni")
- import("//build/config/android/rules.gni") # For generate_jni().
-}
-
-component("geolocation") {
- defines = [ "DEVICE_GEOLOCATION_IMPLEMENTATION" ]
-
- sources = [
- "empty_wifi_data_provider.cc",
- "empty_wifi_data_provider.h",
- "geolocation_config.cc",
- "geolocation_config.h",
- "geolocation_context.cc",
- "geolocation_context.h",
- "geolocation_export.h",
- "geolocation_impl.cc",
- "geolocation_impl.h",
- "geolocation_provider.h",
- "geolocation_provider_impl.cc",
- "geolocation_provider_impl.h",
- "location_api_adapter_android.cc",
- "location_api_adapter_android.h",
- "location_arbitrator.cc",
- "location_arbitrator.h",
- "location_provider_android.cc",
- "location_provider_android.h",
- "network_location_provider.cc",
- "network_location_provider.h",
- "network_location_request.cc",
- "network_location_request.h",
- "wifi_data.cc",
- "wifi_data.h",
- "wifi_data_provider.cc",
- "wifi_data_provider.h",
- "wifi_data_provider_chromeos.cc",
- "wifi_data_provider_chromeos.h",
- "wifi_data_provider_common.cc",
- "wifi_data_provider_common.h",
- "wifi_data_provider_common_win.cc",
- "wifi_data_provider_common_win.h",
- "wifi_data_provider_linux.cc",
- "wifi_data_provider_linux.h",
- "wifi_data_provider_mac.h",
- "wifi_data_provider_mac.mm",
- "wifi_data_provider_manager.cc",
- "wifi_data_provider_manager.h",
- "wifi_data_provider_win.cc",
- "wifi_data_provider_win.h",
- "wifi_polling_policy.cc",
- "wifi_polling_policy.h",
- ]
-
- deps = [
- "//base",
- "//mojo/edk",
- "//mojo/public/cpp/bindings",
- "//net",
- "//ui/gfx",
- ]
- public_deps = [
- "//device/geolocation/public/cpp",
- "//services/device/public/mojom",
- ]
- if (is_android) {
- sources -= [
- "network_location_provider.cc",
- "network_location_provider.h",
- ]
- deps += [ ":geolocation_jni_headers" ]
- }
-
- # TODO(mcasas): prefer adding files than removing them (see cookbook.md).
-
- # Dealing with *wifi_data_provider_*.cc is also a bit complicated given
- # android, chromeos, linux and use_dbus.
- if (is_android) {
- sources -= [ "wifi_data_provider_common.cc" ]
- }
- if (is_chromeos || (is_linux && !use_dbus)) {
- sources -= [ "wifi_data_provider_linux.cc" ]
- }
- if (is_linux && use_dbus) {
- sources -= [ "empty_wifi_data_provider.cc" ]
- deps += [ "//dbus" ]
- }
- if (is_win || is_mac) {
- sources -= [
- "empty_wifi_data_provider.cc",
- "empty_wifi_data_provider.h",
- ]
- }
- if (is_chromeos) {
- deps += [ "//chromeos" ]
- }
-
- if (is_mac) {
- libs = [
- "CoreWLAN.framework",
- "Foundation.framework",
- ]
- }
-}
-
-if (is_android) {
- generate_jni("geolocation_jni_headers") {
- sources = [
- "android/java/src/org/chromium/device/geolocation/LocationProviderAdapter.java",
- ]
- jni_package = "device"
- }
-
- android_library("geolocation_java") {
- java_files = [
- "android/java/src/org/chromium/device/geolocation/LocationProviderAdapter.java",
- "android/java/src/org/chromium/device/geolocation/LocationProviderAndroid.java",
- "android/java/src/org/chromium/device/geolocation/LocationProviderFactory.java",
- "android/java/src/org/chromium/device/geolocation/LocationProviderGmsCore.java",
- ]
-
- deps = [
- ":geolocation",
- ":geolocation_jni_headers",
- "$google_play_services_package:google_play_services_base_java",
- "$google_play_services_package:google_play_services_basement_java",
- "$google_play_services_package:google_play_services_location_java",
- "//base:base_java",
- ]
- }
-
- android_library("geolocation_java_test_support") {
- testonly = true
-
- java_files = [ "android/javatests/src/org/chromium/device/geolocation/MockLocationProvider.java" ]
- deps = [
- ":geolocation_java",
- "//base:base_java",
- ]
- }
-}
-
-source_set("test_support") {
- testonly = true
-
- sources = [
- "fake_location_provider.cc",
- "fake_location_provider.h",
- ]
- public_deps = [
- ":geolocation",
- ]
- deps = [
- "//device/geolocation/public/cpp",
- "//testing/gmock",
- "//testing/gtest",
- ]
-}
-
-source_set("unittests") {
- testonly = true
-
- sources = [
- "geolocation_provider_impl_unittest.cc",
- "location_arbitrator_unittest.cc",
- "network_location_provider_unittest.cc",
- "wifi_data_provider_chromeos_unittest.cc",
- "wifi_data_provider_common_unittest.cc",
- "wifi_data_provider_linux_unittest.cc",
- "wifi_data_provider_win_unittest.cc",
- "wifi_polling_policy_unittest.cc",
- ]
- public_deps = [
- ":geolocation",
- ]
- deps = [
- ":test_support",
- "//base",
- "//base/third_party/dynamic_annotations",
- "//device/geolocation/public/cpp",
- "//net:test_support",
- "//services/device/public/mojom",
- "//testing/gmock",
- "//testing/gtest",
- ]
-
- if (is_linux) {
- if (use_dbus) {
- deps += [ "//dbus:test_support" ]
- } else {
- sources -= [ "wifi_data_provider_linux_unittest.cc" ]
- }
- }
-
- if (is_chromeos) {
- sources -= [ "wifi_data_provider_linux_unittest.cc" ]
- deps += [ "//chromeos" ]
- }
-
- if (is_android) {
- sources -= [
- "network_location_provider_unittest.cc",
- "wifi_data_provider_common_unittest.cc",
- ]
- deps += [ ":geolocation_java_test_support" ]
- }
-}
diff --git a/chromium/device/geolocation/DEPS b/chromium/device/geolocation/DEPS
deleted file mode 100644
index d3e3a8c3db6..00000000000
--- a/chromium/device/geolocation/DEPS
+++ /dev/null
@@ -1,7 +0,0 @@
-include_rules = [
- "+chromeos",
- "+dbus",
- "+jni",
- "+net",
- "+third_party/cros_system_api/dbus",
-]
diff --git a/chromium/device/geolocation/OWNERS b/chromium/device/geolocation/OWNERS
deleted file mode 100644
index 9a6b2e1b369..00000000000
--- a/chromium/device/geolocation/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-mcasas@chromium.org
-timvolodine@chromium.org
-
-# TEAM: device-dev@chromium.org
-# COMPONENT: Blink>Location
diff --git a/chromium/device/geolocation/empty_wifi_data_provider.cc b/chromium/device/geolocation/empty_wifi_data_provider.cc
deleted file mode 100644
index ed16d47233b..00000000000
--- a/chromium/device/geolocation/empty_wifi_data_provider.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2013 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/geolocation/empty_wifi_data_provider.h"
-
-#include "device/geolocation/wifi_data_provider_manager.h"
-
-namespace device {
-
-EmptyWifiDataProvider::EmptyWifiDataProvider() {}
-
-EmptyWifiDataProvider::~EmptyWifiDataProvider() {}
-
-bool EmptyWifiDataProvider::GetData(WifiData* data) {
- DCHECK(data);
- // This is all the data we can get - nothing.
- return true;
-}
-
-// static
-WifiDataProvider* WifiDataProviderManager::DefaultFactoryFunction() {
- return new EmptyWifiDataProvider();
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/empty_wifi_data_provider.h b/chromium/device/geolocation/empty_wifi_data_provider.h
deleted file mode 100644
index b619cd320df..00000000000
--- a/chromium/device/geolocation/empty_wifi_data_provider.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2013 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_GEOLOCATION_EMPTY_WIFI_DATA_PROVIDER_H_
-#define DEVICE_GEOLOCATION_EMPTY_WIFI_DATA_PROVIDER_H_
-
-#include "base/macros.h"
-#include "device/geolocation/wifi_data_provider.h"
-
-namespace device {
-
-// An implementation of WifiDataProvider that does not provide any
-// data. Used on platforms where a real implementation is not available.
-class EmptyWifiDataProvider : public WifiDataProvider {
- public:
- EmptyWifiDataProvider();
-
- // WifiDataProvider implementation
- void StartDataProvider() override {}
- void StopDataProvider() override {}
- bool GetData(WifiData* data) override;
-
- private:
- ~EmptyWifiDataProvider() override;
-
- DISALLOW_COPY_AND_ASSIGN(EmptyWifiDataProvider);
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_EMPTY_WIFI_DATA_PROVIDER_H_
diff --git a/chromium/device/geolocation/fake_location_provider.cc b/chromium/device/geolocation/fake_location_provider.cc
deleted file mode 100644
index d4642c862f5..00000000000
--- a/chromium/device/geolocation/fake_location_provider.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (c) 2012 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.
-
-// This file implements a fake location provider and the factory functions for
-// various ways of creating it.
-// TODO(lethalantidote): Convert location_arbitrator_impl to use actual mock
-// instead of FakeLocationProvider.
-
-#include "device/geolocation/fake_location_provider.h"
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/compiler_specific.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/memory/weak_ptr.h"
-#include "base/threading/thread_task_runner_handle.h"
-
-namespace device {
-
-FakeLocationProvider::FakeLocationProvider()
- : provider_task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
-
-FakeLocationProvider::~FakeLocationProvider() = default;
-
-void FakeLocationProvider::HandlePositionChanged(
- const mojom::Geoposition& position) {
- if (provider_task_runner_->BelongsToCurrentThread()) {
- // The location arbitrator unit tests rely on this method running
- // synchronously.
- position_ = position;
- if (!callback_.is_null())
- callback_.Run(this, position_);
- } else {
- provider_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&FakeLocationProvider::HandlePositionChanged,
- base::Unretained(this), position));
- }
-}
-
-void FakeLocationProvider::SetUpdateCallback(
- const LocationProviderUpdateCallback& callback) {
- callback_ = callback;
-}
-
-void FakeLocationProvider::StartProvider(bool high_accuracy) {
- state_ = high_accuracy ? HIGH_ACCURACY : LOW_ACCURACY;
-}
-
-void FakeLocationProvider::StopProvider() {
- state_ = STOPPED;
-}
-
-const mojom::Geoposition& FakeLocationProvider::GetPosition() {
- return position_;
-}
-
-void FakeLocationProvider::OnPermissionGranted() {
- is_permission_granted_ = true;
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/fake_location_provider.h b/chromium/device/geolocation/fake_location_provider.h
deleted file mode 100644
index f86a2f6df1a..00000000000
--- a/chromium/device/geolocation/fake_location_provider.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 2012 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_GEOLOCATION_FAKE_LOCATION_PROVIDER_H_
-#define DEVICE_GEOLOCATION_FAKE_LOCATION_PROVIDER_H_
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread.h"
-#include "device/geolocation/public/cpp/location_provider.h"
-#include "services/device/public/mojom/geoposition.mojom.h"
-
-namespace device {
-
-// Fake implementation of a location provider for testing.
-class FakeLocationProvider : public LocationProvider {
- public:
- enum State { STOPPED, LOW_ACCURACY, HIGH_ACCURACY } state_ = STOPPED;
-
- FakeLocationProvider();
- ~FakeLocationProvider() override;
-
- // Updates listeners with the new position.
- void HandlePositionChanged(const mojom::Geoposition& position);
-
- State state() const { return state_; }
- bool is_permission_granted() const { return is_permission_granted_; }
-
- // LocationProvider implementation.
- void SetUpdateCallback(
- const LocationProviderUpdateCallback& callback) override;
- void StartProvider(bool high_accuracy) override;
- void StopProvider() override;
- const mojom::Geoposition& GetPosition() override;
- void OnPermissionGranted() override;
-
- scoped_refptr<base::SingleThreadTaskRunner> provider_task_runner_;
-
- private:
- bool is_permission_granted_ = false;
- mojom::Geoposition position_;
- LocationProviderUpdateCallback callback_;
-
- DISALLOW_COPY_AND_ASSIGN(FakeLocationProvider);
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_FAKE_LOCATION_PROVIDER_H_
diff --git a/chromium/device/geolocation/geolocation_config.cc b/chromium/device/geolocation/geolocation_config.cc
deleted file mode 100644
index 82fdde4f839..00000000000
--- a/chromium/device/geolocation/geolocation_config.cc
+++ /dev/null
@@ -1,28 +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/geolocation/geolocation_config.h"
-
-#include "base/bind.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
-
-namespace device {
-
-GeolocationConfig::GeolocationConfig() = default;
-
-GeolocationConfig::~GeolocationConfig() = default;
-
-// static
-void GeolocationConfig::Create(mojom::GeolocationConfigRequest request) {
- mojo::MakeStrongBinding(std::make_unique<GeolocationConfig>(),
- std::move(request));
-}
-
-void GeolocationConfig::IsHighAccuracyLocationBeingCaptured(
- IsHighAccuracyLocationBeingCapturedCallback callback) {
- std::move(callback).Run(
- GeolocationProvider::GetInstance()->HighAccuracyLocationInUse());
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/geolocation_config.h b/chromium/device/geolocation/geolocation_config.h
deleted file mode 100644
index 035d7c4c875..00000000000
--- a/chromium/device/geolocation/geolocation_config.h
+++ /dev/null
@@ -1,34 +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_GEOLOCATION_GEOLOCATION_CONFIG_H_
-#define DEVICE_GEOLOCATION_GEOLOCATION_CONFIG_H_
-
-#include "base/compiler_specific.h"
-#include "device/geolocation/geolocation_export.h"
-#include "device/geolocation/geolocation_provider_impl.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "services/device/public/mojom/geolocation_config.mojom.h"
-
-namespace device {
-
-// Implements the GeolocationConfig Mojo interface.
-class DEVICE_GEOLOCATION_EXPORT GeolocationConfig
- : public mojom::GeolocationConfig {
- public:
- GeolocationConfig();
- ~GeolocationConfig() override;
-
- static void Create(mojom::GeolocationConfigRequest request);
-
- void IsHighAccuracyLocationBeingCaptured(
- IsHighAccuracyLocationBeingCapturedCallback callback) override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(GeolocationConfig);
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_GEOLOCATION_CONFIG_H_
diff --git a/chromium/device/geolocation/geolocation_context.cc b/chromium/device/geolocation/geolocation_context.cc
deleted file mode 100644
index 8ba9f337ec4..00000000000
--- a/chromium/device/geolocation/geolocation_context.cc
+++ /dev/null
@@ -1,57 +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/geolocation/geolocation_context.h"
-
-#include <utility>
-
-#include "base/memory/ptr_util.h"
-#include "device/geolocation/geolocation_impl.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
-
-namespace device {
-
-GeolocationContext::GeolocationContext() = default;
-
-GeolocationContext::~GeolocationContext() = default;
-
-// static
-void GeolocationContext::Create(mojom::GeolocationContextRequest request) {
- mojo::MakeStrongBinding(std::make_unique<GeolocationContext>(),
- std::move(request));
-}
-
-void GeolocationContext::BindGeolocation(mojom::GeolocationRequest request) {
- GeolocationImpl* impl = new GeolocationImpl(std::move(request), this);
- impls_.push_back(base::WrapUnique<GeolocationImpl>(impl));
- if (geoposition_override_)
- impl->SetOverride(*geoposition_override_);
- else
- impl->StartListeningForUpdates();
-}
-
-void GeolocationContext::OnConnectionError(GeolocationImpl* impl) {
- auto it = std::find_if(impls_.begin(), impls_.end(),
- [impl](const std::unique_ptr<GeolocationImpl>& gi) {
- return impl == gi.get();
- });
- DCHECK(it != impls_.end());
- impls_.erase(it);
-}
-
-void GeolocationContext::SetOverride(mojom::GeopositionPtr geoposition) {
- geoposition_override_ = std::move(geoposition);
- for (auto& impl : impls_) {
- impl->SetOverride(*geoposition_override_);
- }
-}
-
-void GeolocationContext::ClearOverride() {
- geoposition_override_.reset();
- for (auto& impl : impls_) {
- impl->ClearOverride();
- }
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/geolocation_context.h b/chromium/device/geolocation/geolocation_context.h
deleted file mode 100644
index fbc08eaae59..00000000000
--- a/chromium/device/geolocation/geolocation_context.h
+++ /dev/null
@@ -1,52 +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_GEOLOCATION_GEOLOCATION_CONTEXT_H_
-#define DEVICE_GEOLOCATION_GEOLOCATION_CONTEXT_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/macros.h"
-#include "device/geolocation/geolocation_export.h"
-#include "services/device/public/mojom/geolocation.mojom.h"
-#include "services/device/public/mojom/geolocation_context.mojom.h"
-#include "services/device/public/mojom/geoposition.mojom.h"
-
-namespace device {
-
-class GeolocationImpl;
-
-// Provides information to a set of GeolocationImpl instances that are
-// associated with a given context. Notably, allows pausing and resuming
-// geolocation on these instances.
-class DEVICE_GEOLOCATION_EXPORT GeolocationContext
- : public mojom::GeolocationContext {
- public:
- GeolocationContext();
- ~GeolocationContext() override;
-
- // Creates GeolocationContext that is strongly bound to |request|.
- static void Create(mojom::GeolocationContextRequest request);
-
- // mojom::GeolocationContext implementation:
- void BindGeolocation(mojom::GeolocationRequest request) override;
- void SetOverride(mojom::GeopositionPtr geoposition) override;
- void ClearOverride() override;
-
- // Called when a GeolocationImpl has a connection error. After this call, it
- // is no longer safe to access |impl|.
- void OnConnectionError(GeolocationImpl* impl);
-
- private:
- std::vector<std::unique_ptr<GeolocationImpl>> impls_;
-
- mojom::GeopositionPtr geoposition_override_;
-
- DISALLOW_COPY_AND_ASSIGN(GeolocationContext);
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_GEOLOCATION_CONTEXT_H_
diff --git a/chromium/device/geolocation/geolocation_export.h b/chromium/device/geolocation/geolocation_export.h
deleted file mode 100644
index fedc4271752..00000000000
--- a/chromium/device/geolocation/geolocation_export.h
+++ /dev/null
@@ -1,28 +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_GEOLOCATION_PUBLIC_CPP_DEVICE_GEOLOCATION_EXPORT_H_
-#define DEVICE_GEOLOCATION_PUBLIC_CPP_DEVICE_GEOLOCATION_EXPORT_H_
-
-#if defined(COMPONENT_BUILD) && defined(WIN32)
-
-#if defined(DEVICE_GEOLOCATION_IMPLEMENTATION)
-#define DEVICE_GEOLOCATION_EXPORT __declspec(dllexport)
-#else
-#define DEVICE_GEOLOCATION_EXPORT __declspec(dllimport)
-#endif
-
-#elif defined(COMPONENT_BUILD) && !defined(WIN32)
-
-#if defined(DEVICE_GEOLOCATION_IMPLEMENTATION)
-#define DEVICE_GEOLOCATION_EXPORT __attribute__((visibility("default")))
-#else
-#define DEVICE_GEOLOCATION_EXPORT
-#endif
-
-#else
-#define DEVICE_GEOLOCATION_EXPORT
-#endif
-
-#endif // DEVICE_GEOLOCATION_PUBLIC_CPP_DEVICE_GEOLOCATION_EXPORT_H_
diff --git a/chromium/device/geolocation/geolocation_impl.cc b/chromium/device/geolocation/geolocation_impl.cc
deleted file mode 100644
index 720c243a67c..00000000000
--- a/chromium/device/geolocation/geolocation_impl.cc
+++ /dev/null
@@ -1,174 +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/geolocation/geolocation_impl.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/metrics/histogram_macros.h"
-#include "device/geolocation/geolocation_context.h"
-#include "device/geolocation/public/cpp/geoposition.h"
-
-namespace device {
-
-namespace {
-
-// Geoposition error codes for reporting in UMA.
-enum GeopositionErrorCode {
- // NOTE: Do not renumber these as that would confuse interpretation of
- // previously logged data. When making changes, also update the enum list
- // in tools/metrics/histograms/histograms.xml to keep it in sync.
-
- // There was no error.
- GEOPOSITION_ERROR_CODE_NONE = 0,
-
- // User denied use of geolocation.
- GEOPOSITION_ERROR_CODE_PERMISSION_DENIED = 1,
-
- // Geoposition could not be determined.
- GEOPOSITION_ERROR_CODE_POSITION_UNAVAILABLE = 2,
-
- // Timeout.
- GEOPOSITION_ERROR_CODE_TIMEOUT = 3,
-
- // NOTE: Add entries only immediately above this line.
- GEOPOSITION_ERROR_CODE_COUNT = 4
-};
-
-void RecordGeopositionErrorCode(mojom::Geoposition::ErrorCode error_code) {
- GeopositionErrorCode code = GEOPOSITION_ERROR_CODE_NONE;
- switch (error_code) {
- case mojom::Geoposition::ErrorCode::NONE:
- code = GEOPOSITION_ERROR_CODE_NONE;
- break;
- case mojom::Geoposition::ErrorCode::PERMISSION_DENIED:
- code = GEOPOSITION_ERROR_CODE_PERMISSION_DENIED;
- break;
- case mojom::Geoposition::ErrorCode::POSITION_UNAVAILABLE:
- code = GEOPOSITION_ERROR_CODE_POSITION_UNAVAILABLE;
- break;
- case mojom::Geoposition::ErrorCode::TIMEOUT:
- code = GEOPOSITION_ERROR_CODE_TIMEOUT;
- break;
- }
- UMA_HISTOGRAM_ENUMERATION("Geolocation.LocationUpdate.ErrorCode", code,
- GEOPOSITION_ERROR_CODE_COUNT);
-}
-
-} // namespace
-
-GeolocationImpl::GeolocationImpl(mojo::InterfaceRequest<Geolocation> request,
- GeolocationContext* context)
- : binding_(this, std::move(request)),
- context_(context),
- high_accuracy_(false),
- has_position_to_report_(false) {
- DCHECK(context_);
- binding_.set_connection_error_handler(base::BindOnce(
- &GeolocationImpl::OnConnectionError, base::Unretained(this)));
-}
-
-GeolocationImpl::~GeolocationImpl() {
- // Make sure to respond to any pending callback even without a valid position.
- if (!position_callback_.is_null()) {
- if (ValidateGeoposition(current_position_)) {
- current_position_.error_code = mojom::Geoposition::ErrorCode(
- GEOPOSITION_ERROR_CODE_POSITION_UNAVAILABLE);
- current_position_.error_message.clear();
- }
- ReportCurrentPosition();
- }
-}
-
-void GeolocationImpl::PauseUpdates() {
- geolocation_subscription_.reset();
-}
-
-void GeolocationImpl::ResumeUpdates() {
- if (ValidateGeoposition(position_override_)) {
- OnLocationUpdate(position_override_);
- return;
- }
-
- StartListeningForUpdates();
-}
-
-void GeolocationImpl::StartListeningForUpdates() {
- geolocation_subscription_ =
- GeolocationProvider::GetInstance()->AddLocationUpdateCallback(
- base::Bind(&GeolocationImpl::OnLocationUpdate,
- base::Unretained(this)),
- high_accuracy_);
-}
-
-void GeolocationImpl::SetHighAccuracy(bool high_accuracy) {
- UMA_HISTOGRAM_BOOLEAN(
- "Geolocation.GeolocationDispatcherHostImpl.EnableHighAccuracy",
- high_accuracy);
- high_accuracy_ = high_accuracy;
-
- if (ValidateGeoposition(position_override_)) {
- OnLocationUpdate(position_override_);
- return;
- }
-
- StartListeningForUpdates();
-}
-
-void GeolocationImpl::QueryNextPosition(QueryNextPositionCallback callback) {
- if (!position_callback_.is_null()) {
- DVLOG(1) << "Overlapped call to QueryNextPosition!";
- OnConnectionError(); // Simulate a connection error.
- return;
- }
-
- position_callback_ = std::move(callback);
-
- if (has_position_to_report_)
- ReportCurrentPosition();
-}
-
-void GeolocationImpl::SetOverride(const mojom::Geoposition& position) {
- if (!position_callback_.is_null())
- ReportCurrentPosition();
- position_override_ = position;
- if (!ValidateGeoposition(position_override_))
- ResumeUpdates();
-
- geolocation_subscription_.reset();
-
- OnLocationUpdate(position_override_);
-}
-
-void GeolocationImpl::ClearOverride() {
- position_override_ = mojom::Geoposition();
- StartListeningForUpdates();
-}
-
-void GeolocationImpl::OnConnectionError() {
- context_->OnConnectionError(this);
-
- // The above call deleted this instance, so the only safe thing to do is
- // return.
-}
-
-void GeolocationImpl::OnLocationUpdate(const mojom::Geoposition& position) {
- RecordGeopositionErrorCode(position.error_code);
- DCHECK(context_);
-
- current_position_ = position;
- current_position_.valid = ValidateGeoposition(position);
- has_position_to_report_ = true;
-
- if (!position_callback_.is_null())
- ReportCurrentPosition();
-}
-
-void GeolocationImpl::ReportCurrentPosition() {
- std::move(position_callback_).Run(current_position_.Clone());
- has_position_to_report_ = false;
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/geolocation_impl.h b/chromium/device/geolocation/geolocation_impl.h
deleted file mode 100644
index d8d9f0ab99e..00000000000
--- a/chromium/device/geolocation/geolocation_impl.h
+++ /dev/null
@@ -1,78 +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_GEOLOCATION_GEOLOCATION_IMPL_H_
-#define DEVICE_GEOLOCATION_GEOLOCATION_IMPL_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "device/geolocation/geolocation_provider_impl.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "services/device/public/mojom/geolocation.mojom.h"
-
-namespace device {
-
-class GeolocationProvider;
-class GeolocationContext;
-
-// Implements the Geolocation Mojo interface.
-class GeolocationImpl : public mojom::Geolocation {
- public:
- // |context| must outlive this object.
- GeolocationImpl(mojo::InterfaceRequest<mojom::Geolocation> request,
- GeolocationContext* context);
- ~GeolocationImpl() override;
-
- // Starts listening for updates.
- void StartListeningForUpdates();
-
- // Pauses and resumes sending updates to the client of this instance.
- void PauseUpdates();
- void ResumeUpdates();
-
- // Enables and disables geolocation override.
- void SetOverride(const mojom::Geoposition& position);
- void ClearOverride();
-
- private:
- // mojom::Geolocation:
- void SetHighAccuracy(bool high_accuracy) override;
- void QueryNextPosition(QueryNextPositionCallback callback) override;
-
- void OnConnectionError();
-
- void OnLocationUpdate(const mojom::Geoposition& position);
- void ReportCurrentPosition();
-
- // The binding between this object and the other end of the pipe.
- mojo::Binding<mojom::Geolocation> binding_;
-
- // Owns this object.
- GeolocationContext* context_;
-
- // Token that unsubscribes from GeolocationProvider updates when destroyed.
- std::unique_ptr<GeolocationProvider::Subscription> geolocation_subscription_;
-
- // The callback passed to QueryNextPosition.
- QueryNextPositionCallback position_callback_;
-
- // Valid if SetOverride() has been called and ClearOverride() has not
- // subsequently been called.
- mojom::Geoposition position_override_;
-
- mojom::Geoposition current_position_;
-
- // Whether this instance is currently observing location updates with high
- // accuracy.
- bool high_accuracy_;
-
- bool has_position_to_report_;
-
- DISALLOW_COPY_AND_ASSIGN(GeolocationImpl);
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_GEOLOCATION_IMPL_H_
diff --git a/chromium/device/geolocation/geolocation_provider.h b/chromium/device/geolocation/geolocation_provider.h
deleted file mode 100644
index 13e0360b649..00000000000
--- a/chromium/device/geolocation/geolocation_provider.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (c) 2012 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_GEOLOCATION_GEOLOCATION_PROVIDER_H_
-#define DEVICE_GEOLOCATION_GEOLOCATION_PROVIDER_H_
-
-#include <memory>
-
-#include "base/callback_list.h"
-#include "device/geolocation/geolocation_export.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "services/device/public/mojom/geoposition.mojom.h"
-
-namespace device {
-
-// This is the main API to the geolocation subsystem. The application will hold
-// a single instance of this class and can register multiple clients to be
-// notified of location changes:
-// * Callbacks are registered by AddLocationUpdateCallback() and will keep
-// receiving updates until the returned subscription object is destructed.
-// The application must instantiate the GeolocationProvider on the UI thread and
-// must communicate with it on the same thread.
-// The underlying location arbitrator will only be enabled whilst there is at
-// least one registered observer or pending callback (and only after
-// mojom::UserDidOptIntoLocationServices() which is implemented by
-// GeolocationProviderImpl). The arbitrator and the location providers it uses
-// run on a separate Geolocation thread.
-// TODO(ke.he@intel.com): With the proceeding of the servicification of
-// geolocation, the geolocation core will be moved into //services/device and as
-// a internal part of Device Service. This geolocation_provider.h will also be
-// removed.
-class GeolocationProvider {
- public:
- DEVICE_GEOLOCATION_EXPORT static GeolocationProvider* GetInstance();
-
- // Callback type for a function that asynchronously produces a
- // URLRequestContextGetter.
- using RequestContextProducer = base::RepeatingCallback<void(
- base::OnceCallback<void(scoped_refptr<net::URLRequestContextGetter>)>)>;
-
- typedef base::Callback<void(const mojom::Geoposition&)>
- LocationUpdateCallback;
- typedef base::CallbackList<void(const mojom::Geoposition&)>::Subscription
- Subscription;
-
- // |enable_high_accuracy| is used as a 'hint' for the provider preferences for
- // this particular observer, however the observer could receive updates for
- // best available locations from any active provider whilst it is registered.
- virtual std::unique_ptr<Subscription> AddLocationUpdateCallback(
- const LocationUpdateCallback& callback,
- bool enable_high_accuracy) = 0;
-
- virtual bool HighAccuracyLocationInUse() = 0;
-
- // Overrides the current location for testing.
- //
- // Overrides the location for automation/testing. Suppresses any further
- // updates from the actual providers and sends an update with the overridden
- // position to all registered clients.
- //
- // Do not use this function in unit tests. The function instantiates the
- // singleton geolocation stack in the background and manipulates it to report
- // a fake location. Neither step can be undone, breaking unit test isolation
- // (crbug.com/125931).
- virtual void OverrideLocationForTesting(
- const mojom::Geoposition& position) = 0;
-
- protected:
- virtual ~GeolocationProvider() {}
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_GEOLOCATION_PROVIDER_H_
diff --git a/chromium/device/geolocation/geolocation_provider_impl.cc b/chromium/device/geolocation/geolocation_provider_impl.cc
deleted file mode 100644
index e30b99d6edd..00000000000
--- a/chromium/device/geolocation/geolocation_provider_impl.cc
+++ /dev/null
@@ -1,226 +0,0 @@
-// Copyright (c) 2012 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/geolocation/geolocation_provider_impl.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/callback.h"
-#include "base/lazy_instance.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/singleton.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "device/geolocation/location_arbitrator.h"
-#include "device/geolocation/public/cpp/geoposition.h"
-
-namespace device {
-
-namespace {
-base::LazyInstance<CustomLocationProviderCallback>::Leaky
- g_custom_location_provider_callback = LAZY_INSTANCE_INITIALIZER;
-base::LazyInstance<GeolocationProvider::RequestContextProducer>::Leaky
- g_request_context_producer = LAZY_INSTANCE_INITIALIZER;
-base::LazyInstance<std::string>::Leaky g_api_key = LAZY_INSTANCE_INITIALIZER;
-} // namespace
-
-// static
-GeolocationProvider* GeolocationProvider::GetInstance() {
- return GeolocationProviderImpl::GetInstance();
-}
-
-// static
-void GeolocationProviderImpl::SetGeolocationGlobals(
- const GeolocationProvider::RequestContextProducer request_context_producer,
- const std::string& api_key,
- const CustomLocationProviderCallback& callback) {
- g_request_context_producer.Get() = request_context_producer;
- g_api_key.Get() = api_key;
- g_custom_location_provider_callback.Get() = callback;
-}
-
-std::unique_ptr<GeolocationProvider::Subscription>
-GeolocationProviderImpl::AddLocationUpdateCallback(
- const LocationUpdateCallback& callback,
- bool enable_high_accuracy) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
- std::unique_ptr<GeolocationProvider::Subscription> subscription;
- if (enable_high_accuracy) {
- subscription = high_accuracy_callbacks_.Add(callback);
- } else {
- subscription = low_accuracy_callbacks_.Add(callback);
- }
-
- OnClientsChanged();
- if (ValidateGeoposition(position_) ||
- position_.error_code != mojom::Geoposition::ErrorCode::NONE) {
- callback.Run(position_);
- }
-
- return subscription;
-}
-
-bool GeolocationProviderImpl::HighAccuracyLocationInUse() {
- return !high_accuracy_callbacks_.empty();
-}
-
-void GeolocationProviderImpl::OverrideLocationForTesting(
- const mojom::Geoposition& position) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
- ignore_location_updates_ = true;
- NotifyClients(position);
-}
-
-void GeolocationProviderImpl::OnLocationUpdate(
- const LocationProvider* provider,
- const mojom::Geoposition& position) {
- DCHECK(OnGeolocationThread());
- // Will be true only in testing.
- if (ignore_location_updates_)
- return;
- main_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&GeolocationProviderImpl::NotifyClients,
- base::Unretained(this), position));
-}
-
-// static
-GeolocationProviderImpl* GeolocationProviderImpl::GetInstance() {
- return base::Singleton<GeolocationProviderImpl>::get();
-}
-
-void GeolocationProviderImpl::BindGeolocationControlRequest(
- mojom::GeolocationControlRequest request) {
- // The |binding_| has been bound already here means that more than one
- // GeolocationPermissionContext in chrome tried to bind to Device Service.
- // We only bind the first request. See more info in geolocation_control.mojom.
- if (!binding_.is_bound())
- binding_.Bind(std::move(request));
-}
-
-void GeolocationProviderImpl::UserDidOptIntoLocationServices() {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
- bool was_permission_granted = user_did_opt_into_location_services_;
- user_did_opt_into_location_services_ = true;
- if (IsRunning() && !was_permission_granted)
- InformProvidersPermissionGranted();
-}
-
-GeolocationProviderImpl::GeolocationProviderImpl()
- : base::Thread("Geolocation"),
- user_did_opt_into_location_services_(false),
- ignore_location_updates_(false),
- main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
- binding_(this) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
- high_accuracy_callbacks_.set_removal_callback(base::Bind(
- &GeolocationProviderImpl::OnClientsChanged, base::Unretained(this)));
- low_accuracy_callbacks_.set_removal_callback(base::Bind(
- &GeolocationProviderImpl::OnClientsChanged, base::Unretained(this)));
-}
-
-GeolocationProviderImpl::~GeolocationProviderImpl() {
- Stop();
- DCHECK(!arbitrator_);
-}
-
-void GeolocationProviderImpl::SetArbitratorForTesting(
- std::unique_ptr<LocationProvider> arbitrator) {
- arbitrator_ = std::move(arbitrator);
-}
-
-bool GeolocationProviderImpl::OnGeolocationThread() const {
- return task_runner()->BelongsToCurrentThread();
-}
-
-void GeolocationProviderImpl::OnClientsChanged() {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
- base::Closure task;
- if (high_accuracy_callbacks_.empty() && low_accuracy_callbacks_.empty()) {
- DCHECK(IsRunning());
- if (!ignore_location_updates_) {
- // We have no more observers, so we clear the cached geoposition so that
- // when the next observer is added we will not provide a stale position.
- position_ = mojom::Geoposition();
- }
- task = base::Bind(&GeolocationProviderImpl::StopProviders,
- base::Unretained(this));
- } else {
- if (!IsRunning()) {
- Start();
- if (user_did_opt_into_location_services_)
- InformProvidersPermissionGranted();
- }
- // Determine a set of options that satisfies all clients.
- bool enable_high_accuracy = !high_accuracy_callbacks_.empty();
-
- // Send the current options to the providers as they may have changed.
- task = base::Bind(&GeolocationProviderImpl::StartProviders,
- base::Unretained(this), enable_high_accuracy);
- }
-
- task_runner()->PostTask(FROM_HERE, task);
-}
-
-void GeolocationProviderImpl::StopProviders() {
- DCHECK(OnGeolocationThread());
- DCHECK(arbitrator_);
- arbitrator_->StopProvider();
-}
-
-void GeolocationProviderImpl::StartProviders(bool enable_high_accuracy) {
- DCHECK(OnGeolocationThread());
- DCHECK(arbitrator_);
- arbitrator_->StartProvider(enable_high_accuracy);
-}
-
-void GeolocationProviderImpl::InformProvidersPermissionGranted() {
- DCHECK(IsRunning());
- if (!OnGeolocationThread()) {
- task_runner()->PostTask(
- FROM_HERE,
- base::BindOnce(
- &GeolocationProviderImpl::InformProvidersPermissionGranted,
- base::Unretained(this)));
- return;
- }
- DCHECK(OnGeolocationThread());
- DCHECK(arbitrator_);
- arbitrator_->OnPermissionGranted();
-}
-
-void GeolocationProviderImpl::NotifyClients(
- const mojom::Geoposition& position) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
- DCHECK(ValidateGeoposition(position) ||
- position.error_code != mojom::Geoposition::ErrorCode::NONE);
- position_ = position;
- high_accuracy_callbacks_.Notify(position_);
- low_accuracy_callbacks_.Notify(position_);
-}
-
-void GeolocationProviderImpl::Init() {
- DCHECK(OnGeolocationThread());
-
- if (arbitrator_)
- return;
-
- LocationProvider::LocationProviderUpdateCallback callback = base::Bind(
- &GeolocationProviderImpl::OnLocationUpdate, base::Unretained(this));
-
- arbitrator_ = std::make_unique<LocationArbitrator>(
- g_custom_location_provider_callback.Get(),
- g_request_context_producer.Get(), g_api_key.Get());
- arbitrator_->SetUpdateCallback(callback);
-}
-
-void GeolocationProviderImpl::CleanUp() {
- DCHECK(OnGeolocationThread());
- arbitrator_.reset();
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/geolocation_provider_impl.h b/chromium/device/geolocation/geolocation_provider_impl.h
deleted file mode 100644
index 21d05e87b12..00000000000
--- a/chromium/device/geolocation/geolocation_provider_impl.h
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright (c) 2012 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_GEOLOCATION_GEOLOCATION_PROVIDER_IMPL_H_
-#define DEVICE_GEOLOCATION_GEOLOCATION_PROVIDER_IMPL_H_
-
-#include <list>
-#include <memory>
-#include <vector>
-
-#include "base/callback_forward.h"
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/threading/thread.h"
-#include "device/geolocation/geolocation_export.h"
-#include "device/geolocation/geolocation_provider.h"
-#include "device/geolocation/public/cpp/location_provider.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "services/device/public/mojom/geolocation_control.mojom.h"
-#include "services/device/public/mojom/geoposition.mojom.h"
-
-namespace base {
-template <typename Type>
-struct DefaultSingletonTraits;
-class SingleThreadTaskRunner;
-}
-
-namespace device {
-
-// Callback that returns the embedder's custom location provider. This callback
-// is provided to the Device Service by its embedder.
-using CustomLocationProviderCallback =
- base::Callback<std::unique_ptr<LocationProvider>()>;
-
-class DEVICE_GEOLOCATION_EXPORT GeolocationProviderImpl
- : public GeolocationProvider,
- public mojom::GeolocationControl,
- public base::Thread {
- public:
- // GeolocationProvider implementation:
- std::unique_ptr<GeolocationProvider::Subscription> AddLocationUpdateCallback(
- const LocationUpdateCallback& callback,
- bool enable_high_accuracy) override;
- bool HighAccuracyLocationInUse() override;
- void OverrideLocationForTesting(const mojom::Geoposition& position) override;
-
- // Callback from the LocationArbitrator. Public for testing.
- void OnLocationUpdate(const LocationProvider* provider,
- const mojom::Geoposition& position);
-
- // Gets a pointer to the singleton instance of the location relayer, which
- // is in turn bound to the browser's global context objects. This must only be
- // called on the UI thread so that the GeolocationProviderImpl is always
- // instantiated on the same thread. Ownership is NOT returned.
- static GeolocationProviderImpl* GetInstance();
-
- // Optional: Provide a callback to produce a request context for network
- // geolocation requests. Provide a Google API key for network geolocation
- // requests. Provide a callback which can return a custom location provider
- // from embedder. Call before using Init() on the singleton GetInstance().
- static void SetGeolocationGlobals(
- const GeolocationProvider::RequestContextProducer
- request_context_producer,
- const std::string& api_key,
- const CustomLocationProviderCallback& callback);
-
- void BindGeolocationControlRequest(mojom::GeolocationControlRequest request);
-
- // mojom::GeolocationControl implementation:
- void UserDidOptIntoLocationServices() override;
-
- bool user_did_opt_into_location_services_for_testing() {
- return user_did_opt_into_location_services_;
- }
-
- // Safe to call while there are no GeolocationProviderImpl clients
- // registered.
- void SetArbitratorForTesting(std::unique_ptr<LocationProvider> arbitrator);
-
- private:
- friend struct base::DefaultSingletonTraits<GeolocationProviderImpl>;
- GeolocationProviderImpl();
- ~GeolocationProviderImpl() override;
-
- bool OnGeolocationThread() const;
-
- // Start and stop providers as needed when clients are added or removed.
- void OnClientsChanged();
-
- // Stops the providers when there are no more registered clients. Note that
- // once the Geolocation thread is started, it will stay alive (but sitting
- // idle without any pending messages).
- void StopProviders();
-
- // Starts the geolocation providers or updates their options (delegates to
- // arbitrator).
- void StartProviders(bool enable_high_accuracy);
-
- // Updates the providers on the geolocation thread, which must be running.
- void InformProvidersPermissionGranted();
-
- // Notifies all registered clients that a position update is available.
- void NotifyClients(const mojom::Geoposition& position);
-
- // Thread
- void Init() override;
- void CleanUp() override;
-
- base::CallbackList<void(const mojom::Geoposition&)> high_accuracy_callbacks_;
- base::CallbackList<void(const mojom::Geoposition&)> low_accuracy_callbacks_;
-
- bool user_did_opt_into_location_services_;
- mojom::Geoposition position_;
-
- // True only in testing, where we want to use a custom position.
- bool ignore_location_updates_;
-
- // Used to PostTask()s from the geolocation thread to caller thread.
- const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
-
- // Only to be used on the geolocation thread.
- std::unique_ptr<LocationProvider> arbitrator_;
-
- mojo::Binding<mojom::GeolocationControl> binding_;
-
- DISALLOW_COPY_AND_ASSIGN(GeolocationProviderImpl);
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_GEOLOCATION_PROVIDER_IMPL_H_
diff --git a/chromium/device/geolocation/geolocation_provider_impl_unittest.cc b/chromium/device/geolocation/geolocation_provider_impl_unittest.cc
deleted file mode 100644
index 9f5f44a4458..00000000000
--- a/chromium/device/geolocation/geolocation_provider_impl_unittest.cc
+++ /dev/null
@@ -1,261 +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/geolocation/geolocation_provider_impl.h"
-
-#include <memory>
-
-#include "base/at_exit.h"
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/location.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
-#include "base/strings/string16.h"
-#include "base/test/scoped_task_environment.h"
-#include "base/threading/thread_checker.h"
-#include "base/time/time.h"
-#include "device/geolocation/fake_location_provider.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::MakeMatcher;
-using testing::Matcher;
-using testing::MatcherInterface;
-using testing::MatchResultListener;
-
-namespace device {
-namespace {
-
-class GeolocationObserver {
- public:
- virtual ~GeolocationObserver() = default;
- virtual void OnLocationUpdate(const mojom::Geoposition& position) = 0;
-};
-
-class MockGeolocationObserver : public GeolocationObserver {
- public:
- MOCK_METHOD1(OnLocationUpdate, void(const mojom::Geoposition& position));
-};
-
-class AsyncMockGeolocationObserver : public MockGeolocationObserver {
- public:
- void OnLocationUpdate(const mojom::Geoposition& position) override {
- MockGeolocationObserver::OnLocationUpdate(position);
- base::RunLoop::QuitCurrentWhenIdleDeprecated();
- }
-};
-
-class MockGeolocationCallbackWrapper {
- public:
- MOCK_METHOD1(Callback, void(const mojom::Geoposition& position));
-};
-
-class GeopositionEqMatcher
- : public MatcherInterface<const mojom::Geoposition&> {
- public:
- explicit GeopositionEqMatcher(const mojom::Geoposition& expected)
- : expected_(expected) {}
-
- bool MatchAndExplain(const mojom::Geoposition& actual,
- MatchResultListener* listener) const override {
- return actual.latitude == expected_.latitude &&
- actual.longitude == expected_.longitude &&
- actual.altitude == expected_.altitude &&
- actual.accuracy == expected_.accuracy &&
- actual.altitude_accuracy == expected_.altitude_accuracy &&
- actual.heading == expected_.heading &&
- actual.speed == expected_.speed &&
- actual.timestamp == expected_.timestamp &&
- actual.error_code == expected_.error_code &&
- actual.error_message == expected_.error_message;
- }
-
- void DescribeTo(::std::ostream* os) const override {
- *os << "which matches the expected position";
- }
-
- void DescribeNegationTo(::std::ostream* os) const override {
- *os << "which does not match the expected position";
- }
-
- private:
- mojom::Geoposition expected_;
-
- DISALLOW_COPY_AND_ASSIGN(GeopositionEqMatcher);
-};
-
-Matcher<const mojom::Geoposition&> GeopositionEq(
- const mojom::Geoposition& expected) {
- return MakeMatcher(new GeopositionEqMatcher(expected));
-}
-
-void DummyFunction(const LocationProvider* provider,
- const mojom::Geoposition& position) {}
-
-} // namespace
-
-class GeolocationProviderTest : public testing::Test {
- protected:
- GeolocationProviderTest()
- : scoped_task_environment_(
- base::test::ScopedTaskEnvironment::MainThreadType::UI),
- arbitrator_(new FakeLocationProvider) {
- provider()->SetArbitratorForTesting(base::WrapUnique(arbitrator_));
- }
-
- ~GeolocationProviderTest() override = default;
-
- GeolocationProviderImpl* provider() {
- return GeolocationProviderImpl::GetInstance();
- }
-
- FakeLocationProvider* arbitrator() { return arbitrator_; }
-
- // Called on test thread.
- bool ProvidersStarted();
- void SendMockLocation(const mojom::Geoposition& position);
-
- private:
- // Called on provider thread.
- void GetProvidersStarted();
-
- // |at_exit| must be initialized before all other variables so that it is
- // available to register with Singletons and can handle tear down when the
- // test completes.
- base::ShadowingAtExitManager at_exit_;
-
- base::test::ScopedTaskEnvironment scoped_task_environment_;
-
- base::ThreadChecker thread_checker_;
-
- // Owned by the GeolocationProviderImpl class.
- FakeLocationProvider* arbitrator_;
-
- // True if |arbitrator_| is started.
- bool is_started_;
-
- DISALLOW_COPY_AND_ASSIGN(GeolocationProviderTest);
-};
-
-bool GeolocationProviderTest::ProvidersStarted() {
- DCHECK(provider()->IsRunning());
- DCHECK(thread_checker_.CalledOnValidThread());
-
- provider()->task_runner()->PostTaskAndReply(
- FROM_HERE,
- base::BindOnce(&GeolocationProviderTest::GetProvidersStarted,
- base::Unretained(this)),
- base::RunLoop::QuitCurrentWhenIdleClosureDeprecated());
- base::RunLoop().Run();
- return is_started_;
-}
-
-void GeolocationProviderTest::GetProvidersStarted() {
- DCHECK(provider()->task_runner()->BelongsToCurrentThread());
- is_started_ = arbitrator()->state() != FakeLocationProvider::STOPPED;
-}
-
-void GeolocationProviderTest::SendMockLocation(
- const mojom::Geoposition& position) {
- DCHECK(provider()->IsRunning());
- DCHECK(thread_checker_.CalledOnValidThread());
- provider()->task_runner()->PostTask(
- FROM_HERE,
- base::BindOnce(&GeolocationProviderImpl::OnLocationUpdate,
- base::Unretained(provider()), arbitrator_, position));
-}
-
-// Regression test for http://crbug.com/59377
-TEST_F(GeolocationProviderTest, OnPermissionGrantedWithoutObservers) {
- // Clear |provider|'s arbitrator so the default arbitrator can be used.
- provider()->SetArbitratorForTesting(nullptr);
- EXPECT_FALSE(provider()->user_did_opt_into_location_services_for_testing());
- provider()->UserDidOptIntoLocationServices();
- EXPECT_TRUE(provider()->user_did_opt_into_location_services_for_testing());
-}
-
-TEST_F(GeolocationProviderTest, StartStop) {
- EXPECT_FALSE(provider()->IsRunning());
- std::unique_ptr<GeolocationProvider::Subscription> subscription =
- provider()->AddLocationUpdateCallback(
- base::Bind(&DummyFunction, arbitrator()), false);
- EXPECT_TRUE(provider()->IsRunning());
- EXPECT_TRUE(ProvidersStarted());
-
- subscription.reset();
-
- EXPECT_FALSE(ProvidersStarted());
- EXPECT_TRUE(provider()->IsRunning());
-}
-
-TEST_F(GeolocationProviderTest, StalePositionNotSent) {
- mojom::Geoposition first_position;
- first_position.latitude = 12;
- first_position.longitude = 34;
- first_position.accuracy = 56;
- first_position.timestamp = base::Time::Now();
-
- AsyncMockGeolocationObserver first_observer;
- GeolocationProviderImpl::LocationUpdateCallback first_callback =
- base::Bind(&MockGeolocationObserver::OnLocationUpdate,
- base::Unretained(&first_observer));
- EXPECT_CALL(first_observer, OnLocationUpdate(GeopositionEq(first_position)));
- std::unique_ptr<GeolocationProvider::Subscription> subscription =
- provider()->AddLocationUpdateCallback(first_callback, false);
- SendMockLocation(first_position);
- base::RunLoop().Run();
-
- subscription.reset();
-
- mojom::Geoposition second_position;
- second_position.latitude = 13;
- second_position.longitude = 34;
- second_position.accuracy = 56;
- second_position.timestamp = base::Time::Now();
-
- AsyncMockGeolocationObserver second_observer;
-
- // After adding a second observer, check that no unexpected position update
- // is sent.
- EXPECT_CALL(second_observer, OnLocationUpdate(testing::_)).Times(0);
- GeolocationProviderImpl::LocationUpdateCallback second_callback =
- base::Bind(&MockGeolocationObserver::OnLocationUpdate,
- base::Unretained(&second_observer));
- std::unique_ptr<GeolocationProvider::Subscription> subscription2 =
- provider()->AddLocationUpdateCallback(second_callback, false);
- base::RunLoop().RunUntilIdle();
-
- // The second observer should receive the new position now.
- EXPECT_CALL(second_observer,
- OnLocationUpdate(GeopositionEq(second_position)));
- SendMockLocation(second_position);
- base::RunLoop().Run();
-
- subscription2.reset();
- EXPECT_FALSE(ProvidersStarted());
-}
-
-TEST_F(GeolocationProviderTest, OverrideLocationForTesting) {
- mojom::Geoposition position;
- position.error_code = mojom::Geoposition::ErrorCode::POSITION_UNAVAILABLE;
- provider()->OverrideLocationForTesting(position);
- // Adding an observer when the location is overridden should synchronously
- // update the observer with our overridden position.
- MockGeolocationObserver mock_observer;
- EXPECT_CALL(mock_observer, OnLocationUpdate(GeopositionEq(position)));
- GeolocationProviderImpl::LocationUpdateCallback callback =
- base::Bind(&MockGeolocationObserver::OnLocationUpdate,
- base::Unretained(&mock_observer));
- std::unique_ptr<GeolocationProvider::Subscription> subscription =
- provider()->AddLocationUpdateCallback(callback, false);
- subscription.reset();
- // Wait for the providers to be stopped now that all clients are gone.
- EXPECT_FALSE(ProvidersStarted());
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/location_api_adapter_android.cc b/chromium/device/geolocation/location_api_adapter_android.cc
deleted file mode 100644
index b7a29862823..00000000000
--- a/chromium/device/geolocation/location_api_adapter_android.cc
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright (c) 2012 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/geolocation/location_api_adapter_android.h"
-
-#include "base/android/jni_android.h"
-#include "base/android/jni_string.h"
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "device/geolocation/location_provider_android.h"
-#include "jni/LocationProviderAdapter_jni.h"
-
-using base::android::AttachCurrentThread;
-using base::android::JavaParamRef;
-using device::LocationApiAdapterAndroid;
-
-static void JNI_LocationProviderAdapter_NewLocationAvailable(
- JNIEnv* env,
- const JavaParamRef<jclass>&,
- jdouble latitude,
- jdouble longitude,
- jdouble time_stamp,
- jboolean has_altitude,
- jdouble altitude,
- jboolean has_accuracy,
- jdouble accuracy,
- jboolean has_heading,
- jdouble heading,
- jboolean has_speed,
- jdouble speed) {
- LocationApiAdapterAndroid::OnNewLocationAvailable(
- latitude, longitude, time_stamp, has_altitude, altitude, has_accuracy,
- accuracy, has_heading, heading, has_speed, speed);
-}
-
-static void JNI_LocationProviderAdapter_NewErrorAvailable(
- JNIEnv* env,
- const JavaParamRef<jclass>&,
- const JavaParamRef<jstring>& message) {
- LocationApiAdapterAndroid::OnNewErrorAvailable(env, message);
-}
-
-namespace device {
-
-void LocationApiAdapterAndroid::Start(OnGeopositionCB on_geoposition_callback,
- bool high_accuracy) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(on_geoposition_callback);
-
- JNIEnv* env = AttachCurrentThread();
-
- if (!on_geoposition_callback_) {
- on_geoposition_callback_ = on_geoposition_callback;
-
- DCHECK(java_location_provider_adapter_.is_null());
- java_location_provider_adapter_.Reset(
- Java_LocationProviderAdapter_create(env));
- }
-
- // At this point we should have all our pre-conditions ready, and they'd only
- // change in Stop() which must be called on the same thread as here. We'll
- // start receiving notifications from java in the main thread looper until
- // Stop() is called.
- DCHECK(!java_location_provider_adapter_.is_null());
- Java_LocationProviderAdapter_start(env, java_location_provider_adapter_,
- high_accuracy);
-}
-
-void LocationApiAdapterAndroid::Stop() {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (!on_geoposition_callback_)
- return;
-
- on_geoposition_callback_.Reset();
-
- JNIEnv* env = AttachCurrentThread();
- Java_LocationProviderAdapter_stop(env, java_location_provider_adapter_);
- java_location_provider_adapter_.Reset();
-}
-
-// static
-void LocationApiAdapterAndroid::OnNewLocationAvailable(double latitude,
- double longitude,
- double time_stamp,
- bool has_altitude,
- double altitude,
- bool has_accuracy,
- double accuracy,
- bool has_heading,
- double heading,
- bool has_speed,
- double speed) {
- mojom::Geoposition position;
- position.latitude = latitude;
- position.longitude = longitude;
- position.timestamp = base::Time::FromDoubleT(time_stamp);
- if (has_altitude)
- position.altitude = altitude;
- if (has_accuracy)
- position.accuracy = accuracy;
- if (has_heading)
- position.heading = heading;
- if (has_speed)
- position.speed = speed;
-
- LocationApiAdapterAndroid* self = GetInstance();
- self->task_runner_->PostTask(
- FROM_HERE, base::Bind(&LocationApiAdapterAndroid::NotifyNewGeoposition,
- base::Unretained(self), position));
-}
-
-// static
-void LocationApiAdapterAndroid::OnNewErrorAvailable(JNIEnv* env,
- jstring message) {
- mojom::Geoposition position_error;
- position_error.error_code =
- mojom::Geoposition::ErrorCode::POSITION_UNAVAILABLE;
- position_error.error_message =
- base::android::ConvertJavaStringToUTF8(env, message);
-
- LocationApiAdapterAndroid* self = GetInstance();
- self->task_runner_->PostTask(
- FROM_HERE, base::Bind(&LocationApiAdapterAndroid::NotifyNewGeoposition,
- base::Unretained(self), position_error));
-}
-
-// static
-LocationApiAdapterAndroid* LocationApiAdapterAndroid::GetInstance() {
- return base::Singleton<LocationApiAdapterAndroid>::get();
-}
-
-LocationApiAdapterAndroid::LocationApiAdapterAndroid()
- : task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
-
-LocationApiAdapterAndroid::~LocationApiAdapterAndroid() {
- DCHECK(thread_checker_.CalledOnValidThread());
-}
-
-void LocationApiAdapterAndroid::NotifyNewGeoposition(
- const mojom::Geoposition& geoposition) {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (on_geoposition_callback_)
- on_geoposition_callback_.Run(geoposition);
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/location_api_adapter_android.h b/chromium/device/geolocation/location_api_adapter_android.h
deleted file mode 100644
index 8ae0701dabd..00000000000
--- a/chromium/device/geolocation/location_api_adapter_android.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (c) 2012 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_GEOLOCATION_LOCATION_API_ADAPTER_ANDROID_H_
-#define DEVICE_GEOLOCATION_LOCATION_API_ADAPTER_ANDROID_H_
-
-#include "base/android/jni_weak_ref.h"
-#include "base/android/scoped_java_ref.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/singleton.h"
-#include "base/synchronization/lock.h"
-#include "base/threading/thread_checker.h"
-#include "services/device/public/mojom/geoposition.mojom.h"
-
-namespace base {
-class SingleThreadTaskRunner;
-}
-
-namespace device {
-
-// Interacts with JNI and reports back to |on_geoposition_callback_|. This class
-// creates a LocationProvider java object and listens for updates.
-// The simplified flow is:
-// - GeolocationProvider runs in a Geolocation |task_runner_| and fetches
-// geolocation data from a LocationProvider.
-// - LocationApiAdapterAndroid calls via JNI and uses the main thread Looper
-// in the java side to listen for location updates. We then bounce these
-// updates to the Geolocation |task_runner_|.
-//
-// Note that LocationApiAdapterAndroid is a singleton and there's at most only
-// one call to Start().
-class LocationApiAdapterAndroid {
- public:
- using OnGeopositionCB = base::Callback<void(const mojom::Geoposition&)>;
-
- // Starts the underlying location provider.
- // Called on |task_runner_|.
- void Start(OnGeopositionCB on_geoposition_callback, bool high_accuracy);
-
- // Stops the underlying location provider. Called on |task_runner_|.
- void Stop();
-
- // Called by JNI on its thread looper.
- static void OnNewLocationAvailable(double latitude,
- double longitude,
- double time_stamp,
- bool has_altitude,
- double altitude,
- bool has_accuracy,
- double accuracy,
- bool has_heading,
- double heading,
- bool has_speed,
- double speed);
- static void OnNewErrorAvailable(JNIEnv* env, jstring message);
-
- // Returns our singleton.
- static LocationApiAdapterAndroid* GetInstance();
-
- private:
- friend struct base::DefaultSingletonTraits<LocationApiAdapterAndroid>;
- LocationApiAdapterAndroid();
- ~LocationApiAdapterAndroid();
-
- // Calls |on_geoposition_callback_| with the new location.
- void NotifyNewGeoposition(const mojom::Geoposition& geoposition);
-
- base::android::ScopedJavaGlobalRef<jobject> java_location_provider_adapter_;
-
- // Valid between Start() and Stop().
- OnGeopositionCB on_geoposition_callback_;
-
- const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-
- base::ThreadChecker thread_checker_;
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_LOCATION_API_ADAPTER_ANDROID_H_
diff --git a/chromium/device/geolocation/location_arbitrator.cc b/chromium/device/geolocation/location_arbitrator.cc
deleted file mode 100644
index 61370c6f813..00000000000
--- a/chromium/device/geolocation/location_arbitrator.cc
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright (c) 2012 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/geolocation/location_arbitrator.h"
-
-#include <map>
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/memory/ptr_util.h"
-#include "build/build_config.h"
-#include "device/geolocation/network_location_provider.h"
-#include "device/geolocation/public/cpp/geoposition.h"
-#include "device/geolocation/wifi_polling_policy.h"
-
-namespace device {
-
-// To avoid oscillations, set this to twice the expected update interval of a
-// a GPS-type location provider (in case it misses a beat) plus a little.
-const base::TimeDelta LocationArbitrator::kFixStaleTimeoutTimeDelta =
- base::TimeDelta::FromSeconds(11);
-
-LocationArbitrator::LocationArbitrator(
- const CustomLocationProviderCallback& custom_location_provider_getter,
- GeolocationProvider::RequestContextProducer request_context_producer,
- const std::string& api_key)
- : custom_location_provider_getter_(custom_location_provider_getter),
- request_context_producer_(request_context_producer),
- api_key_(api_key),
- position_provider_(nullptr),
- is_permission_granted_(false),
- is_running_(false) {}
-
-LocationArbitrator::~LocationArbitrator() {
- // Release the global wifi polling policy state.
- WifiPollingPolicy::Shutdown();
-}
-
-bool LocationArbitrator::HasPermissionBeenGrantedForTest() const {
- return is_permission_granted_;
-}
-
-void LocationArbitrator::OnPermissionGranted() {
- is_permission_granted_ = true;
- for (const auto& provider : providers_)
- provider->OnPermissionGranted();
-}
-
-void LocationArbitrator::SetLastNetworkPosition(
- const mojom::Geoposition& position) {
- last_network_position_ = position;
-}
-
-const mojom::Geoposition& LocationArbitrator::GetLastNetworkPosition() {
- return last_network_position_;
-}
-
-void LocationArbitrator::StartProvider(bool enable_high_accuracy) {
- is_running_ = true;
- enable_high_accuracy_ = enable_high_accuracy;
-
- if (providers_.empty()) {
- RegisterSystemProvider();
-
- // Request a URLRequestContextGetter to use for network geolocation.
- if (!request_context_producer_.is_null()) {
- // Note: .Reset() will cancel any previous callback.
- request_context_response_callback_.Reset(
- base::Bind(&LocationArbitrator::OnRequestContextResponse,
- base::Unretained(this)));
- // Invoke callback to obtain a URL request context.
- request_context_producer_.Run(
- request_context_response_callback_.callback());
- return;
- }
- }
- DoStartProviders();
-}
-
-void LocationArbitrator::DoStartProviders() {
- if (providers_.empty()) {
- // If no providers are available, we report an error to avoid
- // callers waiting indefinitely for a reply.
- mojom::Geoposition position;
- position.error_code = mojom::Geoposition::ErrorCode::POSITION_UNAVAILABLE;
- arbitrator_update_callback_.Run(this, position);
- return;
- }
- for (const auto& provider : providers_) {
- provider->StartProvider(enable_high_accuracy_);
- }
-}
-
-void LocationArbitrator::StopProvider() {
- // Reset the reference location state (provider+position)
- // so that future starts use fresh locations from
- // the newly constructed providers.
- position_provider_ = nullptr;
- position_ = mojom::Geoposition();
-
- providers_.clear();
- is_running_ = false;
-}
-
-void LocationArbitrator::OnRequestContextResponse(
- scoped_refptr<net::URLRequestContextGetter> context_getter) {
- if (context_getter != nullptr) {
- // Create a NetworkLocationProvider using the provided request context.
- RegisterProvider(
- NewNetworkLocationProvider(std::move(context_getter), api_key_));
- }
- DoStartProviders();
-}
-
-void LocationArbitrator::RegisterProvider(
- std::unique_ptr<LocationProvider> provider) {
- if (!provider)
- return;
- provider->SetUpdateCallback(base::Bind(&LocationArbitrator::OnLocationUpdate,
- base::Unretained(this)));
- if (is_permission_granted_)
- provider->OnPermissionGranted();
- providers_.push_back(std::move(provider));
-}
-
-void LocationArbitrator::RegisterSystemProvider() {
- std::unique_ptr<LocationProvider> provider;
- if (custom_location_provider_getter_)
- provider = custom_location_provider_getter_.Run();
-
- // Use the default system provider if the custom provider is null.
- if (!provider)
- provider = NewSystemLocationProvider();
- RegisterProvider(std::move(provider));
-}
-
-void LocationArbitrator::OnLocationUpdate(
- const LocationProvider* provider,
- const mojom::Geoposition& new_position) {
- DCHECK(ValidateGeoposition(new_position) ||
- new_position.error_code != mojom::Geoposition::ErrorCode::NONE);
- if (!IsNewPositionBetter(position_, new_position,
- provider == position_provider_))
- return;
- position_provider_ = provider;
- position_ = new_position;
- arbitrator_update_callback_.Run(this, position_);
-}
-
-const mojom::Geoposition& LocationArbitrator::GetPosition() {
- return position_;
-}
-
-void LocationArbitrator::SetUpdateCallback(
- const LocationProviderUpdateCallback& callback) {
- DCHECK(!callback.is_null());
- arbitrator_update_callback_ = callback;
-}
-
-std::unique_ptr<LocationProvider>
-LocationArbitrator::NewNetworkLocationProvider(
- scoped_refptr<net::URLRequestContextGetter> context,
- const std::string& api_key) {
- DCHECK(context != nullptr);
-#if defined(OS_ANDROID)
- // Android uses its own SystemLocationProvider.
- return nullptr;
-#else
- return std::make_unique<NetworkLocationProvider>(std::move(context), api_key,
- this);
-#endif
-}
-
-std::unique_ptr<LocationProvider>
-LocationArbitrator::NewSystemLocationProvider() {
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
- defined(OS_FUCHSIA)
- return nullptr;
-#else
- return device::NewSystemLocationProvider();
-#endif
-}
-
-base::Time LocationArbitrator::GetTimeNow() const {
- return base::Time::Now();
-}
-
-bool LocationArbitrator::IsNewPositionBetter(
- const mojom::Geoposition& old_position,
- const mojom::Geoposition& new_position,
- bool from_same_provider) const {
- // Updates location_info if it's better than what we currently have,
- // or if it's a newer update from the same provider.
- if (!ValidateGeoposition(old_position)) {
- // Older location wasn't locked.
- return true;
- }
- if (ValidateGeoposition(new_position)) {
- // New location is locked, let's check if it's any better.
- if (old_position.accuracy >= new_position.accuracy) {
- // Accuracy is better.
- return true;
- } else if (from_same_provider) {
- // Same provider, fresher location.
- return true;
- } else if (GetTimeNow() - old_position.timestamp >
- kFixStaleTimeoutTimeDelta) {
- // Existing fix is stale.
- return true;
- }
- }
- return false;
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/location_arbitrator.h b/chromium/device/geolocation/location_arbitrator.h
deleted file mode 100644
index 8e1e9eced54..00000000000
--- a/chromium/device/geolocation/location_arbitrator.h
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright (c) 2012 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_GEOLOCATION_LOCATION_ARBITRATOR_H_
-#define DEVICE_GEOLOCATION_LOCATION_ARBITRATOR_H_
-
-#include <stdint.h>
-#include <memory>
-#include <vector>
-
-#include "base/callback_forward.h"
-#include "base/cancelable_callback.h"
-#include "base/macros.h"
-#include "base/strings/string16.h"
-#include "base/time/time.h"
-#include "device/geolocation/geolocation_export.h"
-#include "device/geolocation/geolocation_provider_impl.h"
-#include "device/geolocation/network_location_provider.h"
-#include "device/geolocation/public/cpp/location_provider.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "services/device/public/mojom/geoposition.mojom.h"
-#include "url/gurl.h"
-
-namespace net {
-class URLRequestContextGetter;
-}
-
-namespace device {
-
-// This class is responsible for handling updates from multiple underlying
-// providers and resolving them to a single 'best' location fix at any given
-// moment.
-class DEVICE_GEOLOCATION_EXPORT LocationArbitrator
- : public LocationProvider,
- public NetworkLocationProvider::LastPositionCache {
- public:
- // The TimeDelta newer a location provider has to be that it's worth
- // switching to this location provider on the basis of it being fresher
- // (regardles of relative accuracy). Public for tests.
- static const base::TimeDelta kFixStaleTimeoutTimeDelta;
-
- // If the |custom_location_provider_getter| callback returns nullptr, then
- // LocationArbitrator uses the default system location provider.
- LocationArbitrator(
- const CustomLocationProviderCallback& custom_location_provider_getter,
- const GeolocationProvider::RequestContextProducer
- request_context_producer,
- const std::string& api_key);
- ~LocationArbitrator() override;
-
- static GURL DefaultNetworkProviderURL();
- bool HasPermissionBeenGrantedForTest() const;
-
- // LocationProvider implementation.
- void SetUpdateCallback(
- const LocationProviderUpdateCallback& callback) override;
- void StartProvider(bool enable_high_accuracy) override;
- void StopProvider() override;
- const mojom::Geoposition& GetPosition() override;
- void OnPermissionGranted() override;
-
- // NetworkLocationProvider::LastPositionCache implementation.
- void SetLastNetworkPosition(const mojom::Geoposition& position) override;
- const mojom::Geoposition& GetLastNetworkPosition() override;
-
- protected:
- // These functions are useful for injection of dependencies in derived
- // testing classes.
- virtual std::unique_ptr<LocationProvider> NewNetworkLocationProvider(
- scoped_refptr<net::URLRequestContextGetter> context,
- const std::string& api_key);
- virtual std::unique_ptr<LocationProvider> NewSystemLocationProvider();
- virtual base::Time GetTimeNow() const;
-
- private:
- friend class TestingLocationArbitrator;
-
- // Provider will either be added to |providers_| or
- // deleted on error (e.g. it fails to start).
- void RegisterProvider(std::unique_ptr<LocationProvider> provider);
- void RegisterSystemProvider();
-
- // Tells all registered providers to start.
- // If |providers_| is empty, immediately provides
- // Geoposition::ERROR_CODE_POSITION_UNAVAILABLE to the client via
- // |arbitrator_update_callback_|.
- void DoStartProviders();
-
- // Response callback for request_context_callback_.
- void OnRequestContextResponse(
- scoped_refptr<net::URLRequestContextGetter> context_getter);
-
- // Gets called when a provider has a new position.
- void OnLocationUpdate(const LocationProvider* provider,
- const mojom::Geoposition& new_position);
-
- // Returns true if |new_position| is an improvement over |old_position|.
- // Set |from_same_provider| to true if both the positions came from the same
- // provider.
- bool IsNewPositionBetter(const mojom::Geoposition& old_position,
- const mojom::Geoposition& new_position,
- bool from_same_provider) const;
-
- const CustomLocationProviderCallback custom_location_provider_getter_;
- const GeolocationProvider::RequestContextProducer request_context_producer_;
- const std::string api_key_;
-
- LocationProvider::LocationProviderUpdateCallback arbitrator_update_callback_;
-
- // CancelableCallback to prevent OnRequestContextReponse from being called
- // multiple times in case request_context_callback_ is invoked multiple times.
- base::CancelableCallback<void(scoped_refptr<net::URLRequestContextGetter>)>
- request_context_response_callback_;
-
- std::vector<std::unique_ptr<LocationProvider>> providers_;
- bool enable_high_accuracy_;
- // The provider which supplied the current |position_|
- const LocationProvider* position_provider_;
- bool is_permission_granted_;
- // The current best estimate of our position.
- mojom::Geoposition position_;
-
- // The most recent position estimate returned by the network location
- // provider. This must be preserved by LocationArbitrator so it is not lost
- // when the provider is destroyed in StopProvider.
- mojom::Geoposition last_network_position_;
-
- // Tracks whether providers should be running.
- bool is_running_;
-
- DISALLOW_COPY_AND_ASSIGN(LocationArbitrator);
-};
-
-// Factory functions for the various types of location provider to abstract
-// over the platform-dependent implementations.
-std::unique_ptr<LocationProvider> NewSystemLocationProvider();
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_LOCATION_ARBITRATOR_H_
diff --git a/chromium/device/geolocation/location_arbitrator_unittest.cc b/chromium/device/geolocation/location_arbitrator_unittest.cc
deleted file mode 100644
index ff9bcd470ff..00000000000
--- a/chromium/device/geolocation/location_arbitrator_unittest.cc
+++ /dev/null
@@ -1,451 +0,0 @@
-// Copyright (c) 2012 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/geolocation/location_arbitrator.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/memory/ptr_util.h"
-#include "base/test/scoped_task_environment.h"
-#include "device/geolocation/fake_location_provider.h"
-#include "device/geolocation/public/cpp/geoposition.h"
-#include "device/geolocation/public/cpp/location_provider.h"
-#include "net/url_request/url_request_test_util.h"
-#include "services/device/public/mojom/geoposition.mojom.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ::testing::NiceMock;
-
-namespace device {
-namespace {
-
-std::unique_ptr<LocationProvider> GetCustomLocationProviderForTest(
- std::unique_ptr<LocationProvider> provider) {
- return provider;
-}
-
-class MockLocationObserver {
- public:
- virtual ~MockLocationObserver() = default;
- void InvalidateLastPosition() {
- last_position_.latitude = 100;
- last_position_.error_code = mojom::Geoposition::ErrorCode::NONE;
- ASSERT_FALSE(ValidateGeoposition(last_position_));
- }
- void OnLocationUpdate(const LocationProvider* provider,
- const mojom::Geoposition& position) {
- last_position_ = position;
- }
-
- mojom::Geoposition last_position() { return last_position_; }
-
- private:
- mojom::Geoposition last_position_;
-};
-
-double g_fake_time_now_secs = 1;
-
-base::Time GetTimeNowForTest() {
- return base::Time::FromDoubleT(g_fake_time_now_secs);
-}
-
-void AdvanceTimeNow(const base::TimeDelta& delta) {
- g_fake_time_now_secs += delta.InSecondsF();
-}
-
-void SetPositionFix(FakeLocationProvider* provider,
- double latitude,
- double longitude,
- double accuracy) {
- mojom::Geoposition position;
- position.error_code = mojom::Geoposition::ErrorCode::NONE;
- position.latitude = latitude;
- position.longitude = longitude;
- position.accuracy = accuracy;
- position.timestamp = GetTimeNowForTest();
- ASSERT_TRUE(ValidateGeoposition(position));
- provider->HandlePositionChanged(position);
-}
-
-// TODO(lethalantidote): Populate a Geoposition in the class from kConstants
-// and then just copy that with "=" versus using a helper function.
-void SetReferencePosition(FakeLocationProvider* provider) {
- SetPositionFix(provider, 51.0, -0.1, 400);
-}
-
-} // namespace
-
-// Simple request context producer that immediately produces a nullptr
-// URLRequestContextGetter, indicating that network geolocation should not be
-// used.
-void NullRequestContextProducer(
- base::OnceCallback<void(scoped_refptr<net::URLRequestContextGetter>)>
- response_callback) {
- std::move(response_callback)
- .Run(scoped_refptr<net::URLRequestContextGetter>(nullptr));
-}
-
-// Simple request context producer that immediately produces a
-// TestURLRequestContextGetter.
-void TestRequestContextProducer(
- const scoped_refptr<base::SingleThreadTaskRunner>& network_task_runner,
- base::OnceCallback<void(scoped_refptr<net::URLRequestContextGetter>)>
- response_callback) {
- std::move(response_callback)
- .Run(base::MakeRefCounted<net::TestURLRequestContextGetter>(
- network_task_runner));
-}
-
-class TestingLocationArbitrator : public LocationArbitrator {
- public:
- TestingLocationArbitrator(
- const LocationProviderUpdateCallback& callback,
- const CustomLocationProviderCallback& provider_getter,
- GeolocationProvider::RequestContextProducer request_context_producer)
- : LocationArbitrator(provider_getter,
- request_context_producer,
- std::string() /* api_key */),
- cell_(nullptr),
- gps_(nullptr) {
- SetUpdateCallback(callback);
- }
-
- base::Time GetTimeNow() const override { return GetTimeNowForTest(); }
-
- std::unique_ptr<LocationProvider> NewNetworkLocationProvider(
- scoped_refptr<net::URLRequestContextGetter> context,
- const std::string& api_key) override {
- cell_ = new FakeLocationProvider;
- return base::WrapUnique(cell_);
- }
-
- std::unique_ptr<LocationProvider> NewSystemLocationProvider() override {
- gps_ = new FakeLocationProvider;
- return base::WrapUnique(gps_);
- }
-
- // Two location providers, with nice short names to make the tests more
- // readable. Note |gps_| will only be set when there is a high accuracy
- // observer registered (and |cell_| when there's at least one observer of any
- // type).
- // TODO(mvanouwerkerk): rename |cell_| to |network_location_provider_| and
- // |gps_| to |gps_location_provider_|
- FakeLocationProvider* cell_;
- FakeLocationProvider* gps_;
-};
-
-class GeolocationLocationArbitratorTest : public testing::Test {
- protected:
- GeolocationLocationArbitratorTest() : observer_(new MockLocationObserver) {}
-
- // Initializes |arbitrator_| with the specified |provider|, which may be null.
- void InitializeArbitrator(
- const CustomLocationProviderCallback& provider_getter,
- GeolocationProvider::RequestContextProducer request_context_producer) {
- const LocationProvider::LocationProviderUpdateCallback callback =
- base::Bind(&MockLocationObserver::OnLocationUpdate,
- base::Unretained(observer_.get()));
- arbitrator_.reset(new TestingLocationArbitrator(callback, provider_getter,
- request_context_producer));
- }
-
- // testing::Test
- void TearDown() override {}
-
- void CheckLastPositionInfo(double latitude,
- double longitude,
- double accuracy) {
- mojom::Geoposition geoposition = observer_->last_position();
- EXPECT_TRUE(ValidateGeoposition(geoposition));
- EXPECT_DOUBLE_EQ(latitude, geoposition.latitude);
- EXPECT_DOUBLE_EQ(longitude, geoposition.longitude);
- EXPECT_DOUBLE_EQ(accuracy, geoposition.accuracy);
- }
-
- base::TimeDelta SwitchOnFreshnessCliff() {
- // Add 1, to ensure it meets any greater-than test.
- return LocationArbitrator::kFixStaleTimeoutTimeDelta +
- base::TimeDelta::FromMilliseconds(1);
- }
-
- FakeLocationProvider* cell() { return arbitrator_->cell_; }
-
- FakeLocationProvider* gps() { return arbitrator_->gps_; }
-
- const std::unique_ptr<MockLocationObserver> observer_;
- std::unique_ptr<TestingLocationArbitrator> arbitrator_;
- base::test::ScopedTaskEnvironment scoped_task_environment_;
-};
-
-// Basic test of the text fixture.
-TEST_F(GeolocationLocationArbitratorTest, CreateDestroy) {
- InitializeArbitrator(base::Bind(&GetCustomLocationProviderForTest, nullptr),
- base::Bind(&NullRequestContextProducer));
- EXPECT_TRUE(arbitrator_);
- arbitrator_.reset();
- SUCCEED();
-}
-
-// Tests OnPermissionGranted().
-TEST_F(GeolocationLocationArbitratorTest, OnPermissionGranted) {
- InitializeArbitrator(base::Bind(&GetCustomLocationProviderForTest, nullptr),
- base::Bind(&NullRequestContextProducer));
- EXPECT_FALSE(arbitrator_->HasPermissionBeenGrantedForTest());
- arbitrator_->OnPermissionGranted();
- EXPECT_TRUE(arbitrator_->HasPermissionBeenGrantedForTest());
- // Can't check the provider has been notified without going through the
- // motions to create the provider (see next test).
- EXPECT_FALSE(cell());
- EXPECT_FALSE(gps());
-}
-
-// Tests basic operation (single position fix) with both network location
-// providers and system location provider.
-TEST_F(GeolocationLocationArbitratorTest, NormalUsage) {
- InitializeArbitrator(
- base::Bind(&GetCustomLocationProviderForTest, nullptr),
- base::Bind(&TestRequestContextProducer,
- scoped_task_environment_.GetMainThreadTaskRunner()));
- ASSERT_TRUE(arbitrator_);
-
- EXPECT_FALSE(cell());
- EXPECT_FALSE(gps());
- arbitrator_->StartProvider(false);
-
- ASSERT_TRUE(cell());
- EXPECT_TRUE(gps());
- EXPECT_EQ(FakeLocationProvider::LOW_ACCURACY, cell()->state_);
- EXPECT_EQ(FakeLocationProvider::LOW_ACCURACY, gps()->state_);
- EXPECT_FALSE(ValidateGeoposition(observer_->last_position()));
- EXPECT_EQ(mojom::Geoposition::ErrorCode::NONE,
- observer_->last_position().error_code);
-
- SetReferencePosition(cell());
-
- EXPECT_TRUE(ValidateGeoposition(observer_->last_position()) ||
- observer_->last_position().error_code !=
- mojom::Geoposition::ErrorCode::NONE);
- EXPECT_EQ(cell()->GetPosition().latitude,
- observer_->last_position().latitude);
-
- EXPECT_FALSE(cell()->is_permission_granted());
- EXPECT_FALSE(arbitrator_->HasPermissionBeenGrantedForTest());
- arbitrator_->OnPermissionGranted();
- EXPECT_TRUE(arbitrator_->HasPermissionBeenGrantedForTest());
- EXPECT_TRUE(cell()->is_permission_granted());
-}
-
-// Tests basic operation (single position fix) with no network location
-// providers and a custom system location provider.
-TEST_F(GeolocationLocationArbitratorTest, CustomSystemProviderOnly) {
- auto provider = std::make_unique<FakeLocationProvider>();
- FakeLocationProvider* fake_location_provider = provider.get();
- InitializeArbitrator(
- base::Bind(&GetCustomLocationProviderForTest, base::Passed(&provider)),
- base::Bind(&NullRequestContextProducer));
- ASSERT_TRUE(arbitrator_);
-
- EXPECT_FALSE(cell());
- EXPECT_FALSE(gps());
- arbitrator_->StartProvider(false);
-
- ASSERT_FALSE(cell());
- EXPECT_FALSE(gps());
- EXPECT_EQ(FakeLocationProvider::LOW_ACCURACY, fake_location_provider->state_);
- EXPECT_FALSE(ValidateGeoposition(observer_->last_position()));
- EXPECT_EQ(mojom::Geoposition::ErrorCode::NONE,
- observer_->last_position().error_code);
-
- SetReferencePosition(fake_location_provider);
-
- EXPECT_TRUE(ValidateGeoposition(observer_->last_position()) ||
- observer_->last_position().error_code !=
- mojom::Geoposition::ErrorCode::NONE);
- EXPECT_EQ(fake_location_provider->GetPosition().latitude,
- observer_->last_position().latitude);
-
- EXPECT_FALSE(fake_location_provider->is_permission_granted());
- EXPECT_FALSE(arbitrator_->HasPermissionBeenGrantedForTest());
- arbitrator_->OnPermissionGranted();
- EXPECT_TRUE(arbitrator_->HasPermissionBeenGrantedForTest());
- EXPECT_TRUE(fake_location_provider->is_permission_granted());
-}
-
-// Tests basic operation (single position fix) with both network location
-// providers and a custom system location provider.
-TEST_F(GeolocationLocationArbitratorTest,
- CustomSystemAndDefaultNetworkProviders) {
- auto provider = std::make_unique<FakeLocationProvider>();
- FakeLocationProvider* fake_location_provider = provider.get();
- InitializeArbitrator(
- base::Bind(&GetCustomLocationProviderForTest, base::Passed(&provider)),
- base::Bind(&TestRequestContextProducer,
- scoped_task_environment_.GetMainThreadTaskRunner()));
- ASSERT_TRUE(arbitrator_);
-
- EXPECT_FALSE(cell());
- EXPECT_FALSE(gps());
- arbitrator_->StartProvider(false);
-
- ASSERT_TRUE(cell());
- EXPECT_FALSE(gps());
- EXPECT_EQ(FakeLocationProvider::LOW_ACCURACY, fake_location_provider->state_);
- EXPECT_EQ(FakeLocationProvider::LOW_ACCURACY, cell()->state_);
- EXPECT_FALSE(ValidateGeoposition(observer_->last_position()));
- EXPECT_EQ(mojom::Geoposition::ErrorCode::NONE,
- observer_->last_position().error_code);
-
- SetReferencePosition(cell());
-
- EXPECT_TRUE(ValidateGeoposition(observer_->last_position()) ||
- observer_->last_position().error_code !=
- mojom::Geoposition::ErrorCode::NONE);
- EXPECT_EQ(cell()->GetPosition().latitude,
- observer_->last_position().latitude);
-
- EXPECT_FALSE(cell()->is_permission_granted());
- EXPECT_FALSE(arbitrator_->HasPermissionBeenGrantedForTest());
- arbitrator_->OnPermissionGranted();
- EXPECT_TRUE(arbitrator_->HasPermissionBeenGrantedForTest());
- EXPECT_TRUE(cell()->is_permission_granted());
-}
-
-// Tests flipping from Low to High accuracy mode as requested by a location
-// observer.
-TEST_F(GeolocationLocationArbitratorTest, SetObserverOptions) {
- InitializeArbitrator(
- base::Bind(&GetCustomLocationProviderForTest, nullptr),
- base::Bind(&TestRequestContextProducer,
- scoped_task_environment_.GetMainThreadTaskRunner()));
- arbitrator_->StartProvider(false);
- ASSERT_TRUE(cell());
- ASSERT_TRUE(gps());
- EXPECT_EQ(FakeLocationProvider::LOW_ACCURACY, cell()->state_);
- EXPECT_EQ(FakeLocationProvider::LOW_ACCURACY, gps()->state_);
- SetReferencePosition(cell());
- EXPECT_EQ(FakeLocationProvider::LOW_ACCURACY, cell()->state_);
- EXPECT_EQ(FakeLocationProvider::LOW_ACCURACY, gps()->state_);
- arbitrator_->StartProvider(true);
- EXPECT_EQ(FakeLocationProvider::HIGH_ACCURACY, cell()->state_);
- EXPECT_EQ(FakeLocationProvider::HIGH_ACCURACY, gps()->state_);
-}
-
-// Tests arbitration algorithm through a sequence of position fixes from
-// multiple sources, with varying accuracy, across a period of time.
-TEST_F(GeolocationLocationArbitratorTest, Arbitration) {
- InitializeArbitrator(
- base::Bind(&GetCustomLocationProviderForTest, nullptr),
- base::Bind(&TestRequestContextProducer,
- scoped_task_environment_.GetMainThreadTaskRunner()));
- arbitrator_->StartProvider(false);
- ASSERT_TRUE(cell());
- ASSERT_TRUE(gps());
-
- SetPositionFix(cell(), 1, 2, 150);
-
- // First position available
- EXPECT_TRUE(ValidateGeoposition(observer_->last_position()));
- CheckLastPositionInfo(1, 2, 150);
-
- SetPositionFix(gps(), 3, 4, 50);
-
- // More accurate fix available
- CheckLastPositionInfo(3, 4, 50);
-
- SetPositionFix(cell(), 5, 6, 150);
-
- // New fix is available but it's less accurate, older fix should be kept.
- CheckLastPositionInfo(3, 4, 50);
-
- // Advance time, and notify once again
- AdvanceTimeNow(SwitchOnFreshnessCliff());
- cell()->HandlePositionChanged(cell()->GetPosition());
-
- // New fix is available, less accurate but fresher
- CheckLastPositionInfo(5, 6, 150);
-
- // Advance time, and set a low accuracy position
- AdvanceTimeNow(SwitchOnFreshnessCliff());
- SetPositionFix(cell(), 5.676731, 139.629385, 1000);
- CheckLastPositionInfo(5.676731, 139.629385, 1000);
-
- // 15 secs later, step outside. Switches to gps signal.
- AdvanceTimeNow(base::TimeDelta::FromSeconds(15));
- SetPositionFix(gps(), 3.5676457, 139.629198, 50);
- CheckLastPositionInfo(3.5676457, 139.629198, 50);
-
- // 5 mins later switch cells while walking. Stay on gps.
- AdvanceTimeNow(base::TimeDelta::FromMinutes(5));
- SetPositionFix(cell(), 3.567832, 139.634648, 300);
- SetPositionFix(gps(), 3.5677675, 139.632314, 50);
- CheckLastPositionInfo(3.5677675, 139.632314, 50);
-
- // Ride train and gps signal degrades slightly. Stay on fresher gps
- AdvanceTimeNow(base::TimeDelta::FromMinutes(5));
- SetPositionFix(gps(), 3.5679026, 139.634777, 300);
- CheckLastPositionInfo(3.5679026, 139.634777, 300);
-
- // 14 minutes later
- AdvanceTimeNow(base::TimeDelta::FromMinutes(14));
-
- // GPS reading misses a beat, but don't switch to cell yet to avoid
- // oscillating.
- SetPositionFix(gps(), 3.5659005, 139.682579, 300);
-
- AdvanceTimeNow(base::TimeDelta::FromSeconds(7));
- SetPositionFix(cell(), 3.5689579, 139.691420, 1000);
- CheckLastPositionInfo(3.5659005, 139.682579, 300);
-
- // 1 minute later
- AdvanceTimeNow(base::TimeDelta::FromMinutes(1));
-
- // Enter tunnel. Stay on fresher gps for a moment.
- SetPositionFix(cell(), 3.5657078, 139.68922, 300);
- SetPositionFix(gps(), 3.5657104, 139.690341, 300);
- CheckLastPositionInfo(3.5657104, 139.690341, 300);
-
- // 2 minutes later
- AdvanceTimeNow(base::TimeDelta::FromMinutes(2));
- // Arrive in station. Cell moves but GPS is stale. Switch to fresher cell.
- SetPositionFix(cell(), 3.5658700, 139.069979, 1000);
- CheckLastPositionInfo(3.5658700, 139.069979, 1000);
-}
-
-// Verifies that the arbitrator doesn't retain pointers to old providers after
-// it has stopped and then restarted (crbug.com/240956).
-TEST_F(GeolocationLocationArbitratorTest, TwoOneShotsIsNewPositionBetter) {
- InitializeArbitrator(
- base::Bind(&GetCustomLocationProviderForTest, nullptr),
- base::Bind(&TestRequestContextProducer,
- scoped_task_environment_.GetMainThreadTaskRunner()));
- arbitrator_->StartProvider(false);
- ASSERT_TRUE(cell());
- ASSERT_TRUE(gps());
-
- // Set the initial position.
- SetPositionFix(cell(), 3, 139, 100);
- CheckLastPositionInfo(3, 139, 100);
-
- // Restart providers to simulate a one-shot request.
- arbitrator_->StopProvider();
-
- // To test 240956, perform a throwaway alloc.
- // This convinces the allocator to put the providers in a new memory location.
- std::unique_ptr<FakeLocationProvider> dummy_provider(
- new FakeLocationProvider);
-
- arbitrator_->StartProvider(false);
-
- // Advance the time a short while to simulate successive calls.
- AdvanceTimeNow(base::TimeDelta::FromMilliseconds(5));
-
- // Update with a less accurate position to verify 240956.
- SetPositionFix(cell(), 3, 139, 150);
- CheckLastPositionInfo(3, 139, 150);
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/location_provider_android.cc b/chromium/device/geolocation/location_provider_android.cc
deleted file mode 100644
index 786dc035f62..00000000000
--- a/chromium/device/geolocation/location_provider_android.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) 2012 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/geolocation/location_provider_android.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/memory/ptr_util.h"
-#include "device/geolocation/location_api_adapter_android.h"
-
-namespace device {
-
-// LocationProviderAndroid
-LocationProviderAndroid::LocationProviderAndroid() : weak_ptr_factory_(this) {}
-
-LocationProviderAndroid::~LocationProviderAndroid() {
- DCHECK(thread_checker_.CalledOnValidThread());
- StopProvider();
-}
-
-void LocationProviderAndroid::NotifyNewGeoposition(
- const mojom::Geoposition& position) {
- DCHECK(thread_checker_.CalledOnValidThread());
- last_position_ = position;
- if (!callback_.is_null())
- callback_.Run(this, position);
-}
-
-void LocationProviderAndroid::SetUpdateCallback(
- const LocationProviderUpdateCallback& callback) {
- DCHECK(thread_checker_.CalledOnValidThread());
- callback_ = callback;
-}
-
-void LocationProviderAndroid::StartProvider(bool high_accuracy) {
- DCHECK(thread_checker_.CalledOnValidThread());
- LocationApiAdapterAndroid::GetInstance()->Start(
- base::Bind(&LocationProviderAndroid::NotifyNewGeoposition,
- weak_ptr_factory_.GetWeakPtr()),
- high_accuracy);
-}
-
-void LocationProviderAndroid::StopProvider() {
- DCHECK(thread_checker_.CalledOnValidThread());
- LocationApiAdapterAndroid::GetInstance()->Stop();
-}
-
-const mojom::Geoposition& LocationProviderAndroid::GetPosition() {
- DCHECK(thread_checker_.CalledOnValidThread());
- return last_position_;
-}
-
-void LocationProviderAndroid::OnPermissionGranted() {
- DCHECK(thread_checker_.CalledOnValidThread());
- // Nothing to do here.
-}
-
-// static
-std::unique_ptr<LocationProvider> NewSystemLocationProvider() {
- return base::WrapUnique(new LocationProviderAndroid);
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/location_provider_android.h b/chromium/device/geolocation/location_provider_android.h
deleted file mode 100644
index 64730e0f13f..00000000000
--- a/chromium/device/geolocation/location_provider_android.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2012 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_GEOLOCATION_LOCATION_PROVIDER_ANDROID_H_
-#define DEVICE_GEOLOCATION_LOCATION_PROVIDER_ANDROID_H_
-
-#include "base/compiler_specific.h"
-#include "base/memory/weak_ptr.h"
-#include "base/threading/thread_checker.h"
-#include "device/geolocation/public/cpp/location_provider.h"
-#include "services/device/public/mojom/geoposition.mojom.h"
-
-namespace device {
-
-// Location provider for Android using the platform provider over JNI.
-class LocationProviderAndroid : public LocationProvider {
- public:
- LocationProviderAndroid();
- ~LocationProviderAndroid() override;
-
- // Called by the LocationApiAdapterAndroid.
- void NotifyNewGeoposition(const mojom::Geoposition& position);
-
- // LocationProvider implementation.
- void SetUpdateCallback(
- const LocationProviderUpdateCallback& callback) override;
- void StartProvider(bool high_accuracy) override;
- void StopProvider() override;
- const mojom::Geoposition& GetPosition() override;
- void OnPermissionGranted() override;
-
- private:
- base::ThreadChecker thread_checker_;
-
- mojom::Geoposition last_position_;
- LocationProviderUpdateCallback callback_;
-
- base::WeakPtrFactory<LocationProviderAndroid> weak_ptr_factory_;
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_LOCATION_PROVIDER_ANDROID_H_
diff --git a/chromium/device/geolocation/network_location_provider.cc b/chromium/device/geolocation/network_location_provider.cc
deleted file mode 100644
index 00032cab0ee..00000000000
--- a/chromium/device/geolocation/network_location_provider.cc
+++ /dev/null
@@ -1,294 +0,0 @@
-// Copyright (c) 2012 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/geolocation/network_location_provider.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/single_thread_task_runner.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "device/geolocation/public/cpp/geoposition.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_request_context_getter.h"
-
-namespace device {
-namespace {
-// The maximum period of time we'll wait for a complete set of wifi data
-// before sending the request.
-const int kDataCompleteWaitSeconds = 2;
-
-// The maximum age of a cached network location estimate before it can no longer
-// be returned as a fresh estimate. This should be at least as long as the
-// longest polling interval used by the WifiDataProvider.
-const int kLastPositionMaxAgeSeconds = 10 * 60; // 10 minutes
-} // namespace
-
-// static
-const size_t NetworkLocationProvider::PositionCache::kMaximumSize = 10;
-
-NetworkLocationProvider::PositionCache::PositionCache() = default;
-
-NetworkLocationProvider::PositionCache::~PositionCache() = default;
-
-bool NetworkLocationProvider::PositionCache::CachePosition(
- const WifiData& wifi_data,
- const mojom::Geoposition& position) {
- // Check that we can generate a valid key for the wifi data.
- base::string16 key;
- if (!MakeKey(wifi_data, &key)) {
- return false;
- }
- // If the cache is full, remove the oldest entry.
- if (cache_.size() == kMaximumSize) {
- DCHECK(cache_age_list_.size() == kMaximumSize);
- CacheAgeList::iterator oldest_entry = cache_age_list_.begin();
- DCHECK(oldest_entry != cache_age_list_.end());
- cache_.erase(*oldest_entry);
- cache_age_list_.erase(oldest_entry);
- }
- DCHECK_LT(cache_.size(), kMaximumSize);
- // Insert the position into the cache.
- std::pair<CacheMap::iterator, bool> result =
- cache_.insert(std::make_pair(key, position));
- if (!result.second) {
- NOTREACHED(); // We never try to add the same key twice.
- CHECK_EQ(cache_.size(), cache_age_list_.size());
- return false;
- }
- cache_age_list_.push_back(result.first);
- DCHECK_EQ(cache_.size(), cache_age_list_.size());
- return true;
-}
-
-// Searches for a cached position response for the current WiFi data. Returns
-// the cached position if available, nullptr otherwise.
-const mojom::Geoposition* NetworkLocationProvider::PositionCache::FindPosition(
- const WifiData& wifi_data) {
- base::string16 key;
- if (!MakeKey(wifi_data, &key)) {
- return nullptr;
- }
- CacheMap::const_iterator iter = cache_.find(key);
- return iter == cache_.end() ? nullptr : &iter->second;
-}
-
-// Makes the key for the map of cached positions, using the available data.
-// Returns true if a good key was generated, false otherwise.
-//
-// static
-bool NetworkLocationProvider::PositionCache::MakeKey(const WifiData& wifi_data,
- base::string16* key) {
- // Currently we use only WiFi data and base the key only on the MAC addresses.
- DCHECK(key);
- key->clear();
- const size_t kCharsPerMacAddress = 6 * 3 + 1; // e.g. "11:22:33:44:55:66|"
- key->reserve(wifi_data.access_point_data.size() * kCharsPerMacAddress);
- const base::string16 separator(base::ASCIIToUTF16("|"));
- for (const auto& access_point_data : wifi_data.access_point_data) {
- *key += separator;
- *key += access_point_data.mac_address;
- *key += separator;
- }
- // If the key is the empty string, return false, as we don't want to cache a
- // position for such data.
- return !key->empty();
-}
-
-// NetworkLocationProvider
-NetworkLocationProvider::NetworkLocationProvider(
- scoped_refptr<net::URLRequestContextGetter> url_context_getter,
- const std::string& api_key,
- LastPositionCache* last_position_cache)
- : wifi_data_provider_manager_(nullptr),
- wifi_data_update_callback_(
- base::Bind(&NetworkLocationProvider::OnWifiDataUpdate,
- base::Unretained(this))),
- is_wifi_data_complete_(false),
- last_position_delegate_(last_position_cache),
- is_permission_granted_(false),
- is_new_data_available_(false),
- request_(new NetworkLocationRequest(
- std::move(url_context_getter),
- api_key,
- base::Bind(&NetworkLocationProvider::OnLocationResponse,
- base::Unretained(this)))),
- position_cache_(new PositionCache),
- weak_factory_(this) {
- DCHECK(last_position_delegate_);
-}
-
-NetworkLocationProvider::~NetworkLocationProvider() {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (IsStarted())
- StopProvider();
-}
-
-void NetworkLocationProvider::SetUpdateCallback(
- const LocationProvider::LocationProviderUpdateCallback& callback) {
- DCHECK(thread_checker_.CalledOnValidThread());
- location_provider_update_callback_ = callback;
-}
-
-void NetworkLocationProvider::OnPermissionGranted() {
- const bool was_permission_granted = is_permission_granted_;
- is_permission_granted_ = true;
- if (!was_permission_granted && IsStarted())
- RequestPosition();
-}
-
-void NetworkLocationProvider::OnWifiDataUpdate() {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(IsStarted());
- is_wifi_data_complete_ = wifi_data_provider_manager_->GetData(&wifi_data_);
- if (!is_wifi_data_complete_)
- return;
-
- wifi_timestamp_ = base::Time::Now();
- is_new_data_available_ = true;
- RequestPosition();
-}
-
-void NetworkLocationProvider::OnLocationResponse(
- const mojom::Geoposition& position,
- bool server_error,
- const WifiData& wifi_data) {
- DCHECK(thread_checker_.CalledOnValidThread());
- // Record the position and update our cache.
- last_position_delegate_->SetLastNetworkPosition(position);
- if (ValidateGeoposition(position))
- position_cache_->CachePosition(wifi_data, position);
-
- // Let listeners know that we now have a position available.
- if (!location_provider_update_callback_.is_null()) {
- location_provider_update_callback_.Run(
- this, last_position_delegate_->GetLastNetworkPosition());
- }
-}
-
-void NetworkLocationProvider::StartProvider(bool high_accuracy) {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- if (IsStarted())
- return;
-
- // Registers a callback with the data provider. The first call to Register()
- // will create a singleton data provider that will be deleted on Unregister().
- wifi_data_provider_manager_ =
- WifiDataProviderManager::Register(&wifi_data_update_callback_);
-
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&NetworkLocationProvider::RequestPosition,
- weak_factory_.GetWeakPtr()),
- base::TimeDelta::FromSeconds(kDataCompleteWaitSeconds));
-
- OnWifiDataUpdate();
-}
-
-void NetworkLocationProvider::StopProvider() {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(IsStarted());
- wifi_data_provider_manager_->Unregister(&wifi_data_update_callback_);
- wifi_data_provider_manager_ = nullptr;
- weak_factory_.InvalidateWeakPtrs();
-}
-
-const mojom::Geoposition& NetworkLocationProvider::GetPosition() {
- return last_position_delegate_->GetLastNetworkPosition();
-}
-
-void NetworkLocationProvider::RequestPosition() {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- // The wifi polling policy may require us to wait for several minutes before
- // fresh wifi data is available. To ensure we can return a position estimate
- // quickly when the network location provider is the primary provider, allow
- // a cached value to be returned under certain conditions.
- //
- // If we have a sufficiently recent network location estimate and we do not
- // expect to receive a new one soon (i.e., no new wifi data is available and
- // there is no pending network request), report the last network position
- // estimate as if it were a fresh estimate.
- const mojom::Geoposition& last_position =
- last_position_delegate_->GetLastNetworkPosition();
- if (!is_new_data_available_ && !request_->is_request_pending() &&
- ValidateGeoposition(last_position)) {
- base::Time now = base::Time::Now();
- base::TimeDelta last_position_age = now - last_position.timestamp;
- if (last_position_age.InSeconds() < kLastPositionMaxAgeSeconds &&
- !location_provider_update_callback_.is_null()) {
- // Update the timestamp to the current time.
- mojom::Geoposition position = last_position;
- position.timestamp = now;
- location_provider_update_callback_.Run(this, position);
- }
- }
-
- if (!is_new_data_available_ || !is_wifi_data_complete_)
- return;
- DCHECK(!wifi_timestamp_.is_null())
- << "|wifi_timestamp_| must be set before looking up position";
-
- const mojom::Geoposition* cached_position =
- position_cache_->FindPosition(wifi_data_);
- if (cached_position) {
- mojom::Geoposition position(*cached_position);
- DCHECK(ValidateGeoposition(position));
- // The timestamp of a position fix is determined by the timestamp
- // of the source data update. (The value of position.timestamp from
- // the cache could be from weeks ago!)
- position.timestamp = wifi_timestamp_;
- is_new_data_available_ = false;
-
- // Record the position.
- last_position_delegate_->SetLastNetworkPosition(position);
-
- // Let listeners know that we now have a position available.
- if (!location_provider_update_callback_.is_null())
- location_provider_update_callback_.Run(this, position);
- return;
- }
- // Don't send network requests until authorized. http://crbug.com/39171
- if (!is_permission_granted_)
- return;
-
- is_new_data_available_ = false;
-
- // TODO(joth): Rather than cancel pending requests, we should create a new
- // NetworkLocationRequest for each and hold a set of pending requests.
- DLOG_IF(WARNING, request_->is_request_pending())
- << "NetworkLocationProvider - pre-empting pending network request "
- "with new data. Wifi APs: "
- << wifi_data_.access_point_data.size();
-
- net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
- net::DefinePartialNetworkTrafficAnnotation("network_location_provider",
- "network_location_request",
- R"(
- semantics {
- sender: "Network Location Provider"
- }
- policy {
- setting:
- "Users can control this feature via the Location setting under "
- "'Privacy', 'Content Settings', 'Location'."
- chrome_policy {
- DefaultGeolocationSetting {
- DefaultGeolocationSetting: 2
- }
- }
- })");
- request_->MakeRequest(wifi_data_, wifi_timestamp_,
- partial_traffic_annotation);
-}
-
-bool NetworkLocationProvider::IsStarted() const {
- return wifi_data_provider_manager_ != nullptr;
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/network_location_provider.h b/chromium/device/geolocation/network_location_provider.h
deleted file mode 100644
index 45058788c6e..00000000000
--- a/chromium/device/geolocation/network_location_provider.h
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright (c) 2012 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_GEOLOCATION_NETWORK_LOCATION_PROVIDER_H_
-#define DEVICE_GEOLOCATION_NETWORK_LOCATION_PROVIDER_H_
-
-#include <stddef.h>
-
-#include <list>
-#include <map>
-#include <memory>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/strings/string16.h"
-#include "base/threading/thread.h"
-#include "base/threading/thread_checker.h"
-#include "device/geolocation/geolocation_export.h"
-#include "device/geolocation/network_location_request.h"
-#include "device/geolocation/public/cpp/location_provider.h"
-#include "device/geolocation/wifi_data_provider_manager.h"
-#include "services/device/public/mojom/geoposition.mojom.h"
-
-namespace device {
-
-class NetworkLocationProvider : public LocationProvider {
- public:
- // To ensure the last-used position estimate can be preserved when the network
- // location provider is torn down, a delegate manages the state of the cached
- // position estimate outside of this provider.
- class LastPositionCache {
- public:
- virtual ~LastPositionCache() = default;
- virtual void SetLastNetworkPosition(
- const mojom::Geoposition& new_position) = 0;
- virtual const mojom::Geoposition& GetLastNetworkPosition() = 0;
- };
-
- // Cache of recently resolved locations, keyed by the set of unique WiFi APs
- // used in the network query. Public for tests.
- class DEVICE_GEOLOCATION_EXPORT PositionCache {
- public:
- // The maximum size of the cache of positions.
- static const size_t kMaximumSize;
-
- PositionCache();
- ~PositionCache();
-
- // Caches the current position response for the current set of cell ID and
- // WiFi data. In the case of the cache exceeding kMaximumSize this will
- // evict old entries in FIFO orderer of being added.
- // Returns true on success, false otherwise.
- bool CachePosition(const WifiData& wifi_data,
- const mojom::Geoposition& position);
-
- // Searches for a cached position response for the current set of data.
- // Returns NULL if the position is not in the cache, or the cached
- // position if available. Ownership remains with the cache.
- const mojom::Geoposition* FindPosition(const WifiData& wifi_data);
-
- private:
- // Makes the key for the map of cached positions, using a set of
- // data. Returns true if a good key was generated, false otherwise.
- static bool MakeKey(const WifiData& wifi_data, base::string16* key);
-
- // The cache of positions. This is stored as a map keyed on a string that
- // represents a set of data, and a list to provide
- // least-recently-added eviction.
- typedef std::map<base::string16, mojom::Geoposition> CacheMap;
- CacheMap cache_;
- typedef std::list<CacheMap::iterator> CacheAgeList;
- CacheAgeList cache_age_list_; // Oldest first.
- };
-
- DEVICE_GEOLOCATION_EXPORT NetworkLocationProvider(
- scoped_refptr<net::URLRequestContextGetter> context,
- const std::string& api_key,
- LastPositionCache* last_position_cache);
- ~NetworkLocationProvider() override;
-
- // LocationProvider implementation
- void SetUpdateCallback(const LocationProviderUpdateCallback& cb) override;
- void StartProvider(bool high_accuracy) override;
- void StopProvider() override;
- const mojom::Geoposition& GetPosition() override;
- void OnPermissionGranted() override;
-
- private:
- // Tries to update |position_| request from cache or network.
- void RequestPosition();
-
- // Gets called when new wifi data is available, either via explicit request to
- // or callback from |wifi_data_provider_manager_|.
- void OnWifiDataUpdate();
-
- bool IsStarted() const;
-
- void OnLocationResponse(const mojom::Geoposition& position,
- bool server_error,
- const WifiData& wifi_data);
-
- // The wifi data provider, acquired via global factories. Valid between
- // StartProvider() and StopProvider(), and checked via IsStarted().
- WifiDataProviderManager* wifi_data_provider_manager_;
-
- WifiDataProviderManager::WifiDataUpdateCallback wifi_data_update_callback_;
-
- // The wifi data and a flag to indicate if the data set is complete.
- WifiData wifi_data_;
- bool is_wifi_data_complete_;
-
- // The timestamp for the latest wifi data update.
- base::Time wifi_timestamp_;
-
- // A delegate to manage the current best network position estimate. Must not
- // be nullptr.
- LastPositionCache* const last_position_delegate_;
-
- LocationProvider::LocationProviderUpdateCallback
- location_provider_update_callback_;
-
- // Whether permission has been granted for the provider to operate.
- bool is_permission_granted_;
-
- bool is_new_data_available_;
-
- // The network location request object.
- const std::unique_ptr<NetworkLocationRequest> request_;
-
- // The cache of positions.
- const std::unique_ptr<PositionCache> position_cache_;
-
- base::ThreadChecker thread_checker_;
-
- base::WeakPtrFactory<NetworkLocationProvider> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(NetworkLocationProvider);
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_NETWORK_LOCATION_PROVIDER_H_
diff --git a/chromium/device/geolocation/network_location_provider_unittest.cc b/chromium/device/geolocation/network_location_provider_unittest.cc
deleted file mode 100644
index 978b2ca8bbc..00000000000
--- a/chromium/device/geolocation/network_location_provider_unittest.cc
+++ /dev/null
@@ -1,767 +0,0 @@
-// Copyright (c) 2012 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/geolocation/network_location_provider.h"
-
-#include <stddef.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/json/json_reader.h"
-#include "base/json/json_writer.h"
-#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/values.h"
-#include "device/geolocation/location_arbitrator.h"
-#include "device/geolocation/public/cpp/geoposition.h"
-#include "device/geolocation/wifi_data_provider.h"
-#include "net/base/net_errors.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_status.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace device {
-
-// Records the most recent position update and counts the number of times
-// OnLocationUpdate is called.
-struct LocationUpdateListener {
- LocationUpdateListener()
- : callback(base::BindRepeating(&LocationUpdateListener::OnLocationUpdate,
- base::Unretained(this))) {}
-
- void OnLocationUpdate(const LocationProvider* provider,
- const mojom::Geoposition& position) {
- last_position = position;
- update_count++;
- }
-
- const LocationProvider::LocationProviderUpdateCallback callback;
- mojom::Geoposition last_position;
- int update_count = 0;
-};
-
-// A mock implementation of WifiDataProvider for testing. Adapted from
-// http://gears.googlecode.com/svn/trunk/gears/geolocation/geolocation_test.cc
-class MockWifiDataProvider : public WifiDataProvider {
- public:
- // Factory method for use with WifiDataProvider::SetFactoryForTesting.
- static WifiDataProvider* GetInstance() {
- CHECK(instance_);
- return instance_;
- }
-
- static MockWifiDataProvider* CreateInstance() {
- CHECK(!instance_);
- instance_ = new MockWifiDataProvider;
- return instance_;
- }
-
- MockWifiDataProvider() : start_calls_(0), stop_calls_(0), got_data_(true) {}
-
- // WifiDataProvider implementation.
- void StartDataProvider() override { ++start_calls_; }
-
- void StopDataProvider() override { ++stop_calls_; }
-
- bool GetData(WifiData* data_out) override {
- CHECK(data_out);
- *data_out = data_;
- return got_data_;
- }
-
- void SetData(const WifiData& new_data) {
- got_data_ = true;
- const bool differs = data_.DiffersSignificantly(new_data);
- data_ = new_data;
- if (differs)
- this->RunCallbacks();
- }
-
- void set_got_data(bool got_data) { got_data_ = got_data; }
- int start_calls_;
- int stop_calls_;
-
- private:
- ~MockWifiDataProvider() override {
- CHECK(this == instance_);
- instance_ = nullptr;
- }
-
- static MockWifiDataProvider* instance_;
-
- WifiData data_;
- bool got_data_;
-
- DISALLOW_COPY_AND_ASSIGN(MockWifiDataProvider);
-};
-
-// An implementation of LastPositionCache.
-class TestLastPositionCache
- : public NetworkLocationProvider::LastPositionCache {
- public:
- void SetLastNetworkPosition(const mojom::Geoposition& position) override {
- last_network_position_ = position;
- }
- const mojom::Geoposition& GetLastNetworkPosition() override {
- return last_network_position_;
- }
-
- private:
- mojom::Geoposition last_network_position_;
-};
-
-MockWifiDataProvider* MockWifiDataProvider::instance_ = nullptr;
-
-// Main test fixture
-class GeolocationNetworkProviderTest : public testing::Test {
- public:
- void TearDown() override {
- WifiDataProviderManager::ResetFactoryForTesting();
- }
-
- LocationProvider* CreateProvider(bool set_permission_granted,
- const std::string& api_key = std::string()) {
- // No URLContextGetter needed: The request within the provider is tested
- // directly using TestURLFetcherFactory.
- LocationProvider* provider = new NetworkLocationProvider(
- nullptr, api_key, last_position_cache_.get());
- if (set_permission_granted)
- provider->OnPermissionGranted();
- return provider;
- }
-
- protected:
- GeolocationNetworkProviderTest()
- : wifi_data_provider_(MockWifiDataProvider::CreateInstance()),
- last_position_cache_(std::make_unique<TestLastPositionCache>()) {
- // TODO(joth): Really these should be in SetUp, not here, but they take no
- // effect on Mac OS Release builds if done there. I kid not. Figure out why.
- WifiDataProviderManager::SetFactoryForTesting(
- MockWifiDataProvider::GetInstance);
- }
-
- // Returns the current url fetcher (if any) and advances the id ready for the
- // next test step.
- net::TestURLFetcher* get_url_fetcher_and_advance_id() {
- net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(
- NetworkLocationRequest::url_fetcher_id_for_tests);
- if (fetcher)
- ++NetworkLocationRequest::url_fetcher_id_for_tests;
- return fetcher;
- }
-
- static int IndexToChannel(int index) { return index + 4; }
-
- // Creates wifi data containing the specified number of access points, with
- // some differentiating charactistics in each.
- static WifiData CreateReferenceWifiScanData(int ap_count) {
- WifiData data;
- for (int i = 0; i < ap_count; ++i) {
- AccessPointData ap;
- ap.mac_address =
- base::ASCIIToUTF16(base::StringPrintf("%02d-34-56-78-54-32", i));
- ap.radio_signal_strength = ap_count - i;
- ap.channel = IndexToChannel(i);
- ap.signal_to_noise = i + 42;
- ap.ssid = base::ASCIIToUTF16("Some nice+network|name\\");
- data.access_point_data.insert(ap);
- }
- return data;
- }
-
- static void CreateReferenceWifiScanDataJson(
- int ap_count,
- int start_index,
- base::ListValue* wifi_access_point_list) {
- std::vector<std::string> wifi_data;
- for (int i = 0; i < ap_count; ++i) {
- std::unique_ptr<base::DictionaryValue> ap(new base::DictionaryValue());
- ap->SetString("macAddress", base::StringPrintf("%02d-34-56-78-54-32", i));
- ap->SetInteger("signalStrength", start_index + ap_count - i);
- ap->SetInteger("age", 0);
- ap->SetInteger("channel", IndexToChannel(i));
- ap->SetInteger("signalToNoiseRatio", i + 42);
- wifi_access_point_list->Append(std::move(ap));
- }
- }
-
- static mojom::Geoposition CreateReferencePosition(int id) {
- mojom::Geoposition pos;
- pos.latitude = id;
- pos.longitude = -(id + 1);
- pos.altitude = 2 * id;
- pos.accuracy = 3 * id;
- pos.timestamp = base::Time::Now();
- return pos;
- }
-
- static std::string PrettyJson(const base::Value& value) {
- std::string pretty;
- base::JSONWriter::WriteWithOptions(
- value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &pretty);
- return pretty;
- }
-
- static testing::AssertionResult JsonGetList(
- const std::string& field,
- const base::DictionaryValue& dict,
- const base::ListValue** output_list) {
- if (!dict.GetList(field, output_list))
- return testing::AssertionFailure() << "Dictionary " << PrettyJson(dict)
- << " is missing list field " << field;
- return testing::AssertionSuccess();
- }
-
- static testing::AssertionResult JsonFieldEquals(
- const std::string& field,
- const base::DictionaryValue& expected,
- const base::DictionaryValue& actual) {
- const base::Value* expected_value;
- const base::Value* actual_value;
- if (!expected.Get(field, &expected_value))
- return testing::AssertionFailure() << "Expected dictionary "
- << PrettyJson(expected)
- << " is missing field " << field;
- if (!expected.Get(field, &actual_value))
- return testing::AssertionFailure() << "Actual dictionary "
- << PrettyJson(actual)
- << " is missing field " << field;
- if (!expected_value->Equals(actual_value))
- return testing::AssertionFailure()
- << "Field " << field
- << " mismatch: " << PrettyJson(*expected_value)
- << " != " << PrettyJson(*actual_value);
- return testing::AssertionSuccess();
- }
-
- // Checks that |request| contains valid JSON upload data. The Wifi access
- // points specified in the JSON are validated against the first
- // |expected_wifi_aps| access points, starting from position
- // |wifi_start_index|, that are generated by CreateReferenceWifiScanDataJson.
- void CheckRequestIsValid(const net::TestURLFetcher& request,
- int expected_wifi_aps,
- int wifi_start_index) {
- const std::string& upload_data = request.upload_data();
- ASSERT_FALSE(upload_data.empty());
- std::string json_parse_error_msg;
- std::unique_ptr<base::Value> parsed_json =
- base::JSONReader::ReadAndReturnError(upload_data, base::JSON_PARSE_RFC,
- nullptr, &json_parse_error_msg);
- EXPECT_TRUE(json_parse_error_msg.empty());
- ASSERT_TRUE(parsed_json);
-
- const base::DictionaryValue* request_json;
- ASSERT_TRUE(parsed_json->GetAsDictionary(&request_json));
-
- if (expected_wifi_aps) {
- base::ListValue expected_wifi_aps_json;
- CreateReferenceWifiScanDataJson(expected_wifi_aps, wifi_start_index,
- &expected_wifi_aps_json);
- EXPECT_EQ(size_t(expected_wifi_aps), expected_wifi_aps_json.GetSize());
-
- const base::ListValue* wifi_aps_json;
- ASSERT_TRUE(
- JsonGetList("wifiAccessPoints", *request_json, &wifi_aps_json));
- for (size_t i = 0; i < expected_wifi_aps_json.GetSize(); ++i) {
- const base::DictionaryValue* expected_json;
- ASSERT_TRUE(expected_wifi_aps_json.GetDictionary(i, &expected_json));
- const base::DictionaryValue* actual_json;
- ASSERT_TRUE(wifi_aps_json->GetDictionary(i, &actual_json));
- ASSERT_TRUE(
- JsonFieldEquals("macAddress", *expected_json, *actual_json));
- ASSERT_TRUE(
- JsonFieldEquals("signalStrength", *expected_json, *actual_json));
- ASSERT_TRUE(JsonFieldEquals("channel", *expected_json, *actual_json));
- ASSERT_TRUE(JsonFieldEquals("signalToNoiseRatio", *expected_json,
- *actual_json));
- }
- } else {
- ASSERT_FALSE(request_json->HasKey("wifiAccessPoints"));
- }
- }
-
- const base::MessageLoop main_message_loop_;
- const net::TestURLFetcherFactory url_fetcher_factory_;
- const scoped_refptr<MockWifiDataProvider> wifi_data_provider_;
- std::unique_ptr<NetworkLocationProvider::LastPositionCache>
- last_position_cache_;
-};
-
-// Tests that fixture members were SetUp correctly.
-TEST_F(GeolocationNetworkProviderTest, CreateDestroy) {
- EXPECT_EQ(&main_message_loop_, base::MessageLoop::current());
- std::unique_ptr<LocationProvider> provider(CreateProvider(true));
- EXPECT_TRUE(provider);
- provider.reset();
- SUCCEED();
-}
-
-// Tests that, with an empty api_key, no query string parameter is included in
-// the request.
-TEST_F(GeolocationNetworkProviderTest, EmptyApiKey) {
- const std::string api_key = "";
- std::unique_ptr<LocationProvider> provider(CreateProvider(true, api_key));
- provider->StartProvider(false);
- net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
- ASSERT_TRUE(fetcher);
- EXPECT_FALSE(fetcher->GetOriginalURL().has_query());
-}
-
-// Tests that, with non-empty api_key, a "key" query string parameter is
-// included in the request.
-TEST_F(GeolocationNetworkProviderTest, NonEmptyApiKey) {
- const std::string api_key = "something";
- std::unique_ptr<LocationProvider> provider(CreateProvider(true, api_key));
- provider->StartProvider(false);
- net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
- ASSERT_TRUE(fetcher);
- EXPECT_TRUE(fetcher->GetOriginalURL().has_query());
- EXPECT_TRUE(fetcher->GetOriginalURL().query_piece().starts_with("key="));
-}
-
-// Tests that, after StartProvider(), a TestURLFetcher can be extracted,
-// representing a valid request.
-TEST_F(GeolocationNetworkProviderTest, StartProvider) {
- std::unique_ptr<LocationProvider> provider(CreateProvider(true));
- provider->StartProvider(false);
- net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
- ASSERT_TRUE(fetcher);
- CheckRequestIsValid(*fetcher, 0, 0);
-}
-
-// Tests that, with a very large number of access points, the set of access
-// points represented in the request is truncated to fit within 2048 characters.
-TEST_F(GeolocationNetworkProviderTest, StartProviderLongRequest) {
- std::unique_ptr<LocationProvider> provider(CreateProvider(true));
- provider->StartProvider(false);
- // Create Wifi scan data with too many access points.
- const int kFirstScanAps = 20;
- wifi_data_provider_->SetData(CreateReferenceWifiScanData(kFirstScanAps));
- base::RunLoop().RunUntilIdle();
- net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
- ASSERT_TRUE(fetcher);
- // The request url should have been shortened to less than 2048 characters
- // in length by not including access points with the lowest signal strength
- // in the request.
- EXPECT_LT(fetcher->GetOriginalURL().spec().size(), size_t(2048));
- // Expect only 16 out of 20 original access points.
- CheckRequestIsValid(*fetcher, 16, 4);
-}
-
-// Tests that the provider issues the right requests, and provides the right
-// GetPosition() results based on the responses, for the following complex
-// sequence of Wifi data situations:
-// 1. Initial "no fix" response -> provide 'invalid' position.
-// 2. Wifi data arrives -> make new request.
-// 3. Response has good fix -> provide corresponding position.
-// 4. Wifi data changes slightly -> no new request.
-// 5. Wifi data changes a lot -> new request.
-// 6. Response is error -> provide 'invalid' position.
-// 7. Wifi data changes back to (2.) -> no new request, provide cached position.
-TEST_F(GeolocationNetworkProviderTest, MultipleWifiScansComplete) {
- std::unique_ptr<LocationProvider> provider(CreateProvider(true));
- provider->StartProvider(false);
-
- net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
- ASSERT_TRUE(fetcher);
-
- // 1. Complete the network request with bad position fix.
- const char* kNoFixNetworkResponse =
- "{"
- " \"status\": \"ZERO_RESULTS\""
- "}";
- fetcher->set_status(net::URLRequestStatus());
- fetcher->set_response_code(200); // OK
- fetcher->SetResponseString(kNoFixNetworkResponse);
- fetcher->delegate()->OnURLFetchComplete(fetcher);
-
- mojom::Geoposition position = provider->GetPosition();
- EXPECT_FALSE(ValidateGeoposition(position));
-
- // 2. Now wifi data arrives -- SetData will notify listeners.
- const int kFirstScanAps = 6;
- wifi_data_provider_->SetData(CreateReferenceWifiScanData(kFirstScanAps));
- base::RunLoop().RunUntilIdle();
- fetcher = get_url_fetcher_and_advance_id();
- ASSERT_TRUE(fetcher);
- // The request should have the wifi data.
- CheckRequestIsValid(*fetcher, kFirstScanAps, 0);
-
- // 3. Send a reply with good position fix.
- const char* kReferenceNetworkResponse =
- "{"
- " \"accuracy\": 1200.4,"
- " \"location\": {"
- " \"lat\": 51.0,"
- " \"lng\": -0.1"
- " }"
- "}";
- fetcher->set_status(net::URLRequestStatus());
- fetcher->set_response_code(200); // OK
- fetcher->SetResponseString(kReferenceNetworkResponse);
- fetcher->delegate()->OnURLFetchComplete(fetcher);
-
- position = provider->GetPosition();
- EXPECT_EQ(51.0, position.latitude);
- EXPECT_EQ(-0.1, position.longitude);
- EXPECT_EQ(1200.4, position.accuracy);
- EXPECT_FALSE(position.timestamp.is_null());
- EXPECT_TRUE(ValidateGeoposition(position));
-
- // 4. Wifi updated again, with one less AP. This is 'close enough' to the
- // previous scan, so no new request made.
- const int kSecondScanAps = kFirstScanAps - 1;
- wifi_data_provider_->SetData(CreateReferenceWifiScanData(kSecondScanAps));
- base::RunLoop().RunUntilIdle();
- fetcher = get_url_fetcher_and_advance_id();
- EXPECT_FALSE(fetcher);
-
- position = provider->GetPosition();
- EXPECT_EQ(51.0, position.latitude);
- EXPECT_EQ(-0.1, position.longitude);
- EXPECT_TRUE(ValidateGeoposition(position));
-
- // 5. Now a third scan with more than twice the original APs -> new request.
- const int kThirdScanAps = kFirstScanAps * 2 + 1;
- wifi_data_provider_->SetData(CreateReferenceWifiScanData(kThirdScanAps));
- base::RunLoop().RunUntilIdle();
- fetcher = get_url_fetcher_and_advance_id();
- EXPECT_TRUE(fetcher);
- CheckRequestIsValid(*fetcher, kThirdScanAps, 0);
-
- // 6. ...reply with a network error.
- fetcher->set_status(net::URLRequestStatus::FromError(net::ERR_FAILED));
- fetcher->set_response_code(200); // should be ignored
- fetcher->SetResponseString(std::string());
- fetcher->delegate()->OnURLFetchComplete(fetcher);
-
- // Error means we now no longer have a fix.
- position = provider->GetPosition();
- EXPECT_FALSE(ValidateGeoposition(position));
-
- // 7. Wifi scan returns to original set: should be serviced from cache.
- wifi_data_provider_->SetData(CreateReferenceWifiScanData(kFirstScanAps));
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(get_url_fetcher_and_advance_id()); // No new request created.
-
- position = provider->GetPosition();
- EXPECT_EQ(51.0, position.latitude);
- EXPECT_EQ(-0.1, position.longitude);
- EXPECT_TRUE(ValidateGeoposition(position));
-}
-
-// Tests that, if no Wifi scan data is available at startup, the provider
-// doesn't initiate a request, until Wifi data later becomes available.
-TEST_F(GeolocationNetworkProviderTest, NoRequestOnStartupUntilWifiData) {
- LocationUpdateListener listener;
- wifi_data_provider_->set_got_data(false); // No initial Wifi data.
- std::unique_ptr<LocationProvider> provider(CreateProvider(true));
- provider->StartProvider(false);
-
- provider->SetUpdateCallback(listener.callback);
-
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(get_url_fetcher_and_advance_id())
- << "Network request should not be created right away on startup when "
- "wifi data has not yet arrived";
-
- // Now Wifi data becomes available.
- wifi_data_provider_->SetData(CreateReferenceWifiScanData(1));
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(get_url_fetcher_and_advance_id());
-}
-
-// Tests that, even if a request is already in flight, new wifi data results in
-// a new request being sent.
-TEST_F(GeolocationNetworkProviderTest, NewDataReplacesExistingNetworkRequest) {
- // Send initial request with empty data
- std::unique_ptr<LocationProvider> provider(CreateProvider(true));
- provider->StartProvider(false);
- net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
- EXPECT_TRUE(fetcher);
-
- // Now wifi data arrives; new request should be sent.
- wifi_data_provider_->SetData(CreateReferenceWifiScanData(4));
- base::RunLoop().RunUntilIdle();
- fetcher = get_url_fetcher_and_advance_id();
- EXPECT_TRUE(fetcher);
-}
-
-// Tests that, if user geolocation permission hasn't been granted during
-// startup, the provider doesn't initiate a request until it is notified of the
-// user granting permission.
-TEST_F(GeolocationNetworkProviderTest, NetworkRequestDeferredForPermission) {
- std::unique_ptr<LocationProvider> provider(CreateProvider(false));
- provider->StartProvider(false);
- net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
- EXPECT_FALSE(fetcher);
- provider->OnPermissionGranted();
-
- fetcher = get_url_fetcher_and_advance_id();
- ASSERT_TRUE(fetcher);
-}
-
-// Tests that, even if new Wifi data arrives, the provider doesn't initiate its
-// first request unless & until the user grants permission.
-TEST_F(GeolocationNetworkProviderTest,
- NetworkRequestWithWifiDataDeferredForPermission) {
- std::unique_ptr<LocationProvider> provider(CreateProvider(false));
- provider->StartProvider(false);
- net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
- EXPECT_FALSE(fetcher);
-
- static const int kScanCount = 4;
- wifi_data_provider_->SetData(CreateReferenceWifiScanData(kScanCount));
- base::RunLoop().RunUntilIdle();
-
- fetcher = get_url_fetcher_and_advance_id();
- EXPECT_FALSE(fetcher);
-
- provider->OnPermissionGranted();
-
- fetcher = get_url_fetcher_and_advance_id();
- ASSERT_TRUE(fetcher);
-
- CheckRequestIsValid(*fetcher, kScanCount, 0);
-}
-
-// Tests that the provider's position cache correctly caches each item, and
-// begins evicting the oldest entries in order once it reaches its maximum size.
-TEST_F(GeolocationNetworkProviderTest, NetworkPositionCache) {
- NetworkLocationProvider::PositionCache cache;
-
- const int kCacheSize = NetworkLocationProvider::PositionCache::kMaximumSize;
- for (int i = 1; i < kCacheSize * 2 + 1; ++i) {
- mojom::Geoposition pos = CreateReferencePosition(i);
- bool ret = cache.CachePosition(CreateReferenceWifiScanData(i), pos);
- EXPECT_TRUE(ret) << i;
- const mojom::Geoposition* item =
- cache.FindPosition(CreateReferenceWifiScanData(i));
- ASSERT_TRUE(item) << i;
- EXPECT_EQ(pos.latitude, item->latitude) << i;
- EXPECT_EQ(pos.longitude, item->longitude) << i;
- if (i <= kCacheSize) {
- // Nothing should have spilled yet; check oldest item is still there.
- EXPECT_TRUE(cache.FindPosition(CreateReferenceWifiScanData(1)));
- } else {
- const int evicted = i - kCacheSize;
- EXPECT_FALSE(cache.FindPosition(CreateReferenceWifiScanData(evicted)));
- EXPECT_TRUE(cache.FindPosition(CreateReferenceWifiScanData(evicted + 1)));
- }
- }
-}
-
-// Tests that the provider's last position cache delegate is correctly used to
-// cache the most recent network position estimate, and that this estimate is
-// not lost when the provider is torn down and recreated.
-TEST_F(GeolocationNetworkProviderTest, LastPositionCache) {
- std::unique_ptr<LocationProvider> provider(CreateProvider(true));
- provider->StartProvider(false);
-
- // Check that the provider is initialized with an invalid position.
- mojom::Geoposition position = provider->GetPosition();
- EXPECT_FALSE(ValidateGeoposition(position));
-
- // Check that the cached value is also invalid.
- position = last_position_cache_->GetLastNetworkPosition();
- EXPECT_FALSE(ValidateGeoposition(position));
-
- // Now wifi data arrives -- SetData will notify listeners.
- const int kFirstScanAps = 6;
- wifi_data_provider_->SetData(CreateReferenceWifiScanData(kFirstScanAps));
- base::RunLoop().RunUntilIdle();
- net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
- ASSERT_TRUE(fetcher);
- // The request should have the wifi data.
- CheckRequestIsValid(*fetcher, kFirstScanAps, 0);
-
- // Send a reply with good position fix.
- const char* kReferenceNetworkResponse =
- "{"
- " \"accuracy\": 1200.4,"
- " \"location\": {"
- " \"lat\": 51.0,"
- " \"lng\": -0.1"
- " }"
- "}";
- fetcher->set_status(net::URLRequestStatus());
- fetcher->set_response_code(200); // OK
- fetcher->SetResponseString(kReferenceNetworkResponse);
- fetcher->delegate()->OnURLFetchComplete(fetcher);
-
- // The provider should return the position as the current best estimate.
- position = provider->GetPosition();
- EXPECT_EQ(51.0, position.latitude);
- EXPECT_EQ(-0.1, position.longitude);
- EXPECT_EQ(1200.4, position.accuracy);
- EXPECT_FALSE(position.timestamp.is_null());
- EXPECT_TRUE(ValidateGeoposition(position));
-
- // Shut down the provider. This typically happens whenever there are no active
- // Geolocation API calls.
- provider->StopProvider();
- provider = nullptr;
-
- // The cache preserves the last estimate while the provider is inactive.
- position = last_position_cache_->GetLastNetworkPosition();
- EXPECT_EQ(51.0, position.latitude);
- EXPECT_EQ(-0.1, position.longitude);
- EXPECT_EQ(1200.4, position.accuracy);
- EXPECT_FALSE(position.timestamp.is_null());
- EXPECT_TRUE(ValidateGeoposition(position));
-
- // Restart the provider.
- provider.reset(CreateProvider(true));
- provider->StartProvider(false);
-
- // Check that the most recent position estimate is retained.
- position = provider->GetPosition();
- EXPECT_EQ(51.0, position.latitude);
- EXPECT_EQ(-0.1, position.longitude);
- EXPECT_EQ(1200.4, position.accuracy);
- EXPECT_FALSE(position.timestamp.is_null());
- EXPECT_TRUE(ValidateGeoposition(position));
-}
-
-// Tests that when the last network position estimate is sufficiently recent and
-// we do not expect to receive a fresh estimate soon (no new wifi data available
-// and no pending geolocation service request) then the provider may return the
-// last position instead of waiting to acquire a fresh estimate.
-TEST_F(GeolocationNetworkProviderTest, LastPositionCacheUsed) {
- LocationUpdateListener listener;
-
- // Seed the last position cache with a valid geoposition value and the
- // timestamp set to the current time.
- mojom::Geoposition last_position = CreateReferencePosition(0);
- EXPECT_TRUE(ValidateGeoposition(last_position));
- last_position_cache_->SetLastNetworkPosition(last_position);
-
- // Simulate no initial wifi data.
- wifi_data_provider_->set_got_data(false);
-
- // Start the provider without geolocation permission.
- std::unique_ptr<LocationProvider> provider(CreateProvider(false));
- provider->StartProvider(false);
-
- // Register a location update callback. The listener will count how many times
- // OnLocationUpdate is called.
- provider->SetUpdateCallback(listener.callback);
-
- // Under normal circumstances, when there is no initial wifi data
- // RequestPosition is not called until a few seconds after the provider is
- // started to allow time for the wifi scan to complete. To avoid waiting,
- // grant permissions once the provider is running to cause RequestPosition to
- // be called immediately.
- provider->OnPermissionGranted();
-
- base::RunLoop().RunUntilIdle();
-
- // Check that the listener received the position update and that the position
- // is the same as the seeded value except for the timestamp, which should be
- // newer.
- EXPECT_EQ(1, listener.update_count);
- EXPECT_TRUE(ValidateGeoposition(listener.last_position));
- EXPECT_EQ(last_position.latitude, listener.last_position.latitude);
- EXPECT_EQ(last_position.longitude, listener.last_position.longitude);
- EXPECT_EQ(last_position.accuracy, listener.last_position.accuracy);
- EXPECT_LT(last_position.timestamp, listener.last_position.timestamp);
-}
-
-// Tests that the last network position estimate is not returned if the
-// estimate is too old.
-TEST_F(GeolocationNetworkProviderTest, LastPositionNotUsedTooOld) {
- LocationUpdateListener listener;
-
- // Seed the last position cache with a geoposition value with the timestamp
- // set to 20 minutes ago.
- mojom::Geoposition last_position = CreateReferencePosition(0);
- last_position.timestamp =
- base::Time::Now() - base::TimeDelta::FromMinutes(20);
- EXPECT_TRUE(ValidateGeoposition(last_position));
- last_position_cache_->SetLastNetworkPosition(last_position);
-
- // Simulate no initial wifi data.
- wifi_data_provider_->set_got_data(false);
-
- // Start the provider without geolocation permission.
- std::unique_ptr<LocationProvider> provider(CreateProvider(false));
- provider->StartProvider(false);
-
- // Register a location update callback. The listener will count how many times
- // OnLocationUpdate is called.
- provider->SetUpdateCallback(listener.callback);
-
- // Under normal circumstances, when there is no initial wifi data
- // RequestPosition is not called until a few seconds after the provider is
- // started to allow time for the wifi scan to complete. To avoid waiting,
- // grant permissions once the provider is running to cause RequestPosition to
- // be called immediately.
- provider->OnPermissionGranted();
-
- base::RunLoop().RunUntilIdle();
-
- // Check that the listener received no updates.
- EXPECT_EQ(0, listener.update_count);
- EXPECT_FALSE(ValidateGeoposition(listener.last_position));
-}
-
-// Tests that the last network position estimate is not returned if there is
-// new wifi data or a pending geolocation service request.
-TEST_F(GeolocationNetworkProviderTest, LastPositionNotUsedNewData) {
- LocationUpdateListener listener;
-
- // Seed the last position cache with a valid geoposition value. The timestamp
- // of the cached position is set to the current time.
- mojom::Geoposition last_position = CreateReferencePosition(0);
- EXPECT_TRUE(ValidateGeoposition(last_position));
- last_position_cache_->SetLastNetworkPosition(last_position);
-
- // Simulate a completed wifi scan.
- const int kFirstScanAps = 6;
- wifi_data_provider_->SetData(CreateReferenceWifiScanData(kFirstScanAps));
-
- // Create the provider without permissions enabled.
- std::unique_ptr<LocationProvider> provider(CreateProvider(false));
-
- // Register a location update callback. The callback will count how many times
- // OnLocationUpdate is called.
- provider->SetUpdateCallback(listener.callback);
-
- // Start the provider.
- provider->StartProvider(false);
- base::RunLoop().RunUntilIdle();
-
- // The listener should not receive any updates. There is a valid cached value
- // but it should not be sent while we have pending wifi data.
- EXPECT_EQ(0, listener.update_count);
- EXPECT_FALSE(ValidateGeoposition(listener.last_position));
-
- // Check that there is no pending network request.
- EXPECT_FALSE(get_url_fetcher_and_advance_id());
-
- // Simulate no new wifi data.
- wifi_data_provider_->set_got_data(false);
-
- // Grant permission to allow the network request to proceed.
- provider->OnPermissionGranted();
- base::RunLoop().RunUntilIdle();
-
- // The listener should still not receive any updates. There is a valid cached
- // value and no new wifi data, but the cached value should not be sent while
- // we have a pending request to the geolocation service.
- EXPECT_EQ(0, listener.update_count);
- EXPECT_FALSE(ValidateGeoposition(listener.last_position));
-
- // Check that a network request is pending.
- EXPECT_TRUE(get_url_fetcher_and_advance_id());
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/network_location_request.cc b/chromium/device/geolocation/network_location_request.cc
deleted file mode 100644
index a9a739e4f9a..00000000000
--- a/chromium/device/geolocation/network_location_request.cc
+++ /dev/null
@@ -1,434 +0,0 @@
-// Copyright (c) 2012 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/geolocation/network_location_request.h"
-
-#include <stdint.h>
-
-#include <limits>
-#include <set>
-#include <string>
-#include <utility>
-
-#include "base/json/json_reader.h"
-#include "base/json/json_writer.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/values.h"
-#include "device/geolocation/location_arbitrator.h"
-#include "device/geolocation/public/cpp/geoposition.h"
-#include "net/base/escape.h"
-#include "net/base/load_flags.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "net/url_request/url_request_status.h"
-
-namespace device {
-namespace {
-
-const char kNetworkLocationBaseUrl[] =
- "https://www.googleapis.com/geolocation/v1/geolocate";
-
-const char kLocationString[] = "location";
-const char kLatitudeString[] = "lat";
-const char kLongitudeString[] = "lng";
-const char kAccuracyString[] = "accuracy";
-
-enum NetworkLocationRequestEvent {
- // NOTE: Do not renumber these as that would confuse interpretation of
- // previously logged data. When making changes, also update the enum list
- // in tools/metrics/histograms/histograms.xml to keep it in sync.
- NETWORK_LOCATION_REQUEST_EVENT_REQUEST_START = 0,
- NETWORK_LOCATION_REQUEST_EVENT_REQUEST_CANCEL = 1,
- NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_SUCCESS = 2,
- NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_NOT_OK = 3,
- NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_EMPTY = 4,
- NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_MALFORMED = 5,
- NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_INVALID_FIX = 6,
-
- // NOTE: Add entries only immediately above this line.
- NETWORK_LOCATION_REQUEST_EVENT_COUNT = 7
-};
-
-void RecordUmaEvent(NetworkLocationRequestEvent event) {
- UMA_HISTOGRAM_ENUMERATION("Geolocation.NetworkLocationRequest.Event", event,
- NETWORK_LOCATION_REQUEST_EVENT_COUNT);
-}
-
-void RecordUmaResponseCode(int code) {
- base::UmaHistogramSparse("Geolocation.NetworkLocationRequest.ResponseCode",
- code);
-}
-
-void RecordUmaAccessPoints(int count) {
- const int min = 1;
- const int max = 20;
- const int buckets = 21;
- UMA_HISTOGRAM_CUSTOM_COUNTS("Geolocation.NetworkLocationRequest.AccessPoints",
- count, min, max, buckets);
-}
-
-// Local functions
-
-// Returns a URL for a request to the Google Maps geolocation API. If the
-// specified |api_key| is not empty, it is escaped and included as a query
-// string parameter.
-GURL FormRequestURL(const std::string& api_key);
-
-void FormUploadData(const WifiData& wifi_data,
- const base::Time& wifi_timestamp,
- std::string* upload_data);
-
-// Attempts to extract a position from the response. Detects and indicates
-// various failure cases.
-void GetLocationFromResponse(bool http_post_result,
- int status_code,
- const std::string& response_body,
- const base::Time& wifi_timestamp,
- const GURL& server_url,
- mojom::Geoposition* position);
-
-// Parses the server response body. Returns true if parsing was successful.
-// Sets |*position| to the parsed location if a valid fix was received,
-// otherwise leaves it unchanged.
-bool ParseServerResponse(const std::string& response_body,
- const base::Time& wifi_timestamp,
- mojom::Geoposition* position);
-void AddWifiData(const WifiData& wifi_data,
- int age_milliseconds,
- base::DictionaryValue* request);
-} // namespace
-
-int NetworkLocationRequest::url_fetcher_id_for_tests = 0;
-
-NetworkLocationRequest::NetworkLocationRequest(
- scoped_refptr<net::URLRequestContextGetter> context,
- const std::string& api_key,
- LocationResponseCallback callback)
- : url_context_(std::move(context)),
- api_key_(api_key),
- location_response_callback_(callback) {}
-
-NetworkLocationRequest::~NetworkLocationRequest() = default;
-
-bool NetworkLocationRequest::MakeRequest(
- const WifiData& wifi_data,
- const base::Time& wifi_timestamp,
- const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation) {
- RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_REQUEST_START);
- RecordUmaAccessPoints(wifi_data.access_point_data.size());
- if (url_fetcher_ != NULL) {
- DVLOG(1) << "NetworkLocationRequest : Cancelling pending request";
- RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_REQUEST_CANCEL);
- url_fetcher_.reset();
- }
- wifi_data_ = wifi_data;
- wifi_timestamp_ = wifi_timestamp;
-
- net::NetworkTrafficAnnotationTag traffic_annotation =
- net::CompleteNetworkTrafficAnnotation("network_location_request",
- partial_traffic_annotation,
- R"(
- semantics {
- description:
- "Obtains geo position based on current IP address."
- trigger:
- "Location requests are sent when the page requests them or new "
- "IP address is available."
- data: "IP Address."
- destination: GOOGLE_OWNED_SERVICE
- }
- policy {
- cookies_allowed: NO
- })");
- const GURL request_url = FormRequestURL(api_key_);
- DCHECK(request_url.is_valid());
- url_fetcher_ =
- net::URLFetcher::Create(url_fetcher_id_for_tests, request_url,
- net::URLFetcher::POST, this, traffic_annotation);
- url_fetcher_->SetRequestContext(url_context_.get());
- std::string upload_data;
- FormUploadData(wifi_data, wifi_timestamp, &upload_data);
- url_fetcher_->SetUploadData("application/json", upload_data);
- url_fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
- net::LOAD_DO_NOT_SAVE_COOKIES |
- net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SEND_AUTH_DATA);
-
- request_start_time_ = base::TimeTicks::Now();
- url_fetcher_->Start();
- return true;
-}
-
-void NetworkLocationRequest::OnURLFetchComplete(const net::URLFetcher* source) {
- DCHECK_EQ(url_fetcher_.get(), source);
-
- net::URLRequestStatus status = source->GetStatus();
- int response_code = source->GetResponseCode();
- RecordUmaResponseCode(response_code);
-
- mojom::Geoposition position;
- std::string data;
- source->GetResponseAsString(&data);
- GetLocationFromResponse(status.is_success(), response_code, data,
- wifi_timestamp_, source->GetURL(), &position);
- const bool server_error =
- !status.is_success() || (response_code >= 500 && response_code < 600);
- url_fetcher_.reset();
-
- if (!server_error) {
- const base::TimeDelta request_time =
- base::TimeTicks::Now() - request_start_time_;
-
- UMA_HISTOGRAM_CUSTOM_TIMES("Net.Wifi.LbsLatency", request_time,
- base::TimeDelta::FromMilliseconds(1),
- base::TimeDelta::FromSeconds(10), 100);
- }
-
- DVLOG(1) << "NetworkLocationRequest::OnURLFetchComplete() : run callback.";
- location_response_callback_.Run(position, server_error, wifi_data_);
-}
-
-// Local functions.
-namespace {
-
-struct AccessPointLess {
- bool operator()(const AccessPointData* ap1,
- const AccessPointData* ap2) const {
- return ap2->radio_signal_strength < ap1->radio_signal_strength;
- }
-};
-
-GURL FormRequestURL(const std::string& api_key) {
- GURL url(kNetworkLocationBaseUrl);
- if (!api_key.empty()) {
- std::string query(url.query());
- if (!query.empty())
- query += "&";
- query += "key=" + net::EscapeQueryParamValue(api_key, true);
- GURL::Replacements replacements;
- replacements.SetQueryStr(query);
- return url.ReplaceComponents(replacements);
- }
- return url;
-}
-
-void FormUploadData(const WifiData& wifi_data,
- const base::Time& wifi_timestamp,
- std::string* upload_data) {
- int age = std::numeric_limits<int32_t>::min(); // Invalid so AddInteger()
- // will ignore.
- if (!wifi_timestamp.is_null()) {
- // Convert absolute timestamps into a relative age.
- int64_t delta_ms = (base::Time::Now() - wifi_timestamp).InMilliseconds();
- if (delta_ms >= 0 && delta_ms < std::numeric_limits<int32_t>::max())
- age = static_cast<int>(delta_ms);
- }
-
- base::DictionaryValue request;
- AddWifiData(wifi_data, age, &request);
- base::JSONWriter::Write(request, upload_data);
-}
-
-void AddString(const std::string& property_name,
- const std::string& value,
- base::DictionaryValue* dict) {
- DCHECK(dict);
- if (!value.empty())
- dict->SetString(property_name, value);
-}
-
-void AddInteger(const std::string& property_name,
- int value,
- base::DictionaryValue* dict) {
- DCHECK(dict);
- if (value != std::numeric_limits<int32_t>::min())
- dict->SetInteger(property_name, value);
-}
-
-void AddWifiData(const WifiData& wifi_data,
- int age_milliseconds,
- base::DictionaryValue* request) {
- DCHECK(request);
-
- if (wifi_data.access_point_data.empty())
- return;
-
- typedef std::multiset<const AccessPointData*, AccessPointLess> AccessPointSet;
- AccessPointSet access_points_by_signal_strength;
-
- for (const auto& ap_data : wifi_data.access_point_data)
- access_points_by_signal_strength.insert(&ap_data);
-
- auto wifi_access_point_list = std::make_unique<base::ListValue>();
- for (auto* ap_data : access_points_by_signal_strength) {
- auto wifi_dict = std::make_unique<base::DictionaryValue>();
- AddString("macAddress", base::UTF16ToUTF8(ap_data->mac_address),
- wifi_dict.get());
- AddInteger("signalStrength", ap_data->radio_signal_strength,
- wifi_dict.get());
- AddInteger("age", age_milliseconds, wifi_dict.get());
- AddInteger("channel", ap_data->channel, wifi_dict.get());
- AddInteger("signalToNoiseRatio", ap_data->signal_to_noise, wifi_dict.get());
- wifi_access_point_list->Append(std::move(wifi_dict));
- }
- request->Set("wifiAccessPoints", std::move(wifi_access_point_list));
-}
-
-void FormatPositionError(const GURL& server_url,
- const std::string& message,
- mojom::Geoposition* position) {
- position->error_code = mojom::Geoposition::ErrorCode::POSITION_UNAVAILABLE;
- position->error_message = "Network location provider at '";
- position->error_message += server_url.GetOrigin().spec();
- position->error_message += "' : ";
- position->error_message += message;
- position->error_message += ".";
- VLOG(1) << "NetworkLocationRequest::GetLocationFromResponse() : "
- << position->error_message;
-}
-
-void GetLocationFromResponse(bool http_post_result,
- int status_code,
- const std::string& response_body,
- const base::Time& wifi_timestamp,
- const GURL& server_url,
- mojom::Geoposition* position) {
- DCHECK(position);
-
- // HttpPost can fail for a number of reasons. Most likely this is because
- // we're offline, or there was no response.
- if (!http_post_result) {
- FormatPositionError(server_url, "No response received", position);
- RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_EMPTY);
- return;
- }
- if (status_code != 200) { // HTTP OK.
- std::string message = "Returned error code ";
- message += base::IntToString(status_code);
- FormatPositionError(server_url, message, position);
- RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_NOT_OK);
- return;
- }
- // We use the timestamp from the wifi data that was used to generate
- // this position fix.
- if (!ParseServerResponse(response_body, wifi_timestamp, position)) {
- // We failed to parse the repsonse.
- FormatPositionError(server_url, "Response was malformed", position);
- RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_MALFORMED);
- return;
- }
- // The response was successfully parsed, but it may not be a valid
- // position fix.
- if (!ValidateGeoposition(*position)) {
- FormatPositionError(server_url, "Did not provide a good position fix",
- position);
- RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_INVALID_FIX);
- return;
- }
- RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_SUCCESS);
-}
-
-// Numeric values without a decimal point have type integer and IsDouble() will
-// return false. This is convenience function for detecting integer or floating
-// point numeric values. Note that isIntegral() includes boolean values, which
-// is not what we want.
-bool GetAsDouble(const base::DictionaryValue& object,
- const std::string& property_name,
- double* out) {
- DCHECK(out);
- const base::Value* value = NULL;
- if (!object.Get(property_name, &value))
- return false;
- int value_as_int;
- DCHECK(value);
- if (value->GetAsInteger(&value_as_int)) {
- *out = value_as_int;
- return true;
- }
- return value->GetAsDouble(out);
-}
-
-bool ParseServerResponse(const std::string& response_body,
- const base::Time& wifi_timestamp,
- mojom::Geoposition* position) {
- DCHECK(position);
- DCHECK(!ValidateGeoposition(*position));
- DCHECK(position->error_code == mojom::Geoposition::ErrorCode::NONE);
- DCHECK(!wifi_timestamp.is_null());
-
- if (response_body.empty()) {
- LOG(WARNING) << "ParseServerResponse() : Response was empty.";
- return false;
- }
- DVLOG(1) << "ParseServerResponse() : Parsing response " << response_body;
-
- // Parse the response, ignoring comments.
- std::string error_msg;
- std::unique_ptr<base::Value> response_value =
- base::JSONReader::ReadAndReturnError(response_body, base::JSON_PARSE_RFC,
- NULL, &error_msg);
- if (response_value == NULL) {
- LOG(WARNING) << "ParseServerResponse() : JSONReader failed : " << error_msg;
- return false;
- }
-
- if (!response_value->is_dict()) {
- VLOG(1) << "ParseServerResponse() : Unexpected response type "
- << response_value->type();
- return false;
- }
- const base::DictionaryValue* response_object =
- static_cast<base::DictionaryValue*>(response_value.get());
-
- // Get the location
- const base::Value* location_value = NULL;
- if (!response_object->Get(kLocationString, &location_value)) {
- VLOG(1) << "ParseServerResponse() : Missing location attribute.";
- // GLS returns a response with no location property to represent
- // no fix available; return true to indicate successful parse.
- return true;
- }
- DCHECK(location_value);
-
- if (!location_value->is_dict()) {
- if (!location_value->is_none()) {
- VLOG(1) << "ParseServerResponse() : Unexpected location type "
- << location_value->type();
- // If the network provider was unable to provide a position fix, it should
- // return a HTTP 200, with "location" : null. Otherwise it's an error.
- return false;
- }
- return true; // Successfully parsed response containing no fix.
- }
- const base::DictionaryValue* location_object =
- static_cast<const base::DictionaryValue*>(location_value);
-
- // latitude and longitude fields are always required.
- double latitude = 0;
- double longitude = 0;
- if (!GetAsDouble(*location_object, kLatitudeString, &latitude) ||
- !GetAsDouble(*location_object, kLongitudeString, &longitude)) {
- VLOG(1) << "ParseServerResponse() : location lacks lat and/or long.";
- return false;
- }
- // All error paths covered: now start actually modifying postion.
- position->latitude = latitude;
- position->longitude = longitude;
- position->timestamp = wifi_timestamp;
-
- // Other fields are optional.
- GetAsDouble(*response_object, kAccuracyString, &position->accuracy);
-
- return true;
-}
-
-} // namespace
-
-} // namespace device
diff --git a/chromium/device/geolocation/network_location_request.h b/chromium/device/geolocation/network_location_request.h
deleted file mode 100644
index 066294e4702..00000000000
--- a/chromium/device/geolocation/network_location_request.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (c) 2012 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_GEOLOCATION_NETWORK_LOCATION_REQUEST_H_
-#define DEVICE_GEOLOCATION_NETWORK_LOCATION_REQUEST_H_
-
-#include <memory>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/strings/string16.h"
-#include "device/geolocation/geolocation_export.h"
-#include "device/geolocation/wifi_data_provider.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "services/device/public/mojom/geoposition.mojom.h"
-#include "url/gurl.h"
-
-namespace net {
-class URLFetcher;
-class URLRequestContextGetter;
-
-struct PartialNetworkTrafficAnnotationTag;
-}
-
-namespace device {
-
-// Takes wifi data and sends it to a server to get a position fix.
-// It performs formatting of the request and interpretation of the response.
-class DEVICE_GEOLOCATION_EXPORT NetworkLocationRequest
- : private net::URLFetcherDelegate {
- public:
- // ID passed to URLFetcher::Create(). Used for testing.
- static int url_fetcher_id_for_tests;
-
- // Called when a new geo position is available. The second argument indicates
- // whether there was a server error or not. It is true when there was a
- // server or network error - either no response or a 500 error code.
- using LocationResponseCallback =
- base::Callback<void(const mojom::Geoposition& /* position */,
- bool /* server_error */,
- const WifiData& /* wifi_data */)>;
-
- NetworkLocationRequest(scoped_refptr<net::URLRequestContextGetter> context,
- const std::string& api_key,
- LocationResponseCallback callback);
- ~NetworkLocationRequest() override;
-
- // Makes a new request using the specified |wifi_data|. Returns true if the
- // new request was successfully started. In all cases, any currently pending
- // request will be canceled. The specified |wifi_data| and |wifi_timestamp|
- // are passed back to the client upon completion, via
- // LocationResponseCallback's |wifi_data| and |position.timestamp|
- // respectively.
- bool MakeRequest(const WifiData& wifi_data,
- const base::Time& wifi_timestamp,
- const net::PartialNetworkTrafficAnnotationTag&
- partial_traffic_annotation);
-
- bool is_request_pending() const { return url_fetcher_ != NULL; }
-
- private:
- // net::URLFetcherDelegate
- void OnURLFetchComplete(const net::URLFetcher* source) override;
-
- const scoped_refptr<net::URLRequestContextGetter> url_context_;
- const std::string api_key_;
- const LocationResponseCallback location_response_callback_;
- std::unique_ptr<net::URLFetcher> url_fetcher_;
-
- // Keep a copy of the data sent in the request, so we can refer back to it
- // when the response arrives.
- WifiData wifi_data_;
- base::Time wifi_timestamp_;
-
- // The start time for the request.
- base::TimeTicks request_start_time_;
-
- DISALLOW_COPY_AND_ASSIGN(NetworkLocationRequest);
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_NETWORK_LOCATION_REQUEST_H_
diff --git a/chromium/device/geolocation/public/cpp/BUILD.gn b/chromium/device/geolocation/public/cpp/BUILD.gn
deleted file mode 100644
index 3d34b870a3b..00000000000
--- a/chromium/device/geolocation/public/cpp/BUILD.gn
+++ /dev/null
@@ -1,29 +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.
-
-source_set("cpp") {
- sources = [
- "geoposition.cc",
- "geoposition.h",
- "location_provider.h",
- ]
-
- public_deps = [
- "//services/device/public/mojom",
- ]
-}
-
-source_set("test_support") {
- testonly = true
-
- sources = [
- "scoped_geolocation_overrider.cc",
- "scoped_geolocation_overrider.h",
- ]
-
- deps = [
- ":cpp",
- "//services/service_manager/public/cpp",
- ]
-}
diff --git a/chromium/device/geolocation/public/cpp/geoposition.cc b/chromium/device/geolocation/public/cpp/geoposition.cc
deleted file mode 100644
index 85cfa1746e4..00000000000
--- a/chromium/device/geolocation/public/cpp/geoposition.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 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/geolocation/public/cpp/geoposition.h"
-
-namespace device {
-
-bool ValidateGeoposition(const mojom::Geoposition& position) {
- return position.latitude >= -90. && position.latitude <= 90. &&
- position.longitude >= -180. && position.longitude <= 180. &&
- position.accuracy >= 0. && !position.timestamp.is_null();
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/public/cpp/geoposition.h b/chromium/device/geolocation/public/cpp/geoposition.h
deleted file mode 100644
index 9130e48a311..00000000000
--- a/chromium/device/geolocation/public/cpp/geoposition.h
+++ /dev/null
@@ -1,16 +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_GEOLOCATION_PUBLIC_CPP_GEOPOSITION_H_
-#define DEVICE_GEOLOCATION_PUBLIC_CPP_GEOPOSITION_H_
-
-#include "services/device/public/mojom/geoposition.mojom.h"
-
-namespace device {
-
-bool ValidateGeoposition(const mojom::Geoposition& position);
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_PUBLIC_CPP_GEOPOSITION_H_
diff --git a/chromium/device/geolocation/public/cpp/location_provider.h b/chromium/device/geolocation/public/cpp/location_provider.h
deleted file mode 100644
index 1e1399c17f3..00000000000
--- a/chromium/device/geolocation/public/cpp/location_provider.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 2013 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_GEOLOCATION_PUBLIC_CPP_LOCATION_PROVIDER_H_
-#define DEVICE_GEOLOCATION_PUBLIC_CPP_LOCATION_PROVIDER_H_
-
-#include "base/callback.h"
-#include "base/compiler_specific.h"
-#include "services/device/public/mojom/geoposition.mojom.h"
-
-namespace device {
-
-// The interface for providing location information.
-class LocationProvider {
- public:
- virtual ~LocationProvider() {}
-
- typedef base::Callback<void(const LocationProvider*,
- const mojom::Geoposition&)>
- LocationProviderUpdateCallback;
-
- // This callback will be used to notify when a new Geoposition becomes
- // available.
- virtual void SetUpdateCallback(
- const LocationProviderUpdateCallback& callback) = 0;
-
- // StartProvider maybe called multiple times, e.g. to alter the
- // |high_accuracy| setting.
- virtual void StartProvider(bool high_accuracy) = 0;
-
- // Stops the provider from sending more requests.
- // Important: a LocationProvider may be instantiated and StartProvider() may
- // be called before the user has granted permission via OnPermissionGranted().
- // This is to allow underlying providers to warm up, load their internal
- // libraries, etc. No |LocationProviderUpdateCallback| can be run and no
- // network requests can be done until OnPermissionGranted() has been called.
- virtual void StopProvider() = 0;
-
- // Gets the current best position estimate.
- virtual const mojom::Geoposition& GetPosition() = 0;
-
- // Called everytime permission is granted to a page for using geolocation.
- // This may either be through explicit user action (e.g. responding to the
- // infobar prompt) or inferred from a persisted site permission.
- // Note: See |StartProvider()| for more information.
- virtual void OnPermissionGranted() = 0;
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_PUBLIC_CPP_LOCATION_PROVIDER_H_
diff --git a/chromium/device/geolocation/public/cpp/scoped_geolocation_overrider.cc b/chromium/device/geolocation/public/cpp/scoped_geolocation_overrider.cc
deleted file mode 100644
index 20e651a2861..00000000000
--- a/chromium/device/geolocation/public/cpp/scoped_geolocation_overrider.cc
+++ /dev/null
@@ -1,205 +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 <vector>
-
-#include "device/geolocation/public/cpp/geoposition.h"
-#include "device/geolocation/public/cpp/scoped_geolocation_overrider.h"
-#include "services/device/public/mojom/constants.mojom.h"
-#include "services/service_manager/public/cpp/service_context.h"
-
-namespace device {
-
-// This class is a fake implementation of GeolocationContext and Geolocation
-// mojo interfaces for those tests which want to set an override geoposition
-// value and verify their code where there are geolocation mojo calls.
-class ScopedGeolocationOverrider::FakeGeolocationContext
- : public mojom::GeolocationContext {
- public:
- explicit FakeGeolocationContext(const mojom::Geoposition& position);
- ~FakeGeolocationContext() override;
-
- void UpdateLocation(const mojom::Geoposition& position);
- const mojom::Geoposition& GetGeoposition() const;
-
- void BindForOverrideService(
- const std::string& interface_name,
- mojo::ScopedMessagePipeHandle handle,
- const service_manager::BindSourceInfo& source_info);
-
- // mojom::GeolocationContext implementation:
- void BindGeolocation(mojom::GeolocationRequest request) override;
- void SetOverride(mojom::GeopositionPtr geoposition) override;
- void ClearOverride() override;
-
- private:
- mojom::Geoposition position_;
- mojom::GeopositionPtr override_position_;
- std::vector<std::unique_ptr<FakeGeolocation>> impls_;
- mojo::BindingSet<mojom::GeolocationContext> context_bindings_;
-};
-
-class ScopedGeolocationOverrider::FakeGeolocation : public mojom::Geolocation {
- public:
- FakeGeolocation(mojom::GeolocationRequest request,
- const FakeGeolocationContext* context);
- ~FakeGeolocation() override;
-
- void UpdateLocation(const mojom::Geoposition& position);
-
- // mojom::Geolocation implementation:
- void QueryNextPosition(QueryNextPositionCallback callback) override;
- void SetHighAccuracy(bool high_accuracy) override;
-
- private:
- const FakeGeolocationContext* context_;
- bool has_new_position_;
- QueryNextPositionCallback position_callback_;
- mojo::Binding<mojom::Geolocation> binding_;
-};
-
-ScopedGeolocationOverrider::ScopedGeolocationOverrider(
- const mojom::Geoposition& position) {
- OverrideGeolocation(position);
-}
-
-ScopedGeolocationOverrider::ScopedGeolocationOverrider(double latitude,
- double longitude) {
- mojom::Geoposition position;
- position.latitude = latitude;
- position.longitude = longitude;
- position.altitude = 0.;
- position.accuracy = 0.;
- position.timestamp = base::Time::Now();
-
- OverrideGeolocation(position);
-}
-
-ScopedGeolocationOverrider::~ScopedGeolocationOverrider() {
- service_manager::ServiceContext::ClearGlobalBindersForTesting(
- mojom::kServiceName);
-}
-
-void ScopedGeolocationOverrider::OverrideGeolocation(
- const mojom::Geoposition& position) {
- geolocation_context_ = std::make_unique<FakeGeolocationContext>(position);
- service_manager::ServiceContext::SetGlobalBinderForTesting(
- mojom::kServiceName, mojom::GeolocationContext::Name_,
- base::BindRepeating(&FakeGeolocationContext::BindForOverrideService,
- base::Unretained(geolocation_context_.get())));
-}
-
-void ScopedGeolocationOverrider::UpdateLocation(
- const mojom::Geoposition& position) {
- geolocation_context_->UpdateLocation(position);
-}
-
-void ScopedGeolocationOverrider::UpdateLocation(double latitude,
- double longitude) {
- mojom::Geoposition position;
- position.latitude = latitude;
- position.longitude = longitude;
- position.altitude = 0.;
- position.accuracy = 0.;
- position.timestamp = base::Time::Now();
-
- UpdateLocation(position);
-}
-
-ScopedGeolocationOverrider::FakeGeolocationContext::FakeGeolocationContext(
- const mojom::Geoposition& position)
- : position_(position) {
- position_.valid = false;
- if (ValidateGeoposition(position_))
- position_.valid = true;
-}
-
-ScopedGeolocationOverrider::FakeGeolocationContext::~FakeGeolocationContext() {}
-
-void ScopedGeolocationOverrider::FakeGeolocationContext::UpdateLocation(
- const mojom::Geoposition& position) {
- position_ = position;
-
- position_.valid = false;
- if (ValidateGeoposition(position_))
- position_.valid = true;
-
- for (auto& impl : impls_) {
- impl->UpdateLocation(position_);
- }
-}
-
-const mojom::Geoposition&
-ScopedGeolocationOverrider::FakeGeolocationContext::GetGeoposition() const {
- if (!override_position_.is_null())
- return *override_position_;
-
- return position_;
-}
-
-void ScopedGeolocationOverrider::FakeGeolocationContext::BindForOverrideService(
- const std::string& interface_name,
- mojo::ScopedMessagePipeHandle handle,
- const service_manager::BindSourceInfo& source_info) {
- context_bindings_.AddBinding(
- this, mojom::GeolocationContextRequest(std::move(handle)));
-}
-
-void ScopedGeolocationOverrider::FakeGeolocationContext::BindGeolocation(
- mojom::GeolocationRequest request) {
- impls_.push_back(std::make_unique<FakeGeolocation>(std::move(request), this));
-}
-
-void ScopedGeolocationOverrider::FakeGeolocationContext::SetOverride(
- mojom::GeopositionPtr geoposition) {
- override_position_ = std::move(geoposition);
- if (override_position_.is_null())
- return;
-
- override_position_->valid = false;
- if (ValidateGeoposition(*override_position_))
- override_position_->valid = true;
-
- for (auto& impl : impls_) {
- impl->UpdateLocation(*override_position_);
- }
-}
-
-void ScopedGeolocationOverrider::FakeGeolocationContext::ClearOverride() {
- override_position_.reset();
-}
-
-ScopedGeolocationOverrider::FakeGeolocation::FakeGeolocation(
- mojom::GeolocationRequest request,
- const FakeGeolocationContext* context)
- : context_(context), has_new_position_(true), binding_(this) {
- binding_.Bind(std::move(request));
-}
-
-ScopedGeolocationOverrider::FakeGeolocation::~FakeGeolocation() {}
-
-void ScopedGeolocationOverrider::FakeGeolocation::UpdateLocation(
- const mojom::Geoposition& position) {
- has_new_position_ = true;
- if (!position_callback_.is_null()) {
- std::move(position_callback_).Run(position.Clone());
- has_new_position_ = false;
- }
-}
-
-void ScopedGeolocationOverrider::FakeGeolocation::QueryNextPosition(
- QueryNextPositionCallback callback) {
- // Pending callbacks might be overrided.
- position_callback_ = std::move(callback);
-
- if (has_new_position_) {
- std::move(position_callback_).Run(context_->GetGeoposition().Clone());
- has_new_position_ = false;
- }
-}
-
-void ScopedGeolocationOverrider::FakeGeolocation::SetHighAccuracy(
- bool high_accuracy) {}
-
-} // namespace device
diff --git a/chromium/device/geolocation/public/cpp/scoped_geolocation_overrider.h b/chromium/device/geolocation/public/cpp/scoped_geolocation_overrider.h
deleted file mode 100644
index 84c5fb0fc83..00000000000
--- a/chromium/device/geolocation/public/cpp/scoped_geolocation_overrider.h
+++ /dev/null
@@ -1,41 +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_GEOLOCATION_PUBLIC_CPP_SCOPED_GEOLOCATION_OVERRIDER_H_
-#define DEVICE_GEOLOCATION_PUBLIC_CPP_SCOPED_GEOLOCATION_OVERRIDER_H_
-
-#include "base/bind.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/device/public/mojom/geolocation.mojom.h"
-#include "services/device/public/mojom/geolocation_context.mojom.h"
-#include "services/device/public/mojom/geoposition.mojom.h"
-#include "services/service_manager/public/cpp/bind_source_info.h"
-
-namespace device {
-
-// A helper class which owns a FakeGeolocationContext by which the geolocation
-// is overriden to a given position or latitude and longitude values.
-// The FakeGeolocationContext overrides the binder of Device Service by
-// service_manager::ServiceContext::SetGlobalBinderForTesting().
-// The override of the geolocation implementation will be in effect for the
-// duration of this object's lifetime.
-class ScopedGeolocationOverrider {
- public:
- explicit ScopedGeolocationOverrider(const mojom::Geoposition& position);
- ScopedGeolocationOverrider(double latitude, double longitude);
- ~ScopedGeolocationOverrider();
- void OverrideGeolocation(const mojom::Geoposition& position);
- void UpdateLocation(const mojom::Geoposition& position);
- void UpdateLocation(double latitude, double longitude);
-
- private:
- class FakeGeolocation;
- class FakeGeolocationContext;
- std::unique_ptr<FakeGeolocationContext> geolocation_context_;
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_PUBLIC_CPP_SCOPED_GEOLOCATION_OVERRIDER_H_
diff --git a/chromium/device/geolocation/wifi_data.cc b/chromium/device/geolocation/wifi_data.cc
deleted file mode 100644
index bd752d27f53..00000000000
--- a/chromium/device/geolocation/wifi_data.cc
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2013 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/geolocation/wifi_data.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <algorithm>
-#include <limits>
-
-#include "base/logging.h"
-
-namespace device {
-
-AccessPointData::AccessPointData()
- : radio_signal_strength(std::numeric_limits<int32_t>::min()),
- channel(std::numeric_limits<int32_t>::min()),
- signal_to_noise(std::numeric_limits<int32_t>::min()) {}
-
-AccessPointData::~AccessPointData() = default;
-
-WifiData::WifiData() = default;
-
-WifiData::WifiData(const WifiData& other) = default;
-
-WifiData::~WifiData() = default;
-
-bool WifiData::DiffersSignificantly(const WifiData& other) const {
- // More than 4 or 50% of access points added or removed is significant.
- static const size_t kMinChangedAccessPoints = 4;
- const size_t min_ap_count =
- std::min(access_point_data.size(), other.access_point_data.size());
- const size_t max_ap_count =
- std::max(access_point_data.size(), other.access_point_data.size());
- const size_t difference_threadhold =
- std::min(kMinChangedAccessPoints, min_ap_count / 2);
- if (max_ap_count > min_ap_count + difference_threadhold)
- return true;
- // Compute size of intersection of old and new sets.
- size_t num_common = 0;
- for (AccessPointDataSet::const_iterator iter = access_point_data.begin();
- iter != access_point_data.end(); iter++) {
- if (other.access_point_data.find(*iter) != other.access_point_data.end()) {
- ++num_common;
- }
- }
- DCHECK(num_common <= min_ap_count);
-
- // Test how many have changed.
- return max_ap_count > num_common + difference_threadhold;
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/wifi_data.h b/chromium/device/geolocation/wifi_data.h
deleted file mode 100644
index ee12a20e922..00000000000
--- a/chromium/device/geolocation/wifi_data.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2013 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_GEOLOCATION_WIFI_DATA_H_
-#define DEVICE_GEOLOCATION_WIFI_DATA_H_
-
-#include <set>
-
-#include "base/strings/string16.h"
-#include "device/geolocation/geolocation_export.h"
-
-namespace device {
-
-// Wifi data relating to a single access point.
-struct DEVICE_GEOLOCATION_EXPORT AccessPointData {
- AccessPointData();
- ~AccessPointData();
-
- // MAC address, formatted as per MacAddressAsString16.
- base::string16 mac_address;
- int radio_signal_strength; // Measured in dBm
- int channel;
- int signal_to_noise; // Ratio in dB
- base::string16 ssid; // Network identifier
-};
-
-// This is to allow AccessPointData to be used in std::set. We order
-// lexicographically by MAC address.
-struct AccessPointDataLess {
- bool operator()(const AccessPointData& data1,
- const AccessPointData& data2) const {
- return data1.mac_address < data2.mac_address;
- }
-};
-
-// All data for wifi.
-struct DEVICE_GEOLOCATION_EXPORT WifiData {
- WifiData();
- WifiData(const WifiData& other);
- ~WifiData();
-
- // Determines whether a new set of WiFi data differs significantly from this.
- bool DiffersSignificantly(const WifiData& other) const;
-
- // Store access points as a set, sorted by MAC address. This allows quick
- // comparison of sets for detecting changes and for caching.
- typedef std::set<AccessPointData, AccessPointDataLess> AccessPointDataSet;
- AccessPointDataSet access_point_data;
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_WIFI_DATA_H_
diff --git a/chromium/device/geolocation/wifi_data_provider.cc b/chromium/device/geolocation/wifi_data_provider.cc
deleted file mode 100644
index d2715afe0a6..00000000000
--- a/chromium/device/geolocation/wifi_data_provider.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2013 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/geolocation/wifi_data_provider.h"
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/location.h"
-#include "base/threading/thread_task_runner_handle.h"
-
-namespace device {
-
-WifiDataProvider::WifiDataProvider()
- : client_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
- DCHECK(client_task_runner_);
-}
-
-WifiDataProvider::~WifiDataProvider() = default;
-
-void WifiDataProvider::AddCallback(WifiDataUpdateCallback* callback) {
- callbacks_.insert(callback);
-}
-
-bool WifiDataProvider::RemoveCallback(WifiDataUpdateCallback* callback) {
- return callbacks_.erase(callback) == 1;
-}
-
-bool WifiDataProvider::has_callbacks() const {
- return !callbacks_.empty();
-}
-
-void WifiDataProvider::RunCallbacks() {
- client_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&WifiDataProvider::DoRunCallbacks, this));
-}
-
-bool WifiDataProvider::CalledOnClientThread() const {
- return client_task_runner()->BelongsToCurrentThread();
-}
-
-void WifiDataProvider::DoRunCallbacks() {
- // It's possible that all the callbacks went away whilst this task was
- // pending. This is fine; the loop will be a no-op.
- CallbackSet::const_iterator iter = callbacks_.begin();
- while (iter != callbacks_.end()) {
- WifiDataUpdateCallback* callback = *iter;
- ++iter; // Advance iter before running, in case callback unregisters.
- callback->Run();
- }
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/wifi_data_provider.h b/chromium/device/geolocation/wifi_data_provider.h
deleted file mode 100644
index 44ec7346b92..00000000000
--- a/chromium/device/geolocation/wifi_data_provider.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2013 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_GEOLOCATION_WIFI_DATA_PROVIDER_H_
-#define DEVICE_GEOLOCATION_WIFI_DATA_PROVIDER_H_
-
-#include <set>
-
-#include "base/callback_forward.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/single_thread_task_runner.h"
-#include "device/geolocation/geolocation_export.h"
-#include "device/geolocation/wifi_data.h"
-
-namespace device {
-
-class DEVICE_GEOLOCATION_EXPORT WifiDataProvider
- : public base::RefCountedThreadSafe<WifiDataProvider> {
- public:
- WifiDataProvider();
-
- // Tells the provider to start looking for data. Callbacks will start
- // receiving notifications after this call.
- virtual void StartDataProvider() = 0;
-
- // Tells the provider to stop looking for data. Callbacks will stop
- // receiving notifications after this call.
- virtual void StopDataProvider() = 0;
-
- // Provides whatever data the provider has, which may be nothing. Return
- // value indicates whether this is all the data the provider could ever
- // obtain.
- virtual bool GetData(WifiData* data) = 0;
-
- typedef base::Closure WifiDataUpdateCallback;
-
- void AddCallback(WifiDataUpdateCallback* callback);
-
- bool RemoveCallback(WifiDataUpdateCallback* callback);
-
- bool has_callbacks() const;
-
- protected:
- friend class base::RefCountedThreadSafe<WifiDataProvider>;
- virtual ~WifiDataProvider();
-
- typedef std::set<WifiDataUpdateCallback*> CallbackSet;
-
- // Runs all callbacks via a posted task, so we can unwind callstack here and
- // avoid client reentrancy.
- void RunCallbacks();
-
- bool CalledOnClientThread() const;
-
- scoped_refptr<base::SingleThreadTaskRunner> client_task_runner() const {
- return client_task_runner_;
- }
-
- private:
- void DoRunCallbacks();
-
- // The task runner for the client thread, all callbacks should run on it.
- scoped_refptr<base::SingleThreadTaskRunner> client_task_runner_;
-
- CallbackSet callbacks_;
-
- DISALLOW_COPY_AND_ASSIGN(WifiDataProvider);
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_WIFI_DATA_PROVIDER_H_
diff --git a/chromium/device/geolocation/wifi_data_provider_chromeos.cc b/chromium/device/geolocation/wifi_data_provider_chromeos.cc
deleted file mode 100644
index b4b4f1a9b53..00000000000
--- a/chromium/device/geolocation/wifi_data_provider_chromeos.cc
+++ /dev/null
@@ -1,184 +0,0 @@
-// Copyright (c) 2012 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.
-
-// Provides wifi scan API binding for chromeos, using proprietary APIs.
-
-#include "device/geolocation/wifi_data_provider_chromeos.h"
-
-#include <stdint.h>
-
-#include "base/bind.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chromeos/network/geolocation_handler.h"
-#include "chromeos/network/network_handler.h"
-#include "device/geolocation/wifi_data_provider_manager.h"
-
-using chromeos::NetworkHandler;
-
-namespace device {
-
-namespace {
-
-// The time periods between successive polls of the wifi data.
-const int kDefaultPollingIntervalMilliseconds = 10 * 1000; // 10s
-const int kNoChangePollingIntervalMilliseconds = 2 * 60 * 1000; // 2 mins
-const int kTwoNoChangePollingIntervalMilliseconds = 10 * 60 * 1000; // 10 mins
-const int kNoWifiPollingIntervalMilliseconds = 20 * 1000; // 20s
-
-} // namespace
-
-WifiDataProviderChromeOs::WifiDataProviderChromeOs()
- : started_(false), is_first_scan_complete_(false) {}
-
-WifiDataProviderChromeOs::~WifiDataProviderChromeOs() = default;
-
-void WifiDataProviderChromeOs::StartDataProvider() {
- DCHECK(CalledOnClientThread());
-
- DCHECK(polling_policy_ == nullptr);
- polling_policy_.reset(
- new GenericWifiPollingPolicy<kDefaultPollingIntervalMilliseconds,
- kNoChangePollingIntervalMilliseconds,
- kTwoNoChangePollingIntervalMilliseconds,
- kNoWifiPollingIntervalMilliseconds>);
-
- ScheduleStart();
-}
-
-void WifiDataProviderChromeOs::StopDataProvider() {
- DCHECK(CalledOnClientThread());
-
- polling_policy_.reset();
- ScheduleStop();
-}
-
-bool WifiDataProviderChromeOs::GetData(WifiData* data) {
- DCHECK(CalledOnClientThread());
- DCHECK(data);
- *data = wifi_data_;
- return is_first_scan_complete_;
-}
-
-void WifiDataProviderChromeOs::DoWifiScanTaskOnNetworkHandlerThread() {
- // This method could be scheduled after a ScheduleStop.
- if (!started_)
- return;
-
- WifiData new_data;
-
- if (GetAccessPointData(&new_data.access_point_data)) {
- client_task_runner()->PostTask(
- FROM_HERE,
- base::Bind(&WifiDataProviderChromeOs::DidWifiScanTask, this, new_data));
- } else {
- client_task_runner()->PostTask(
- FROM_HERE,
- base::Bind(&WifiDataProviderChromeOs::DidWifiScanTaskNoResults, this));
- }
-}
-
-void WifiDataProviderChromeOs::DidWifiScanTaskNoResults() {
- DCHECK(CalledOnClientThread());
- // Schedule next scan if started (StopDataProvider could have been called
- // in between DoWifiScanTaskOnNetworkHandlerThread and this method).
- if (started_)
- ScheduleNextScan(polling_policy_->NoWifiInterval());
-}
-
-void WifiDataProviderChromeOs::DidWifiScanTask(const WifiData& new_data) {
- DCHECK(CalledOnClientThread());
- bool update_available = wifi_data_.DiffersSignificantly(new_data);
- wifi_data_ = new_data;
- // Schedule next scan if started (StopDataProvider could have been called
- // in between DoWifiScanTaskOnNetworkHandlerThread and this method).
- if (started_) {
- polling_policy_->UpdatePollingInterval(update_available);
- ScheduleNextScan(polling_policy_->PollingInterval());
- }
-
- if (update_available || !is_first_scan_complete_) {
- is_first_scan_complete_ = true;
- RunCallbacks();
- }
-}
-
-void WifiDataProviderChromeOs::ScheduleNextScan(int interval) {
- DCHECK(CalledOnClientThread());
- DCHECK(started_);
- if (!NetworkHandler::IsInitialized()) {
- LOG(ERROR) << "ScheduleNextScan called with uninitialized NetworkHandler";
- return;
- }
- NetworkHandler::Get()->task_runner()->PostDelayedTask(
- FROM_HERE,
- base::Bind(
- &WifiDataProviderChromeOs::DoWifiScanTaskOnNetworkHandlerThread,
- this),
- base::TimeDelta::FromMilliseconds(interval));
-}
-
-void WifiDataProviderChromeOs::ScheduleStop() {
- DCHECK(CalledOnClientThread());
- DCHECK(started_);
- started_ = false;
-}
-
-void WifiDataProviderChromeOs::ScheduleStart() {
- DCHECK(CalledOnClientThread());
- DCHECK(!started_);
- if (!NetworkHandler::IsInitialized()) {
- LOG(ERROR) << "ScheduleStart called with uninitialized NetworkHandler";
- return;
- }
- started_ = true;
- // Perform first scan ASAP regardless of the polling policy. If this scan
- // fails we'll retry at a rate in line with the polling policy.
- NetworkHandler::Get()->task_runner()->PostTask(
- FROM_HERE,
- base::Bind(
- &WifiDataProviderChromeOs::DoWifiScanTaskOnNetworkHandlerThread,
- this));
-}
-
-bool WifiDataProviderChromeOs::GetAccessPointData(
- WifiData::AccessPointDataSet* result) {
- // If in startup or shutdown, NetworkHandler is uninitialized.
- if (!NetworkHandler::IsInitialized())
- return false; // Data not ready.
-
- DCHECK(NetworkHandler::Get()->task_runner()->BelongsToCurrentThread());
-
- // If wifi isn't enabled, we've effectively completed the task.
- chromeos::GeolocationHandler* const geolocation_handler =
- NetworkHandler::Get()->geolocation_handler();
- if (!geolocation_handler || !geolocation_handler->wifi_enabled())
- return true; // Access point list is empty, no more data.
-
- chromeos::WifiAccessPointVector access_points;
- int64_t age_ms = 0;
- if (!geolocation_handler->GetWifiAccessPoints(&access_points, &age_ms))
- return false;
-
- for (const auto& access_point : access_points) {
- AccessPointData ap_data;
- ap_data.mac_address = base::ASCIIToUTF16(access_point.mac_address);
- ap_data.radio_signal_strength = access_point.signal_strength;
- ap_data.channel = access_point.channel;
- ap_data.signal_to_noise = access_point.signal_to_noise;
- ap_data.ssid = base::UTF8ToUTF16(access_point.ssid);
- result->insert(ap_data);
- }
- // If the age is significantly longer than our long polling time, assume the
- // data is stale and return false which will trigger a faster update.
- if (age_ms > kTwoNoChangePollingIntervalMilliseconds * 2)
- return false;
- return true;
-}
-
-// static
-WifiDataProvider* WifiDataProviderManager::DefaultFactoryFunction() {
- return new WifiDataProviderChromeOs();
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/wifi_data_provider_chromeos.h b/chromium/device/geolocation/wifi_data_provider_chromeos.h
deleted file mode 100644
index f9630f6fc63..00000000000
--- a/chromium/device/geolocation/wifi_data_provider_chromeos.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) 2011 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_GEOLOCATION_WIFI_DATA_PROVIDER_CHROMEOS_H_
-#define DEVICE_GEOLOCATION_WIFI_DATA_PROVIDER_CHROMEOS_H_
-
-#include <memory>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "device/geolocation/wifi_data_provider.h"
-#include "device/geolocation/wifi_polling_policy.h"
-
-namespace device {
-
-class DEVICE_GEOLOCATION_EXPORT WifiDataProviderChromeOs
- : public WifiDataProvider {
- public:
- WifiDataProviderChromeOs();
-
- // WifiDataProvider
- void StartDataProvider() override;
- void StopDataProvider() override;
- bool GetData(WifiData* data) override;
-
- private:
- friend class GeolocationChromeOsWifiDataProviderTest;
- ~WifiDataProviderChromeOs() override;
-
- // NetworkHandler thread
- void DoWifiScanTaskOnNetworkHandlerThread();
-
- // Client thread
- void DidWifiScanTaskNoResults();
- void DidWifiScanTask(const WifiData& new_data);
-
- // Will schedule a scan; i.e. enqueue DoWifiScanTask deferred task.
- void ScheduleNextScan(int interval);
-
- // Will schedule starting of the scanning process.
- void ScheduleStart();
-
- // Will schedule stopping of the scanning process.
- void ScheduleStop();
-
- // Get access point data from chromeos.
- bool GetAccessPointData(WifiData::AccessPointDataSet* data);
-
- // Controls the polling update interval. (client thread)
- std::unique_ptr<WifiPollingPolicy> polling_policy_;
-
- // The latest wifi data. (client thread)
- WifiData wifi_data_;
-
- // Whether we have strated the data provider. (client thread)
- bool started_;
-
- // Whether we've successfully completed a scan for WiFi data. (client thread)
- bool is_first_scan_complete_;
-
- DISALLOW_COPY_AND_ASSIGN(WifiDataProviderChromeOs);
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_WIFI_DATA_PROVIDER_CHROMEOS_H_
diff --git a/chromium/device/geolocation/wifi_data_provider_chromeos_unittest.cc b/chromium/device/geolocation/wifi_data_provider_chromeos_unittest.cc
deleted file mode 100644
index e499ff8d591..00000000000
--- a/chromium/device/geolocation/wifi_data_provider_chromeos_unittest.cc
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (c) 2011 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/geolocation/wifi_data_provider_chromeos.h"
-#include "base/run_loop.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_task_environment.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/shill_manager_client.h"
-#include "chromeos/network/geolocation_handler.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/cros_system_api/dbus/service_constants.h"
-
-namespace device {
-
-class GeolocationChromeOsWifiDataProviderTest : public testing::Test {
- protected:
- GeolocationChromeOsWifiDataProviderTest()
- : scoped_task_environment_(
- base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
-
- void SetUp() override {
- chromeos::DBusThreadManager::Initialize();
- chromeos::NetworkHandler::Initialize();
- manager_client_ =
- chromeos::DBusThreadManager::Get()->GetShillManagerClient();
- manager_test_ = manager_client_->GetTestInterface();
- provider_ = new WifiDataProviderChromeOs();
- base::RunLoop().RunUntilIdle();
- }
-
- void TearDown() override {
- provider_ = NULL;
- chromeos::NetworkHandler::Shutdown();
- chromeos::DBusThreadManager::Shutdown();
- }
-
- bool GetAccessPointData() { return provider_->GetAccessPointData(&ap_data_); }
-
- void AddAccessPoints(int ssids, int aps_per_ssid) {
- for (int i = 0; i < ssids; ++i) {
- for (int j = 0; j < aps_per_ssid; ++j) {
- base::DictionaryValue properties;
- std::string mac_address = base::StringPrintf(
- "%02X:%02X:%02X:%02X:%02X:%02X", i, j, 3, 4, 5, 6);
- std::string channel = base::IntToString(i * 10 + j);
- std::string strength = base::IntToString(i * 100 + j);
- properties.SetKey(shill::kGeoMacAddressProperty,
- base::Value(mac_address));
- properties.SetKey(shill::kGeoChannelProperty, base::Value(channel));
- properties.SetKey(shill::kGeoSignalStrengthProperty,
- base::Value(strength));
- manager_test_->AddGeoNetwork(shill::kGeoWifiAccessPointsProperty,
- properties);
- }
- }
- base::RunLoop().RunUntilIdle();
- }
-
- base::test::ScopedTaskEnvironment scoped_task_environment_;
- scoped_refptr<WifiDataProviderChromeOs> provider_;
- chromeos::ShillManagerClient* manager_client_;
- chromeos::ShillManagerClient::TestInterface* manager_test_;
- WifiData::AccessPointDataSet ap_data_;
-};
-
-TEST_F(GeolocationChromeOsWifiDataProviderTest, NoAccessPoints) {
- base::RunLoop().RunUntilIdle();
- // Initial call to GetAccessPointData requests data and will return false.
- EXPECT_FALSE(GetAccessPointData());
- base::RunLoop().RunUntilIdle();
- // Additional call to GetAccessPointData also returns false with no devices.
- EXPECT_FALSE(GetAccessPointData());
- EXPECT_EQ(0u, ap_data_.size());
-}
-
-TEST_F(GeolocationChromeOsWifiDataProviderTest, GetOneAccessPoint) {
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(GetAccessPointData());
-
- AddAccessPoints(1, 1);
- EXPECT_TRUE(GetAccessPointData());
- ASSERT_EQ(1u, ap_data_.size());
- EXPECT_EQ("00:00:03:04:05:06",
- base::UTF16ToUTF8(ap_data_.begin()->mac_address));
-}
-
-TEST_F(GeolocationChromeOsWifiDataProviderTest, GetManyAccessPoints) {
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(GetAccessPointData());
-
- AddAccessPoints(3, 4);
- EXPECT_TRUE(GetAccessPointData());
- ASSERT_EQ(12u, ap_data_.size());
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/wifi_data_provider_common.cc b/chromium/device/geolocation/wifi_data_provider_common.cc
deleted file mode 100644
index 64102d56da3..00000000000
--- a/chromium/device/geolocation/wifi_data_provider_common.cc
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (c) 2012 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/geolocation/wifi_data_provider_common.h"
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/single_thread_task_runner.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-
-namespace device {
-
-base::string16 MacAddressAsString16(const uint8_t mac_as_int[6]) {
- // |mac_as_int| is big-endian. Write in byte chunks.
- // Format is XX-XX-XX-XX-XX-XX.
- static const char* const kMacFormatString = "%02x-%02x-%02x-%02x-%02x-%02x";
- return base::ASCIIToUTF16(base::StringPrintf(
- kMacFormatString, mac_as_int[0], mac_as_int[1], mac_as_int[2],
- mac_as_int[3], mac_as_int[4], mac_as_int[5]));
-}
-
-WifiDataProviderCommon::WifiDataProviderCommon()
- : is_first_scan_complete_(false), weak_factory_(this) {}
-
-WifiDataProviderCommon::~WifiDataProviderCommon() = default;
-
-void WifiDataProviderCommon::StartDataProvider() {
- DCHECK(!wlan_api_);
- wlan_api_ = CreateWlanApi();
- if (!wlan_api_) {
- // Error! Can't do scans, so don't try and schedule one.
- is_first_scan_complete_ = true;
- return;
- }
-
- if (!WifiPollingPolicy::IsInitialized())
- WifiPollingPolicy::Initialize(CreatePollingPolicy());
- DCHECK(WifiPollingPolicy::IsInitialized());
-
- ScheduleNextScan(WifiPollingPolicy::Get()->InitialInterval());
-}
-
-void WifiDataProviderCommon::StopDataProvider() {
- wlan_api_.reset();
-}
-
-bool WifiDataProviderCommon::GetData(WifiData* data) {
- *data = wifi_data_;
- // If we've successfully completed a scan, indicate that we have all of the
- // data we can get.
- return is_first_scan_complete_;
-}
-
-void WifiDataProviderCommon::DoWifiScanTask() {
- // Abort the wifi scan if the provider is already being torn down.
- if (!wlan_api_)
- return;
-
- bool update_available = false;
- WifiData new_data;
- if (!wlan_api_->GetAccessPointData(&new_data.access_point_data)) {
- ScheduleNextScan(WifiPollingPolicy::Get()->NoWifiInterval());
- } else {
- update_available = wifi_data_.DiffersSignificantly(new_data);
- wifi_data_ = new_data;
- WifiPollingPolicy::Get()->UpdatePollingInterval(update_available);
- ScheduleNextScan(WifiPollingPolicy::Get()->PollingInterval());
- }
- if (update_available || !is_first_scan_complete_) {
- is_first_scan_complete_ = true;
- RunCallbacks();
- }
-}
-
-void WifiDataProviderCommon::ScheduleNextScan(int interval) {
- client_task_runner()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&WifiDataProviderCommon::DoWifiScanTask,
- weak_factory_.GetWeakPtr()),
- base::TimeDelta::FromMilliseconds(interval));
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/wifi_data_provider_common.h b/chromium/device/geolocation/wifi_data_provider_common.h
deleted file mode 100644
index ad39a54e63a..00000000000
--- a/chromium/device/geolocation/wifi_data_provider_common.h
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (c) 2012 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_GEOLOCATION_WIFI_DATA_PROVIDER_COMMON_H_
-#define DEVICE_GEOLOCATION_WIFI_DATA_PROVIDER_COMMON_H_
-
-#include <assert.h>
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/strings/string16.h"
-#include "device/geolocation/geolocation_export.h"
-#include "device/geolocation/wifi_data_provider.h"
-#include "device/geolocation/wifi_polling_policy.h"
-
-namespace device {
-
-// Converts a MAC address stored as an array of uint8_t to a string.
-base::string16 MacAddressAsString16(const uint8_t mac_as_int[6]);
-
-// Base class to promote code sharing between platform specific wifi data
-// providers. It's optional for specific platforms to derive this, but if they
-// do polling behavior is taken care of by this base class, and all the platform
-// need do is provide the underlying WLAN access API and polling policy.
-// Also designed this way for ease of testing the cross-platform behavior.
-class DEVICE_GEOLOCATION_EXPORT WifiDataProviderCommon
- : public WifiDataProvider {
- public:
- // Interface to abstract the low level data OS library call, and to allow
- // mocking (hence public).
- class WlanApiInterface {
- public:
- virtual ~WlanApiInterface() {}
- // Gets wifi data for all visible access points.
- virtual bool GetAccessPointData(WifiData::AccessPointDataSet* data) = 0;
- };
-
- WifiDataProviderCommon();
-
- // WifiDataProvider implementation
- void StartDataProvider() override;
- void StopDataProvider() override;
- bool GetData(WifiData* data) override;
-
- protected:
- ~WifiDataProviderCommon() override;
-
- // TODO(mcasas): change return types and possibly names of these two methods,
- // see https://crbug.com/714348.
- // Returns ownership.
- virtual std::unique_ptr<WlanApiInterface> CreateWlanApi() = 0;
- // Returns ownership.
- virtual std::unique_ptr<WifiPollingPolicy> CreatePollingPolicy() = 0;
-
- private:
- // Runs a scan. Calls the callbacks if new data is found.
- void DoWifiScanTask();
-
- // Will schedule a scan; i.e. enqueue DoWifiScanTask deferred task.
- void ScheduleNextScan(int interval);
-
- WifiData wifi_data_;
-
- // Whether we've successfully completed a scan for WiFi data.
- bool is_first_scan_complete_;
-
- // Underlying OS wifi API.
- std::unique_ptr<WlanApiInterface> wlan_api_;
-
- // Holder for delayed tasks; takes care of cleanup.
- base::WeakPtrFactory<WifiDataProviderCommon> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(WifiDataProviderCommon);
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_WIFI_DATA_PROVIDER_COMMON_H_
diff --git a/chromium/device/geolocation/wifi_data_provider_common_unittest.cc b/chromium/device/geolocation/wifi_data_provider_common_unittest.cc
deleted file mode 100644
index 5f126ee3752..00000000000
--- a/chromium/device/geolocation/wifi_data_provider_common_unittest.cc
+++ /dev/null
@@ -1,213 +0,0 @@
-// Copyright (c) 2012 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/geolocation/wifi_data_provider_common.h"
-
-#include <memory>
-
-#include "base/bind_helpers.h"
-#include "base/macros.h"
-#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_task_environment.h"
-#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "device/geolocation/wifi_data_provider_manager.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::_;
-using testing::AnyNumber;
-using testing::AtLeast;
-using testing::DoAll;
-using testing::Invoke;
-using testing::InvokeWithoutArgs;
-using testing::Return;
-using testing::SetArgPointee;
-using testing::WithArgs;
-
-namespace device {
-
-class MockWlanApi : public WifiDataProviderCommon::WlanApiInterface {
- public:
- MockWlanApi() {
- ON_CALL(*this, GetAccessPointData(_))
- .WillByDefault(DoAll(SetArgPointee<0>(data_out_), Return(true)));
- }
-
- MOCK_METHOD1(GetAccessPointData, bool(WifiData::AccessPointDataSet* data));
-
- private:
- WifiData::AccessPointDataSet data_out_;
-};
-
-class MockPollingPolicy : public WifiPollingPolicy {
- public:
- MockPollingPolicy() {
- ON_CALL(*this, InitialInterval()).WillByDefault(Return(0));
- ON_CALL(*this, PollingInterval()).WillByDefault(Return(1));
- ON_CALL(*this, NoWifiInterval()).WillByDefault(Return(1));
- // We are not interested in calls to UpdatePollingInterval() method.
- EXPECT_CALL(*this, UpdatePollingInterval(_)).Times(AnyNumber());
- }
-
- // WifiPollingPolicy implementation.
- MOCK_METHOD1(UpdatePollingInterval, void(bool));
- MOCK_METHOD0(InitialInterval, int());
- MOCK_METHOD0(PollingInterval, int());
- MOCK_METHOD0(NoWifiInterval, int());
-};
-
-class WifiDataProviderCommonWithMock : public WifiDataProviderCommon {
- public:
- WifiDataProviderCommonWithMock() : wlan_api_(new MockWlanApi) {}
-
- // WifiDataProviderCommon
- std::unique_ptr<WlanApiInterface> CreateWlanApi() override {
- return std::move(wlan_api_);
- }
- std::unique_ptr<WifiPollingPolicy> CreatePollingPolicy() override {
- auto policy = std::make_unique<MockPollingPolicy>();
- // Save a pointer to the MockPollingPolicy.
- polling_policy_ = policy.get();
- return std::move(policy);
- }
-
- std::unique_ptr<MockWlanApi> wlan_api_;
- MockPollingPolicy* polling_policy_ = nullptr;
-
- private:
- ~WifiDataProviderCommonWithMock() override = default;
-
- DISALLOW_COPY_AND_ASSIGN(WifiDataProviderCommonWithMock);
-};
-
-// Main test fixture
-class GeolocationWifiDataProviderCommonTest : public testing::Test {
- public:
- GeolocationWifiDataProviderCommonTest()
- : scoped_task_environment_(
- base::test::ScopedTaskEnvironment::MainThreadType::UI),
- wifi_data_callback_(base::DoNothing()),
- provider_(new WifiDataProviderCommonWithMock),
- wlan_api_(provider_->wlan_api_.get()) {}
-
- void SetUp() override {
- // Initialize WifiPollingPolicy early so we can watch for calls to mocked
- // functions.
- WifiPollingPolicy::Initialize(provider_->CreatePollingPolicy());
- if (WifiPollingPolicy::IsInitialized())
- polling_policy_ = provider_->polling_policy_;
-
- provider_->AddCallback(&wifi_data_callback_);
- }
-
- void TearDown() override {
- provider_->RemoveCallback(&wifi_data_callback_);
- provider_->StopDataProvider();
- WifiPollingPolicy::Shutdown();
- }
-
- protected:
- const base::test::ScopedTaskEnvironment scoped_task_environment_;
- WifiDataProviderManager::WifiDataUpdateCallback wifi_data_callback_;
- const scoped_refptr<WifiDataProviderCommonWithMock> provider_;
-
- MockWlanApi* const wlan_api_;
- MockPollingPolicy* polling_policy_ = nullptr;
-};
-
-TEST_F(GeolocationWifiDataProviderCommonTest, CreateDestroy) {
- // Test fixture members were SetUp correctly.
- EXPECT_TRUE(provider_);
- EXPECT_TRUE(wlan_api_);
- EXPECT_TRUE(polling_policy_);
-}
-
-TEST_F(GeolocationWifiDataProviderCommonTest, NoWifi) {
- base::RunLoop run_loop;
- EXPECT_CALL(*polling_policy_, InitialInterval()).Times(1);
- EXPECT_CALL(*polling_policy_, NoWifiInterval()).Times(AtLeast(1));
- EXPECT_CALL(*wlan_api_, GetAccessPointData(_))
- .WillOnce(InvokeWithoutArgs([&run_loop]() {
- run_loop.Quit();
- return false;
- }));
-
- provider_->StartDataProvider();
- run_loop.Run();
-}
-
-TEST_F(GeolocationWifiDataProviderCommonTest, IntermittentWifi) {
- base::RunLoop run_loop;
- EXPECT_CALL(*polling_policy_, InitialInterval()).Times(1);
- EXPECT_CALL(*polling_policy_, PollingInterval()).Times(AtLeast(1));
- EXPECT_CALL(*polling_policy_, NoWifiInterval()).Times(1);
- EXPECT_CALL(*wlan_api_, GetAccessPointData(_))
- .WillOnce(Return(true))
- .WillOnce(InvokeWithoutArgs([&run_loop]() {
- run_loop.Quit();
- return false;
- }));
-
- provider_->StartDataProvider();
- run_loop.Run();
-}
-
-// This test runs StartDataProvider() and expects that GetAccessPointData() is
-// called. The retrieved WifiData is expected to be empty.
-TEST_F(GeolocationWifiDataProviderCommonTest, DoAnEmptyScan) {
- base::RunLoop run_loop;
-
- EXPECT_CALL(*polling_policy_, InitialInterval()).Times(1);
- EXPECT_CALL(*polling_policy_, PollingInterval()).Times(AtLeast(1));
- EXPECT_CALL(*wlan_api_, GetAccessPointData(_))
- .WillOnce(InvokeWithoutArgs([&run_loop]() {
- run_loop.Quit();
- return true;
- }));
-
- provider_->StartDataProvider();
- run_loop.Run();
-
- WifiData data;
- EXPECT_TRUE(provider_->GetData(&data));
- EXPECT_TRUE(data.access_point_data.empty());
-}
-
-// This test runs StartDataProvider() and expects that GetAccessPointData() is
-// called. Some mock WifiData is returned then and expected to be retrieved.
-TEST_F(GeolocationWifiDataProviderCommonTest, DoScanWithResults) {
- base::RunLoop run_loop;
-
- EXPECT_CALL(*polling_policy_, InitialInterval()).Times(1);
- EXPECT_CALL(*polling_policy_, PollingInterval()).Times(AtLeast(1));
- AccessPointData single_access_point;
- single_access_point.channel = 2;
- single_access_point.mac_address = 3;
- single_access_point.radio_signal_strength = 4;
- single_access_point.signal_to_noise = 5;
- single_access_point.ssid = base::ASCIIToUTF16("foossid");
-
- WifiData::AccessPointDataSet data_out({single_access_point});
-
- EXPECT_CALL(*wlan_api_, GetAccessPointData(_))
- .WillOnce(WithArgs<0>(
- Invoke([&data_out, &run_loop](WifiData::AccessPointDataSet* data) {
- *data = data_out;
- run_loop.Quit();
- return true;
- })));
-
- provider_->StartDataProvider();
- run_loop.Run();
-
- WifiData data;
- EXPECT_TRUE(provider_->GetData(&data));
- ASSERT_EQ(1u, data.access_point_data.size());
- EXPECT_EQ(single_access_point.ssid, data.access_point_data.begin()->ssid);
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/wifi_data_provider_common_win.cc b/chromium/device/geolocation/wifi_data_provider_common_win.cc
deleted file mode 100644
index 3e6bd971cdc..00000000000
--- a/chromium/device/geolocation/wifi_data_provider_common_win.cc
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (c) 2010 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/geolocation/wifi_data_provider_common_win.h"
-
-#include <assert.h>
-#include <stdint.h>
-
-#include "base/strings/utf_string_conversions.h"
-#include "device/geolocation/wifi_data_provider_common.h"
-
-namespace device {
-
-bool ConvertToAccessPointData(const NDIS_WLAN_BSSID& data,
- AccessPointData* access_point_data) {
- // Currently we get only MAC address, signal strength and SSID.
- // TODO(steveblock): Work out how to get age, channel and signal-to-noise.
- DCHECK(access_point_data);
- access_point_data->mac_address = MacAddressAsString16(data.MacAddress);
- access_point_data->radio_signal_strength = data.Rssi;
- // Note that _NDIS_802_11_SSID::Ssid::Ssid is not null-terminated.
- base::UTF8ToUTF16(reinterpret_cast<const char*>(data.Ssid.Ssid),
- data.Ssid.SsidLength, &access_point_data->ssid);
- return true;
-}
-
-int GetDataFromBssIdList(const NDIS_802_11_BSSID_LIST& bss_id_list,
- int list_size,
- WifiData::AccessPointDataSet* data) {
- // Walk through the BSS IDs.
- int found = 0;
- const uint8_t* iterator =
- reinterpret_cast<const uint8_t*>(&bss_id_list.Bssid[0]);
- const uint8_t* end_of_buffer =
- reinterpret_cast<const uint8_t*>(&bss_id_list) + list_size;
- for (int i = 0; i < static_cast<int>(bss_id_list.NumberOfItems); ++i) {
- const NDIS_WLAN_BSSID* bss_id =
- reinterpret_cast<const NDIS_WLAN_BSSID*>(iterator);
- // Check that the length of this BSS ID is reasonable.
- if (bss_id->Length < sizeof(NDIS_WLAN_BSSID) ||
- iterator + bss_id->Length > end_of_buffer) {
- break;
- }
- AccessPointData access_point_data;
- if (ConvertToAccessPointData(*bss_id, &access_point_data)) {
- data->insert(access_point_data);
- ++found;
- }
- // Move to the next BSS ID.
- iterator += bss_id->Length;
- }
- return found;
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/wifi_data_provider_common_win.h b/chromium/device/geolocation/wifi_data_provider_common_win.h
deleted file mode 100644
index 7306254b748..00000000000
--- a/chromium/device/geolocation/wifi_data_provider_common_win.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (c) 2012 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_GEOLOCATION_WIFI_DATA_PROVIDER_COMMON_WIN_H_
-#define DEVICE_GEOLOCATION_WIFI_DATA_PROVIDER_COMMON_WIN_H_
-
-#include <windows.h>
-#include <ntddndis.h>
-
-#include "device/geolocation/wifi_data_provider.h"
-
-namespace device {
-
-// Extracts access point data from the NDIS_802_11_BSSID_LIST structure and
-// appends it to the data vector. Returns the number of access points for which
-// data was extracted.
-int GetDataFromBssIdList(const NDIS_802_11_BSSID_LIST& bss_id_list,
- int list_size,
- WifiData::AccessPointDataSet* data);
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_WIFI_DATA_PROVIDER_COMMON_WIN_H_
diff --git a/chromium/device/geolocation/wifi_data_provider_linux.cc b/chromium/device/geolocation/wifi_data_provider_linux.cc
deleted file mode 100644
index 76a09a6e22a..00000000000
--- a/chromium/device/geolocation/wifi_data_provider_linux.cc
+++ /dev/null
@@ -1,373 +0,0 @@
-// Copyright (c) 2012 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.
-
-// Provides wifi scan API binding for suitable for typical linux distributions.
-// Currently, only the NetworkManager API is used, accessed via D-Bus (in turn
-// accessed via the GLib wrapper).
-
-#include "device/geolocation/wifi_data_provider_linux.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "dbus/bus.h"
-#include "dbus/message.h"
-#include "dbus/object_path.h"
-#include "dbus/object_proxy.h"
-#include "device/geolocation/wifi_data_provider_manager.h"
-
-namespace device {
-namespace {
-// The time periods between successive polls of the wifi data.
-const int kDefaultPollingIntervalMilliseconds = 10 * 1000; // 10s
-const int kNoChangePollingIntervalMilliseconds = 2 * 60 * 1000; // 2 mins
-const int kTwoNoChangePollingIntervalMilliseconds = 10 * 60 * 1000; // 10 mins
-const int kNoWifiPollingIntervalMilliseconds = 20 * 1000; // 20s
-
-const char kNetworkManagerServiceName[] = "org.freedesktop.NetworkManager";
-const char kNetworkManagerPath[] = "/org/freedesktop/NetworkManager";
-const char kNetworkManagerInterface[] = "org.freedesktop.NetworkManager";
-
-// From http://projects.gnome.org/NetworkManager/developers/spec.html
-enum { NM_DEVICE_TYPE_WIFI = 2 };
-
-// Wifi API binding to NetworkManager, to allow reuse of the polling behavior
-// defined in WifiDataProviderCommon.
-// TODO(joth): NetworkManager also allows for notification based handling,
-// however this will require reworking of the threading code to run a GLib
-// event loop (GMainLoop).
-class NetworkManagerWlanApi : public WifiDataProviderCommon::WlanApiInterface {
- public:
- NetworkManagerWlanApi();
- ~NetworkManagerWlanApi() override;
-
- // Must be called before any other interface method. Will return false if the
- // NetworkManager session cannot be created (e.g. not present on this distro),
- // in which case no other method may be called.
- bool Init();
-
- // Similar to Init() but can inject the bus object. Used for testing.
- bool InitWithBus(scoped_refptr<dbus::Bus> bus);
-
- // WifiDataProviderCommon::WlanApiInterface
- //
- // This function makes blocking D-Bus calls, but it's totally fine as
- // the code runs in "Geolocation" thread, not the browser's UI thread.
- bool GetAccessPointData(WifiData::AccessPointDataSet* data) override;
-
- private:
- // Enumerates the list of available network adapter devices known to
- // NetworkManager. Return true on success.
- bool GetAdapterDeviceList(std::vector<dbus::ObjectPath>* device_paths);
-
- // Given the NetworkManager path to a wireless adapater, dumps the wifi scan
- // results and appends them to |data|. Returns false if a fatal error is
- // encountered such that the data set could not be populated.
- bool GetAccessPointsForAdapter(const dbus::ObjectPath& adapter_path,
- WifiData::AccessPointDataSet* data);
-
- // Internal method used by GetAccessPointsForAdapter(), given a wifi access
- // point proxy retrieves the named property and returns it. Returns nullptr if
- // the property could not be read.
- std::unique_ptr<dbus::Response> GetAccessPointProperty(
- dbus::ObjectProxy* proxy,
- const std::string& property_name);
-
- scoped_refptr<dbus::Bus> system_bus_;
- dbus::ObjectProxy* network_manager_proxy_ = nullptr;
-
- DISALLOW_COPY_AND_ASSIGN(NetworkManagerWlanApi);
-};
-
-// Convert a wifi frequency to the corresponding channel. Adapted from
-// geolocation/wifilib.cc in googleclient (internal to google).
-int frquency_in_khz_to_channel(int frequency_khz) {
- if (frequency_khz >= 2412000 && frequency_khz <= 2472000) // Channels 1-13.
- return (frequency_khz - 2407000) / 5000;
- if (frequency_khz == 2484000)
- return 14;
- if (frequency_khz > 5000000 && frequency_khz < 6000000) // .11a bands.
- return (frequency_khz - 5000000) / 5000;
- // Ignore everything else.
- return AccessPointData().channel; // invalid channel
-}
-
-NetworkManagerWlanApi::NetworkManagerWlanApi() {}
-
-NetworkManagerWlanApi::~NetworkManagerWlanApi() {
- // Close the connection.
- system_bus_->ShutdownAndBlock();
-}
-
-bool NetworkManagerWlanApi::Init() {
- dbus::Bus::Options options;
- options.bus_type = dbus::Bus::SYSTEM;
- options.connection_type = dbus::Bus::PRIVATE;
- return InitWithBus(base::MakeRefCounted<dbus::Bus>(options));
-}
-
-bool NetworkManagerWlanApi::InitWithBus(scoped_refptr<dbus::Bus> bus) {
- system_bus_ = bus;
- // system_bus_ will own all object proxies created from the bus.
- network_manager_proxy_ = system_bus_->GetObjectProxy(
- kNetworkManagerServiceName, dbus::ObjectPath(kNetworkManagerPath));
- // Validate the proxy object by checking we can enumerate devices.
- std::vector<dbus::ObjectPath> adapter_paths;
- const bool success = GetAdapterDeviceList(&adapter_paths);
- VLOG(1) << "Init() result: " << success;
- return success;
-}
-
-bool NetworkManagerWlanApi::GetAccessPointData(
- WifiData::AccessPointDataSet* data) {
- std::vector<dbus::ObjectPath> device_paths;
- if (!GetAdapterDeviceList(&device_paths)) {
- LOG(WARNING) << "Could not enumerate access points";
- return false;
- }
- int success_count = 0;
- int fail_count = 0;
-
- // Iterate the devices, getting APs for each wireless adapter found
- for (const dbus::ObjectPath& device_path : device_paths) {
- VLOG(1) << "Checking device: " << device_path.value();
-
- dbus::ObjectProxy* device_proxy =
- system_bus_->GetObjectProxy(kNetworkManagerServiceName, device_path);
-
- dbus::MethodCall method_call(DBUS_INTERFACE_PROPERTIES, "Get");
- dbus::MessageWriter builder(&method_call);
- builder.AppendString("org.freedesktop.NetworkManager.Device");
- builder.AppendString("DeviceType");
- std::unique_ptr<dbus::Response> response(device_proxy->CallMethodAndBlock(
- &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
- if (!response) {
- LOG(WARNING) << "Failed to get the device type for "
- << device_path.value();
- continue; // Check the next device.
- }
- dbus::MessageReader reader(response.get());
- uint32_t device_type = 0;
- if (!reader.PopVariantOfUint32(&device_type)) {
- LOG(WARNING) << "Unexpected response for " << device_type << ": "
- << response->ToString();
- continue; // Check the next device.
- }
- VLOG(1) << "Device type: " << device_type;
-
- if (device_type == NM_DEVICE_TYPE_WIFI) { // Found a wlan adapter
- if (GetAccessPointsForAdapter(device_path, data))
- ++success_count;
- else
- ++fail_count;
- }
- }
- // At least one successful scan overrides any other adapter reporting error.
- return success_count || fail_count == 0;
-}
-
-bool NetworkManagerWlanApi::GetAdapterDeviceList(
- std::vector<dbus::ObjectPath>* device_paths) {
- dbus::MethodCall method_call(kNetworkManagerInterface, "GetDevices");
- std::unique_ptr<dbus::Response> response(
- network_manager_proxy_->CallMethodAndBlock(
- &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
- if (!response) {
- LOG(WARNING) << "Failed to get the device list";
- return false;
- }
-
- dbus::MessageReader reader(response.get());
- if (!reader.PopArrayOfObjectPaths(device_paths)) {
- LOG(WARNING) << "Unexpected response: " << response->ToString();
- return false;
- }
- return true;
-}
-
-bool NetworkManagerWlanApi::GetAccessPointsForAdapter(
- const dbus::ObjectPath& adapter_path,
- WifiData::AccessPointDataSet* data) {
- // Create a proxy object for this wifi adapter, and ask it to do a scan
- // (or at least, dump its scan results).
- dbus::ObjectProxy* device_proxy =
- system_bus_->GetObjectProxy(kNetworkManagerServiceName, adapter_path);
- dbus::MethodCall method_call("org.freedesktop.NetworkManager.Device.Wireless",
- "GetAccessPoints");
- std::unique_ptr<dbus::Response> response(device_proxy->CallMethodAndBlock(
- &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
- if (!response) {
- LOG(WARNING) << "Failed to get access points data for "
- << adapter_path.value();
- return false;
- }
- dbus::MessageReader reader(response.get());
- std::vector<dbus::ObjectPath> access_point_paths;
- if (!reader.PopArrayOfObjectPaths(&access_point_paths)) {
- LOG(WARNING) << "Unexpected response for " << adapter_path.value() << ": "
- << response->ToString();
- return false;
- }
-
- VLOG(1) << "Wireless adapter " << adapter_path.value() << " found "
- << access_point_paths.size() << " access points.";
-
- for (const dbus::ObjectPath& access_point_path : access_point_paths) {
- VLOG(1) << "Checking access point: " << access_point_path.value();
-
- dbus::ObjectProxy* access_point_proxy = system_bus_->GetObjectProxy(
- kNetworkManagerServiceName, access_point_path);
-
- AccessPointData access_point_data;
- {
- std::unique_ptr<dbus::Response> response(
- GetAccessPointProperty(access_point_proxy, "Ssid"));
- if (!response)
- continue;
- // The response should contain a variant that contains an array of bytes.
- dbus::MessageReader reader(response.get());
- dbus::MessageReader variant_reader(response.get());
- if (!reader.PopVariant(&variant_reader)) {
- LOG(WARNING) << "Unexpected response for " << access_point_path.value()
- << ": " << response->ToString();
- continue;
- }
- const uint8_t* ssid_bytes = nullptr;
- size_t ssid_length = 0;
- if (!variant_reader.PopArrayOfBytes(&ssid_bytes, &ssid_length)) {
- LOG(WARNING) << "Unexpected response for " << access_point_path.value()
- << ": " << response->ToString();
- continue;
- }
- std::string ssid(ssid_bytes, ssid_bytes + ssid_length);
- access_point_data.ssid = base::UTF8ToUTF16(ssid);
- }
-
- { // Read the mac address
- std::unique_ptr<dbus::Response> response(
- GetAccessPointProperty(access_point_proxy, "HwAddress"));
- if (!response)
- continue;
- dbus::MessageReader reader(response.get());
- std::string mac;
- if (!reader.PopVariantOfString(&mac)) {
- LOG(WARNING) << "Unexpected response for " << access_point_path.value()
- << ": " << response->ToString();
- continue;
- }
-
- base::ReplaceSubstringsAfterOffset(&mac, 0U, ":", base::StringPiece());
- std::vector<uint8_t> mac_bytes;
- if (!base::HexStringToBytes(mac, &mac_bytes) || mac_bytes.size() != 6) {
- LOG(WARNING) << "Can't parse mac address (found " << mac_bytes.size()
- << " bytes) so using raw string: " << mac;
- access_point_data.mac_address = base::UTF8ToUTF16(mac);
- } else {
- access_point_data.mac_address = MacAddressAsString16(&mac_bytes[0]);
- }
- }
-
- { // Read signal strength.
- std::unique_ptr<dbus::Response> response(
- GetAccessPointProperty(access_point_proxy, "Strength"));
- if (!response)
- continue;
- dbus::MessageReader reader(response.get());
- uint8_t strength = 0;
- if (!reader.PopVariantOfByte(&strength)) {
- LOG(WARNING) << "Unexpected response for " << access_point_path.value()
- << ": " << response->ToString();
- continue;
- }
- // Convert strength as a percentage into dBs.
- access_point_data.radio_signal_strength = -100 + strength / 2;
- }
-
- { // Read the channel
- std::unique_ptr<dbus::Response> response(
- GetAccessPointProperty(access_point_proxy, "Frequency"));
- if (!response)
- continue;
- dbus::MessageReader reader(response.get());
- uint32_t frequency = 0;
- if (!reader.PopVariantOfUint32(&frequency)) {
- LOG(WARNING) << "Unexpected response for " << access_point_path.value()
- << ": " << response->ToString();
- continue;
- }
-
- // NetworkManager returns frequency in MHz.
- access_point_data.channel = frquency_in_khz_to_channel(frequency * 1000);
- }
- VLOG(1) << "Access point data of " << access_point_path.value() << ": "
- << "SSID: " << access_point_data.ssid << ", "
- << "MAC: " << access_point_data.mac_address << ", "
- << "Strength: " << access_point_data.radio_signal_strength << ", "
- << "Channel: " << access_point_data.channel;
-
- data->insert(access_point_data);
- }
- return true;
-}
-
-std::unique_ptr<dbus::Response> NetworkManagerWlanApi::GetAccessPointProperty(
- dbus::ObjectProxy* access_point_proxy,
- const std::string& property_name) {
- dbus::MethodCall method_call(DBUS_INTERFACE_PROPERTIES, "Get");
- dbus::MessageWriter builder(&method_call);
- builder.AppendString("org.freedesktop.NetworkManager.AccessPoint");
- builder.AppendString(property_name);
- std::unique_ptr<dbus::Response> response =
- access_point_proxy->CallMethodAndBlock(
- &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
- if (!response) {
- LOG(WARNING) << "Failed to get property for " << property_name;
- }
- return response;
-}
-
-} // namespace
-
-// static
-WifiDataProvider* WifiDataProviderManager::DefaultFactoryFunction() {
- return new WifiDataProviderLinux();
-}
-
-WifiDataProviderLinux::WifiDataProviderLinux() {}
-
-WifiDataProviderLinux::~WifiDataProviderLinux() {}
-
-std::unique_ptr<WifiDataProviderCommon::WlanApiInterface>
-WifiDataProviderLinux::CreateWlanApi() {
- auto wlan_api = std::make_unique<NetworkManagerWlanApi>();
- if (wlan_api->Init())
- return std::move(wlan_api);
- return nullptr;
-}
-
-std::unique_ptr<WifiPollingPolicy>
-WifiDataProviderLinux::CreatePollingPolicy() {
- return std::make_unique<GenericWifiPollingPolicy<
- kDefaultPollingIntervalMilliseconds, kNoChangePollingIntervalMilliseconds,
- kTwoNoChangePollingIntervalMilliseconds,
- kNoWifiPollingIntervalMilliseconds>>();
-}
-
-std::unique_ptr<WifiDataProviderCommon::WlanApiInterface>
-WifiDataProviderLinux::CreateWlanApiForTesting(scoped_refptr<dbus::Bus> bus) {
- auto wlan_api = std::make_unique<NetworkManagerWlanApi>();
- if (wlan_api->InitWithBus(bus))
- return std::move(wlan_api);
- return nullptr;
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/wifi_data_provider_linux.h b/chromium/device/geolocation/wifi_data_provider_linux.h
deleted file mode 100644
index 94a8d0efb13..00000000000
--- a/chromium/device/geolocation/wifi_data_provider_linux.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2012 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_GEOLOCATION_WIFI_DATA_PROVIDER_LINUX_H_
-#define DEVICE_GEOLOCATION_WIFI_DATA_PROVIDER_LINUX_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "device/geolocation/geolocation_export.h"
-#include "device/geolocation/wifi_data_provider_common.h"
-
-namespace dbus {
-class Bus;
-};
-
-namespace device {
-
-class DEVICE_GEOLOCATION_EXPORT WifiDataProviderLinux
- : public WifiDataProviderCommon {
- public:
- WifiDataProviderLinux();
-
- private:
- friend class GeolocationWifiDataProviderLinuxTest;
-
- ~WifiDataProviderLinux() override;
-
- // WifiDataProviderCommon implementation
- std::unique_ptr<WlanApiInterface> CreateWlanApi() override;
- std::unique_ptr<WifiPollingPolicy> CreatePollingPolicy() override;
-
- std::unique_ptr<WlanApiInterface> CreateWlanApiForTesting(
- scoped_refptr<dbus::Bus> bus);
-
- DISALLOW_COPY_AND_ASSIGN(WifiDataProviderLinux);
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_WIFI_DATA_PROVIDER_LINUX_H_
diff --git a/chromium/device/geolocation/wifi_data_provider_linux_unittest.cc b/chromium/device/geolocation/wifi_data_provider_linux_unittest.cc
deleted file mode 100644
index b4fb94b4841..00000000000
--- a/chromium/device/geolocation/wifi_data_provider_linux_unittest.cc
+++ /dev/null
@@ -1,233 +0,0 @@
-// Copyright (c) 2012 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/geolocation/wifi_data_provider_linux.h"
-
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_task_environment.h"
-#include "dbus/message.h"
-#include "dbus/mock_bus.h"
-#include "dbus/mock_object_proxy.h"
-#include "dbus/object_path.h"
-#include "dbus/object_proxy.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ::testing::_;
-using ::testing::Invoke;
-using ::testing::Return;
-using ::testing::Unused;
-
-namespace device {
-
-class GeolocationWifiDataProviderLinuxTest : public testing::Test {
- void SetUp() override {
- // Create a mock bus.
- dbus::Bus::Options options;
- options.bus_type = dbus::Bus::SYSTEM;
- mock_bus_ = base::MakeRefCounted<dbus::MockBus>(options);
-
- // Create a mock proxy that behaves as NetworkManager.
- mock_network_manager_proxy_ = base::MakeRefCounted<dbus::MockObjectProxy>(
- mock_bus_.get(), "org.freedesktop.NetworkManager",
- dbus::ObjectPath("/org/freedesktop/NetworkManager"));
- // Set an expectation so mock_network_manager_proxy_'s
- // CallMethodAndBlock() will use CreateNetworkManagerProxyResponse()
- // to return responses.
- EXPECT_CALL(*mock_network_manager_proxy_.get(),
- CallMethodAndBlock(_, _))
- .WillRepeatedly(Invoke(this, &GeolocationWifiDataProviderLinuxTest::
- CreateNetworkManagerProxyResponse));
-
- // Create a mock proxy that behaves as NetworkManager/Devices/0.
- mock_device_proxy_ = base::MakeRefCounted<dbus::MockObjectProxy>(
- mock_bus_.get(), "org.freedesktop.NetworkManager",
- dbus::ObjectPath("/org/freedesktop/NetworkManager/Devices/0"));
- EXPECT_CALL(*mock_device_proxy_.get(), CallMethodAndBlock(_, _))
- .WillRepeatedly(Invoke(
- this,
- &GeolocationWifiDataProviderLinuxTest::CreateDeviceProxyResponse));
-
- // Create a mock proxy that behaves as NetworkManager/AccessPoint/0.
- mock_access_point_proxy_ = base::MakeRefCounted<dbus::MockObjectProxy>(
- mock_bus_.get(), "org.freedesktop.NetworkManager",
- dbus::ObjectPath("/org/freedesktop/NetworkManager/AccessPoint/0"));
- EXPECT_CALL(*mock_access_point_proxy_.get(), CallMethodAndBlock(_, _))
- .WillRepeatedly(Invoke(this, &GeolocationWifiDataProviderLinuxTest::
- CreateAccessPointProxyResponse));
-
- // Set an expectation so mock_bus_'s GetObjectProxy() for the given
- // service name and the object path will return
- // mock_network_manager_proxy_.
- EXPECT_CALL(
- *mock_bus_.get(),
- GetObjectProxy("org.freedesktop.NetworkManager",
- dbus::ObjectPath("/org/freedesktop/NetworkManager")))
- .WillOnce(Return(mock_network_manager_proxy_.get()));
- // Likewise, set an expectation for mock_device_proxy_.
- EXPECT_CALL(
- *mock_bus_.get(),
- GetObjectProxy(
- "org.freedesktop.NetworkManager",
- dbus::ObjectPath("/org/freedesktop/NetworkManager/Devices/0")))
- .WillOnce(Return(mock_device_proxy_.get()))
- .WillOnce(Return(mock_device_proxy_.get()));
- // Likewise, set an expectation for mock_access_point_proxy_.
- EXPECT_CALL(
- *mock_bus_.get(),
- GetObjectProxy(
- "org.freedesktop.NetworkManager",
- dbus::ObjectPath("/org/freedesktop/NetworkManager/AccessPoint/0")))
- .WillOnce(Return(mock_access_point_proxy_.get()));
-
- // ShutdownAndBlock() should be called.
- EXPECT_CALL(*mock_bus_.get(), ShutdownAndBlock()).WillOnce(Return());
-
- // Create the wlan API with the mock bus object injected.
- wifi_provider_linux_ = base::MakeRefCounted<WifiDataProviderLinux>();
- wlan_api_ = wifi_provider_linux_->CreateWlanApiForTesting(mock_bus_);
- ASSERT_TRUE(wlan_api_);
- }
-
- protected:
- GeolocationWifiDataProviderLinuxTest()
- : scoped_task_environment_(
- base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
-
- // WifiDataProvider requires a task runner to be present. The |message_loop_|
- // is defined here, as it should outlive |wifi_provider_linux_|.
- base::test::ScopedTaskEnvironment scoped_task_environment_;
- scoped_refptr<dbus::MockBus> mock_bus_;
- scoped_refptr<dbus::MockObjectProxy> mock_network_manager_proxy_;
- scoped_refptr<dbus::MockObjectProxy> mock_access_point_proxy_;
- scoped_refptr<dbus::MockObjectProxy> mock_device_proxy_;
- scoped_refptr<WifiDataProviderLinux> wifi_provider_linux_;
- std::unique_ptr<WifiDataProviderCommon::WlanApiInterface> wlan_api_;
-
- private:
- // Creates a response for |mock_network_manager_proxy_|.
- std::unique_ptr<dbus::Response> CreateNetworkManagerProxyResponse(
- dbus::MethodCall* method_call,
- Unused) {
- if (method_call->GetInterface() == "org.freedesktop.NetworkManager" &&
- method_call->GetMember() == "GetDevices") {
- // The list of devices is asked. Return the object path.
- std::vector<dbus::ObjectPath> object_paths;
- object_paths.push_back(
- dbus::ObjectPath("/org/freedesktop/NetworkManager/Devices/0"));
-
- std::unique_ptr<dbus::Response> response = dbus::Response::CreateEmpty();
- dbus::MessageWriter writer(response.get());
- writer.AppendArrayOfObjectPaths(object_paths);
- return response;
- }
-
- LOG(ERROR) << "Unexpected method call: " << method_call->ToString();
- return nullptr;
- }
-
- // Creates a response for |mock_device_proxy_|.
- std::unique_ptr<dbus::Response> CreateDeviceProxyResponse(
- dbus::MethodCall* method_call,
- Unused) {
- if (method_call->GetInterface() == DBUS_INTERFACE_PROPERTIES &&
- method_call->GetMember() == "Get") {
- dbus::MessageReader reader(method_call);
- std::string interface_name;
- std::string property_name;
- if (reader.PopString(&interface_name) &&
- reader.PopString(&property_name)) {
- // The device type is asked. Respond that the device type is wifi.
- std::unique_ptr<dbus::Response> response =
- dbus::Response::CreateEmpty();
- dbus::MessageWriter writer(response.get());
- // This matches NM_DEVICE_TYPE_WIFI in wifi_data_provider_linux.cc.
- const int kDeviceTypeWifi = 2;
- writer.AppendVariantOfUint32(kDeviceTypeWifi);
- return response;
- }
- } else if (method_call->GetInterface() ==
- "org.freedesktop.NetworkManager.Device.Wireless" &&
- method_call->GetMember() == "GetAccessPoints") {
- // The list of access points is asked. Return the object path.
- std::unique_ptr<dbus::Response> response = dbus::Response::CreateEmpty();
- dbus::MessageWriter writer(response.get());
- std::vector<dbus::ObjectPath> object_paths;
- object_paths.push_back(
- dbus::ObjectPath("/org/freedesktop/NetworkManager/AccessPoint/0"));
- writer.AppendArrayOfObjectPaths(object_paths);
- return response;
- }
-
- LOG(ERROR) << "Unexpected method call: " << method_call->ToString();
- return nullptr;
- }
-
- // Creates a response for |mock_access_point_proxy_|.
- std::unique_ptr<dbus::Response> CreateAccessPointProxyResponse(
- dbus::MethodCall* method_call,
- Unused) {
- if (method_call->GetInterface() == DBUS_INTERFACE_PROPERTIES &&
- method_call->GetMember() == "Get") {
- dbus::MessageReader reader(method_call);
-
- std::string interface_name;
- std::string property_name;
- if (reader.PopString(&interface_name) &&
- reader.PopString(&property_name)) {
- std::unique_ptr<dbus::Response> response =
- dbus::Response::CreateEmpty();
- dbus::MessageWriter writer(response.get());
-
- if (property_name == "Ssid") {
- const uint8_t kSsid[] = {0x74, 0x65, 0x73, 0x74}; // "test"
- dbus::MessageWriter variant_writer(response.get());
- writer.OpenVariant("ay", &variant_writer);
- variant_writer.AppendArrayOfBytes(kSsid, arraysize(kSsid));
- writer.CloseContainer(&variant_writer);
- } else if (property_name == "HwAddress") {
- // This will be converted to "00-11-22-33-44-55".
- const std::string kMacAddress = "00:11:22:33:44:55";
- writer.AppendVariantOfString(kMacAddress);
- } else if (property_name == "Strength") {
- // This will be converted to -50.
- const uint8_t kStrength = 100;
- writer.AppendVariantOfByte(kStrength);
- } else if (property_name == "Frequency") {
- // This will be converted to channel 4.
- const uint32_t kFrequency = 2427;
- writer.AppendVariantOfUint32(kFrequency);
- }
- return response;
- }
- }
-
- LOG(ERROR) << "Unexpected method call: " << method_call->ToString();
- return nullptr;
- }
-};
-
-TEST_F(GeolocationWifiDataProviderLinuxTest, GetAccessPointData) {
- WifiData::AccessPointDataSet access_point_data_set;
- ASSERT_TRUE(wlan_api_->GetAccessPointData(&access_point_data_set));
-
- ASSERT_EQ(1U, access_point_data_set.size());
- const AccessPointData& access_point_data = *access_point_data_set.begin();
-
- // Check the contents of the access point data.
- // The expected values come from CreateAccessPointProxyResponse() above.
- EXPECT_EQ("test", base::UTF16ToUTF8(access_point_data.ssid));
- EXPECT_EQ("00-11-22-33-44-55",
- base::UTF16ToUTF8(access_point_data.mac_address));
- EXPECT_EQ(-50, access_point_data.radio_signal_strength);
- EXPECT_EQ(4, access_point_data.channel);
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/wifi_data_provider_mac.h b/chromium/device/geolocation/wifi_data_provider_mac.h
deleted file mode 100644
index 0c38c3a2c91..00000000000
--- a/chromium/device/geolocation/wifi_data_provider_mac.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2012 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_GEOLOCATION_WIFI_DATA_PROVIDER_MAC_H_
-#define DEVICE_GEOLOCATION_WIFI_DATA_PROVIDER_MAC_H_
-
-#include "base/macros.h"
-#include "device/geolocation/wifi_data_provider_common.h"
-
-namespace device {
-
-// Implementation of the wifi data provider for Mac OSX. Uses different API
-// bindings depending on APIs detected available at runtime in order to access
-// wifi scan data: Apple80211.h on OSX 10.5, CoreWLAN framework on OSX 10.6.
-class WifiDataProviderMac : public WifiDataProviderCommon {
- public:
- WifiDataProviderMac();
-
- private:
- ~WifiDataProviderMac() override;
-
- // WifiDataProviderCommon implementation
- std::unique_ptr<WlanApiInterface> CreateWlanApi() override;
- std::unique_ptr<WifiPollingPolicy> CreatePollingPolicy() override;
-
- DISALLOW_COPY_AND_ASSIGN(WifiDataProviderMac);
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_WIFI_DATA_PROVIDER_MAC_H_
diff --git a/chromium/device/geolocation/wifi_data_provider_mac.mm b/chromium/device/geolocation/wifi_data_provider_mac.mm
deleted file mode 100644
index dd5492dd72f..00000000000
--- a/chromium/device/geolocation/wifi_data_provider_mac.mm
+++ /dev/null
@@ -1,137 +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/geolocation/wifi_data_provider_mac.h"
-
-#import <CoreWLAN/CoreWLAN.h>
-#import <Foundation/Foundation.h>
-
-#include "base/mac/scoped_nsautorelease_pool.h"
-#include "base/mac/scoped_nsobject.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/sys_string_conversions.h"
-#include "device/geolocation/wifi_data_provider_common.h"
-#include "device/geolocation/wifi_data_provider_manager.h"
-
-extern "C" NSString* const kCWScanKeyMerge;
-
-@interface CWInterface (Private)
-- (NSArray*)scanForNetworksWithParameters:(NSDictionary*)params
- error:(NSError**)error;
-@end
-
-namespace device {
-
-namespace {
-
-class CoreWlanApi : public WifiDataProviderCommon::WlanApiInterface {
- public:
- CoreWlanApi() {}
-
- // WlanApiInterface:
- bool GetAccessPointData(WifiData::AccessPointDataSet* data) override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CoreWlanApi);
-};
-
-bool CoreWlanApi::GetAccessPointData(WifiData::AccessPointDataSet* data) {
- base::mac::ScopedNSAutoreleasePool auto_pool;
- // Initialize the scan parameters with scan key merging disabled, so we get
- // every AP listed in the scan without any SSID de-duping logic.
- NSDictionary* params = @{ kCWScanKeyMerge : @NO };
-
- NSSet* supported_interfaces = [CWInterface interfaceNames];
- NSUInteger interface_error_count = 0;
- for (NSString* interface_name in supported_interfaces) {
- CWInterface* corewlan_interface =
- [CWInterface interfaceWithName:interface_name];
- if (!corewlan_interface) {
- DLOG(WARNING) << interface_name << ": initWithName failed";
- ++interface_error_count;
- continue;
- }
-
- const base::TimeTicks start_time = base::TimeTicks::Now();
-
- NSError* err = nil;
- NSArray* scan =
- [corewlan_interface scanForNetworksWithParameters:params error:&err];
- const int error_code = [err code];
- const int count = [scan count];
- // We could get an error code but count != 0 if the scan was interrupted,
- // for example. For our purposes this is not fatal, so process as normal.
- if (error_code && count == 0) {
- DLOG(WARNING) << interface_name << ": CoreWLAN scan failed with error "
- << error_code;
- ++interface_error_count;
- continue;
- }
-
- const base::TimeDelta duration = base::TimeTicks::Now() - start_time;
-
- UMA_HISTOGRAM_CUSTOM_TIMES("Net.Wifi.ScanLatency", duration,
- base::TimeDelta::FromMilliseconds(1),
- base::TimeDelta::FromMinutes(1), 100);
-
- DVLOG(1) << interface_name << ": found " << count << " wifi APs";
-
- for (CWNetwork* network in scan) {
- DCHECK(network);
- AccessPointData access_point_data;
- // -[CWNetwork bssid] uses colons to separate the components of the MAC
- // address, but AccessPointData requires they be separated with a dash.
- access_point_data.mac_address = base::SysNSStringToUTF16([[network bssid]
- stringByReplacingOccurrencesOfString:@":"
- withString:@"-"]);
- access_point_data.radio_signal_strength = [network rssiValue];
- access_point_data.channel = [[network wlanChannel] channelNumber];
- access_point_data.signal_to_noise =
- access_point_data.radio_signal_strength - [network noiseMeasurement];
- access_point_data.ssid = base::SysNSStringToUTF16([network ssid]);
- data->insert(access_point_data);
- }
- }
-
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "Net.Wifi.InterfaceCount",
- [supported_interfaces count] - interface_error_count, 1, 5, 6);
-
- // Return true even if some interfaces failed to scan, so long as at least
- // one interface did not fail.
- return interface_error_count == 0 ||
- [supported_interfaces count] > interface_error_count;
-};
-
-// The time periods, in milliseconds, between successive polls of the wifi data.
-const int kDefaultPollingInterval = 120000; // 2 mins
-const int kNoChangePollingInterval = 300000; // 5 mins
-const int kTwoNoChangePollingInterval = 600000; // 10 mins
-const int kNoWifiPollingIntervalMilliseconds = 20 * 1000; // 20s
-
-} // namespace
-
-// static
-WifiDataProvider* WifiDataProviderManager::DefaultFactoryFunction() {
- return new WifiDataProviderMac();
-}
-
-WifiDataProviderMac::WifiDataProviderMac() {}
-
-WifiDataProviderMac::~WifiDataProviderMac() {}
-
-std::unique_ptr<WifiDataProviderMac::WlanApiInterface>
-WifiDataProviderMac::CreateWlanApi() {
- return std::make_unique<CoreWlanApi>();
-}
-
-std::unique_ptr<WifiPollingPolicy> WifiDataProviderMac::CreatePollingPolicy() {
- return std::make_unique<GenericWifiPollingPolicy<
- kDefaultPollingInterval, kNoChangePollingInterval,
- kTwoNoChangePollingInterval, kNoWifiPollingIntervalMilliseconds>>();
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/wifi_data_provider_manager.cc b/chromium/device/geolocation/wifi_data_provider_manager.cc
deleted file mode 100644
index af08233e9c6..00000000000
--- a/chromium/device/geolocation/wifi_data_provider_manager.cc
+++ /dev/null
@@ -1,98 +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/geolocation/wifi_data_provider_manager.h"
-
-#include "device/geolocation/wifi_data_provider.h"
-
-namespace device {
-
-// static
-WifiDataProviderManager* WifiDataProviderManager::instance_ = NULL;
-
-// static
-WifiDataProviderManager::ImplFactoryFunction
- WifiDataProviderManager::factory_function_ = DefaultFactoryFunction;
-
-// static
-void WifiDataProviderManager::SetFactoryForTesting(
- ImplFactoryFunction factory_function_in) {
- factory_function_ = factory_function_in;
-}
-
-// static
-void WifiDataProviderManager::ResetFactoryForTesting() {
- factory_function_ = DefaultFactoryFunction;
-}
-
-// static
-WifiDataProviderManager* WifiDataProviderManager::Register(
- WifiDataUpdateCallback* callback) {
- bool need_to_start_data_provider = false;
- if (!instance_) {
- instance_ = new WifiDataProviderManager();
- need_to_start_data_provider = true;
- }
- DCHECK(instance_);
- instance_->AddCallback(callback);
- // Start the provider after adding the callback, to avoid any race in
- // it running early.
- if (need_to_start_data_provider)
- instance_->StartDataProvider();
- return instance_;
-}
-
-// static
-bool WifiDataProviderManager::Unregister(WifiDataUpdateCallback* callback) {
- DCHECK(instance_);
- DCHECK(instance_->has_callbacks());
- if (!instance_->RemoveCallback(callback)) {
- return false;
- }
- if (!instance_->has_callbacks()) {
- // Must stop the data provider (and any implementation threads) before
- // destroying to avoid any race conditions in access to the provider in
- // the destructor chain.
- instance_->StopDataProvider();
- delete instance_;
- instance_ = NULL;
- }
- return true;
-}
-
-WifiDataProviderManager::WifiDataProviderManager() {
- DCHECK(factory_function_);
- impl_ = (*factory_function_)();
- DCHECK(impl_.get());
-}
-
-WifiDataProviderManager::~WifiDataProviderManager() {
- DCHECK(impl_.get());
-}
-
-bool WifiDataProviderManager::GetData(WifiData* data) {
- return impl_->GetData(data);
-}
-
-void WifiDataProviderManager::AddCallback(WifiDataUpdateCallback* callback) {
- impl_->AddCallback(callback);
-}
-
-bool WifiDataProviderManager::RemoveCallback(WifiDataUpdateCallback* callback) {
- return impl_->RemoveCallback(callback);
-}
-
-bool WifiDataProviderManager::has_callbacks() const {
- return impl_->has_callbacks();
-}
-
-void WifiDataProviderManager::StartDataProvider() {
- impl_->StartDataProvider();
-}
-
-void WifiDataProviderManager::StopDataProvider() {
- impl_->StopDataProvider();
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/wifi_data_provider_manager.h b/chromium/device/geolocation/wifi_data_provider_manager.h
deleted file mode 100644
index d2b633749cf..00000000000
--- a/chromium/device/geolocation/wifi_data_provider_manager.h
+++ /dev/null
@@ -1,97 +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.
-
-// A wifi data provider provides wifi data from the device that is used by a
-// NetworkLocationProvider to obtain a position fix. We use a singleton
-// instance of the wifi data provider manager, which is used by multiple
-// NetworkLocationProvider objects.
-//
-// This file provides WifiDataProviderManager, which provides static methods to
-// access the singleton instance. The singleton instance uses a private
-// implementation of WifiDataProvider to abstract across platforms and also to
-// allow mock providers to be used for testing.
-
-#ifndef DEVICE_GEOLOCATION_WIFI_DATA_PROVIDER_MANAGER_H_
-#define DEVICE_GEOLOCATION_WIFI_DATA_PROVIDER_MANAGER_H_
-
-#include <set>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/strings/string16.h"
-#include "base/strings/string_util.h"
-#include "device/geolocation/geolocation_export.h"
-#include "device/geolocation/wifi_data.h"
-
-namespace device {
-
-class WifiDataProvider;
-
-// A manager for wifi data providers.
-//
-// We use a singleton instance of this class which is shared by multiple network
-// location providers. These location providers access the instance through the
-// Register and Unregister methods.
-class DEVICE_GEOLOCATION_EXPORT WifiDataProviderManager {
- public:
- typedef WifiDataProvider* (*ImplFactoryFunction)(void);
-
- // Sets the factory function which will be used by Register to create the
- // implementation used by the singleton instance. This factory approach is
- // used both to abstract accross platform-specific implementations and to
- // inject mock implementations for testing.
- static void SetFactoryForTesting(ImplFactoryFunction factory_function_in);
-
- // Resets the factory function to the default.
- static void ResetFactoryForTesting();
-
- typedef base::Closure WifiDataUpdateCallback;
-
- // Registers a callback, which will be run whenever new data is available.
- // Instantiates the singleton if necessary, and always returns it.
- static WifiDataProviderManager* Register(WifiDataUpdateCallback* callback);
-
- // Removes a callback. If this is the last callback, deletes the singleton
- // instance. Return value indicates success.
- static bool Unregister(WifiDataUpdateCallback* callback);
-
- // Provides whatever data the provider has, which may be nothing. Return
- // value indicates whether this is all the data the provider could ever
- // obtain.
- bool GetData(WifiData* data);
-
- private:
- // Private constructor and destructor, callers access singleton through
- // Register and Unregister.
- WifiDataProviderManager();
- ~WifiDataProviderManager();
-
- void AddCallback(WifiDataUpdateCallback* callback);
- bool RemoveCallback(WifiDataUpdateCallback* callback);
- bool has_callbacks() const;
-
- void StartDataProvider();
- void StopDataProvider();
-
- static WifiDataProvider* DefaultFactoryFunction();
-
- // The singleton-like instance of this class. (Not 'true' singleton, as it
- // may go through multiple create/destroy/create cycles per process instance,
- // e.g. when under test).
- static WifiDataProviderManager* instance_;
-
- // The factory function used to create the singleton instance.
- static ImplFactoryFunction factory_function_;
-
- // The internal implementation.
- scoped_refptr<WifiDataProvider> impl_;
-
- DISALLOW_COPY_AND_ASSIGN(WifiDataProviderManager);
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_WIFI_DATA_PROVIDER_MANAGER_H_
diff --git a/chromium/device/geolocation/wifi_data_provider_win.cc b/chromium/device/geolocation/wifi_data_provider_win.cc
deleted file mode 100644
index c3c2821b7c5..00000000000
--- a/chromium/device/geolocation/wifi_data_provider_win.cc
+++ /dev/null
@@ -1,256 +0,0 @@
-// Copyright (c) 2010 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/geolocation/wifi_data_provider_win.h"
-
-#include <windows.h>
-#include <winioctl.h>
-#include <wlanapi.h>
-
-#include "base/memory/free_deleter.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/timer/elapsed_timer.h"
-#include "base/win/windows_version.h"
-#include "device/geolocation/wifi_data_provider_common.h"
-#include "device/geolocation/wifi_data_provider_common_win.h"
-#include "device/geolocation/wifi_data_provider_manager.h"
-
-namespace device {
-
-namespace {
-
-static const int kDefaultPollingIntervalMs = 10 * 1000; // 10s
-static const int kNoChangePollingIntervalMs = 2 * 60 * 1000; // 2 mins
-static const int kTwoNoChangePollingIntervalMs = 10 * 60 * 1000; // 10 mins
-static const int kNoWifiPollingIntervalMs = 20 * 1000; // 20s
-
-// WlanOpenHandle
-typedef DWORD(WINAPI* WlanOpenHandleFunction)(DWORD dwClientVersion,
- PVOID pReserved,
- PDWORD pdwNegotiatedVersion,
- PHANDLE phClientHandle);
-
-// WlanEnumInterfaces
-typedef DWORD(WINAPI* WlanEnumInterfacesFunction)(
- HANDLE hClientHandle,
- PVOID pReserved,
- PWLAN_INTERFACE_INFO_LIST* ppInterfaceList);
-
-// WlanGetNetworkBssList
-typedef DWORD(WINAPI* WlanGetNetworkBssListFunction)(
- HANDLE hClientHandle,
- const GUID* pInterfaceGuid,
- const PDOT11_SSID pDot11Ssid,
- DOT11_BSS_TYPE dot11BssType,
- BOOL bSecurityEnabled,
- PVOID pReserved,
- PWLAN_BSS_LIST* ppWlanBssList);
-
-// WlanFreeMemory
-typedef VOID(WINAPI* WlanFreeMemoryFunction)(PVOID pMemory);
-
-// WlanCloseHandle
-typedef DWORD(WINAPI* WlanCloseHandleFunction)(HANDLE hClientHandle,
- PVOID pReserved);
-
-// Extracts data for an access point and converts to AccessPointData.
-AccessPointData GetNetworkData(const WLAN_BSS_ENTRY& bss_entry) {
- AccessPointData access_point_data;
- // Currently we get only MAC address, signal strength and SSID.
- access_point_data.mac_address = MacAddressAsString16(bss_entry.dot11Bssid);
- access_point_data.radio_signal_strength = bss_entry.lRssi;
- // bss_entry.dot11Ssid.ucSSID is not null-terminated.
- base::UTF8ToUTF16(reinterpret_cast<const char*>(bss_entry.dot11Ssid.ucSSID),
- static_cast<ULONG>(bss_entry.dot11Ssid.uSSIDLength),
- &access_point_data.ssid);
-
- // TODO(steveblock): Is it possible to get the following?
- // access_point_data.signal_to_noise
- // access_point_data.age
- // access_point_data.channel
- return access_point_data;
-}
-
-// This class encapsulates loading and interacting with wlan_api.dll, which can
-// not be loaded statically because it's not available in Server 2008 R2, where
-// it must be installed explicitly by the user if and when they wants to use the
-// Wireless interface.
-// https://www.bonusbits.com/wiki/KB:Wlanapi.dll_missing_on_Windows_Server_2008_R2
-class WindowsWlanApi : public WifiDataProviderCommon::WlanApiInterface {
- public:
- static std::unique_ptr<WindowsWlanApi> Create();
-
- // Takes ownership of the library handle.
- explicit WindowsWlanApi(HINSTANCE library);
- ~WindowsWlanApi() override;
-
- // WlanApiInterface implementation
- bool GetAccessPointData(WifiData::AccessPointDataSet* data) override;
-
- private:
- // Logs number of detected wlan interfaces.
- static void LogWlanInterfaceCount(int count);
-
- bool GetInterfaceDataWLAN(HANDLE wlan_handle,
- const GUID& interface_id,
- WifiData::AccessPointDataSet* data);
- // Handle to the wlanapi.dll library.
- HINSTANCE library_;
-
- // Function pointers for WLAN
- WlanOpenHandleFunction WlanOpenHandle_function_;
- WlanEnumInterfacesFunction WlanEnumInterfaces_function_;
- WlanGetNetworkBssListFunction WlanGetNetworkBssList_function_;
- WlanFreeMemoryFunction WlanFreeMemory_function_;
- WlanCloseHandleFunction WlanCloseHandle_function_;
-};
-
-// static
-std::unique_ptr<WindowsWlanApi> WindowsWlanApi::Create() {
- // Use an absolute path to load the DLL to avoid DLL preloading attacks.
- static const wchar_t* const kDLL = L"%WINDIR%\\system32\\wlanapi.dll";
- wchar_t path[MAX_PATH] = {0};
- ExpandEnvironmentStrings(kDLL, path, arraysize(path));
- HINSTANCE library = LoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
- if (!library)
- return nullptr;
- return std::make_unique<WindowsWlanApi>(library);
-}
-
-WindowsWlanApi::WindowsWlanApi(HINSTANCE library) : library_(library) {
- DCHECK(library_);
- // Extract all methods from |library_|.
- WlanOpenHandle_function_ = reinterpret_cast<WlanOpenHandleFunction>(
- GetProcAddress(library_, "WlanOpenHandle"));
- WlanEnumInterfaces_function_ = reinterpret_cast<WlanEnumInterfacesFunction>(
- GetProcAddress(library_, "WlanEnumInterfaces"));
- WlanGetNetworkBssList_function_ =
- reinterpret_cast<WlanGetNetworkBssListFunction>(
- GetProcAddress(library_, "WlanGetNetworkBssList"));
- WlanFreeMemory_function_ = reinterpret_cast<WlanFreeMemoryFunction>(
- GetProcAddress(library_, "WlanFreeMemory"));
- WlanCloseHandle_function_ = reinterpret_cast<WlanCloseHandleFunction>(
- GetProcAddress(library_, "WlanCloseHandle"));
-
- DCHECK(WlanOpenHandle_function_ && WlanEnumInterfaces_function_ &&
- WlanGetNetworkBssList_function_ && WlanFreeMemory_function_ &&
- WlanCloseHandle_function_);
-}
-
-WindowsWlanApi::~WindowsWlanApi() {
- FreeLibrary(library_);
-}
-
-// static
-void WindowsWlanApi::LogWlanInterfaceCount(int count) {
- UMA_HISTOGRAM_CUSTOM_COUNTS("Net.Wifi.InterfaceCount", count, 1 /* min */,
- 5 /* max */, 6 /* bucket_count */);
-}
-
-bool WindowsWlanApi::GetAccessPointData(WifiData::AccessPointDataSet* data) {
- DCHECK(data);
-
- DWORD negotiated_version;
- HANDLE wlan_handle = nullptr;
- // Highest WLAN API version supported by the client; pass the lowest. It seems
- // that the negotiated version is the Vista version (the highest) irrespective
- // of what we pass!
- static const int kXpWlanClientVersion = 1;
- if ((*WlanOpenHandle_function_)(kXpWlanClientVersion, NULL,
- &negotiated_version,
- &wlan_handle) != ERROR_SUCCESS) {
- LogWlanInterfaceCount(0);
- return false;
- }
- DCHECK(wlan_handle);
-
- // Get the list of interfaces. WlanEnumInterfaces allocates |interface_list|.
- WLAN_INTERFACE_INFO_LIST* interface_list = nullptr;
- if ((*WlanEnumInterfaces_function_)(wlan_handle, NULL, &interface_list) !=
- ERROR_SUCCESS) {
- LogWlanInterfaceCount(0);
- return false;
- }
- DCHECK(interface_list);
-
- LogWlanInterfaceCount(interface_list->dwNumberOfItems);
-
- // Go through the list of interfaces and get the data for each.
- for (size_t i = 0; i < interface_list->dwNumberOfItems; ++i) {
- const WLAN_INTERFACE_INFO interface_info = interface_list->InterfaceInfo[i];
-
- // Skip any interface that is midway through association; the
- // WlanGetNetworkBssList function call is known to hang indefinitely
- // when it's in this state. https://crbug.com/39300
- if (interface_info.isState == wlan_interface_state_associating) {
- DLOG(WARNING) << "Skipping wifi scan on adapter " << i << " ("
- << interface_info.strInterfaceDescription
- << ") in 'associating' state. Repeated occurrences "
- "indicates a non-responding adapter.";
- continue;
- }
- GetInterfaceDataWLAN(wlan_handle, interface_info.InterfaceGuid, data);
- }
-
- (*WlanFreeMemory_function_)(interface_list);
-
- return (*WlanCloseHandle_function_)(wlan_handle, NULL) == ERROR_SUCCESS;
-}
-
-// Appends the data for a single interface to |data|. Returns false for error.
-bool WindowsWlanApi::GetInterfaceDataWLAN(const HANDLE wlan_handle,
- const GUID& interface_id,
- WifiData::AccessPointDataSet* data) {
- base::ElapsedTimer wlan_get_network_list_timer;
- // WlanGetNetworkBssList allocates |bss_list|.
- WLAN_BSS_LIST* bss_list = nullptr;
- if ((*WlanGetNetworkBssList_function_)(wlan_handle, &interface_id,
- NULL, // Use all SSIDs.
- dot11_BSS_type_any,
- false, // bSecurityEnabled - unused
- NULL, // reserved
- &bss_list) != ERROR_SUCCESS) {
- return false;
- }
- // WlanGetNetworkBssList() can return success without filling |bss_list|.
- if (!bss_list)
- return false;
-
- UMA_HISTOGRAM_CUSTOM_TIMES("Net.Wifi.ScanLatency",
- wlan_get_network_list_timer.Elapsed(),
- base::TimeDelta::FromMilliseconds(1),
- base::TimeDelta::FromMinutes(1), 100);
-
- for (size_t i = 0; i < bss_list->dwNumberOfItems; ++i)
- data->insert(GetNetworkData(bss_list->wlanBssEntries[i]));
-
- (*WlanFreeMemory_function_)(bss_list);
-
- return true;
-}
-
-} // anonymous namespace
-
-WifiDataProvider* WifiDataProviderManager::DefaultFactoryFunction() {
- return new WifiDataProviderWin();
-}
-
-WifiDataProviderWin::WifiDataProviderWin() = default;
-
-WifiDataProviderWin::~WifiDataProviderWin() = default;
-
-std::unique_ptr<WifiDataProviderCommon::WlanApiInterface>
-WifiDataProviderWin::CreateWlanApi() {
- return WindowsWlanApi::Create();
-}
-
-std::unique_ptr<WifiPollingPolicy> WifiDataProviderWin::CreatePollingPolicy() {
- return std::make_unique<GenericWifiPollingPolicy<
- kDefaultPollingIntervalMs, kNoChangePollingIntervalMs,
- kTwoNoChangePollingIntervalMs, kNoWifiPollingIntervalMs>>();
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/wifi_data_provider_win.h b/chromium/device/geolocation/wifi_data_provider_win.h
deleted file mode 100644
index e100e155aed..00000000000
--- a/chromium/device/geolocation/wifi_data_provider_win.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (c) 2012 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_GEOLOCATION_WIFI_DATA_PROVIDER_WIN_H_
-#define DEVICE_GEOLOCATION_WIFI_DATA_PROVIDER_WIN_H_
-
-#include "base/macros.h"
-#include "device/geolocation/geolocation_export.h"
-#include "device/geolocation/wifi_data_provider_common.h"
-
-namespace device {
-
-class DEVICE_GEOLOCATION_EXPORT WifiDataProviderWin
- : public WifiDataProviderCommon {
- public:
- WifiDataProviderWin();
-
- private:
- ~WifiDataProviderWin() override;
-
- // WifiDataProviderCommon implementation
- std::unique_ptr<WlanApiInterface> CreateWlanApi() override;
- std::unique_ptr<WifiPollingPolicy> CreatePollingPolicy() override;
-
- DISALLOW_COPY_AND_ASSIGN(WifiDataProviderWin);
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_WIFI_DATA_PROVIDER_WIN_H_
diff --git a/chromium/device/geolocation/wifi_data_provider_win_unittest.cc b/chromium/device/geolocation/wifi_data_provider_win_unittest.cc
deleted file mode 100644
index 102401ab38a..00000000000
--- a/chromium/device/geolocation/wifi_data_provider_win_unittest.cc
+++ /dev/null
@@ -1,24 +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.
-
-// Most logic for the platform wifi provider is now factored into
-// WifiDataProviderCommon and covered by it's unit tests.
-
-#include "base/message_loop/message_loop.h"
-#include "device/geolocation/wifi_data_provider_win.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace device {
-
-TEST(GeolocationWifiDataProviderWinTest, CreateDestroy) {
- // WifiDataProvider requires a task runner to be present.
- base::MessageLoopForUI message_loop_;
- scoped_refptr<WifiDataProviderWin> instance(new WifiDataProviderWin);
- instance = NULL;
- SUCCEED();
- // Can't actually call start provider on the WifiDataProviderWin without
- // it accessing hardware and so risking making the test flaky.
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/wifi_polling_policy.cc b/chromium/device/geolocation/wifi_polling_policy.cc
deleted file mode 100644
index 8c3b3f53cbf..00000000000
--- a/chromium/device/geolocation/wifi_polling_policy.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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/geolocation/wifi_polling_policy.h"
-
-namespace device {
-
-namespace {
-WifiPollingPolicy* g_wifi_polling_policy;
-} // namespace
-
-// static
-void WifiPollingPolicy::Initialize(std::unique_ptr<WifiPollingPolicy> policy) {
- DCHECK(!g_wifi_polling_policy);
- g_wifi_polling_policy = policy.release();
-}
-
-// static
-void WifiPollingPolicy::Shutdown() {
- if (g_wifi_polling_policy)
- delete g_wifi_polling_policy;
- g_wifi_polling_policy = nullptr;
-}
-
-// static
-WifiPollingPolicy* WifiPollingPolicy::Get() {
- DCHECK(g_wifi_polling_policy);
- return g_wifi_polling_policy;
-}
-
-// static
-bool WifiPollingPolicy::IsInitialized() {
- return g_wifi_polling_policy != nullptr;
-}
-
-} // namespace device
diff --git a/chromium/device/geolocation/wifi_polling_policy.h b/chromium/device/geolocation/wifi_polling_policy.h
deleted file mode 100644
index 8463f3e4afd..00000000000
--- a/chromium/device/geolocation/wifi_polling_policy.h
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright 2013 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_GEOLOCATION_WIFI_POLLING_POLICY_H_
-#define DEVICE_GEOLOCATION_WIFI_POLLING_POLICY_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/time/time.h"
-#include "device/geolocation/geolocation_export.h"
-
-namespace device {
-
-// Allows sharing and mocking of the update polling policy function.
-class DEVICE_GEOLOCATION_EXPORT WifiPollingPolicy {
- public:
- virtual ~WifiPollingPolicy() = default;
-
- // Methods for managing the single instance of WifiPollingPolicy. The WiFi
- // policy is global so it can outlive the WifiDataProvider instance, which is
- // shut down and destroyed when no WiFi scanning is active.
- static void Initialize(std::unique_ptr<WifiPollingPolicy>);
- static void Shutdown();
- static WifiPollingPolicy* Get();
- static bool IsInitialized();
-
- // Calculates the new polling interval for wifi scans, given the previous
- // interval and whether the last scan produced new results.
- virtual void UpdatePollingInterval(bool scan_results_differ) = 0;
-
- // Use InitialInterval to schedule the initial scan when the wifi data
- // provider is first started. Returns the number of milliseconds before the
- // initial scan should be performed. May return zero if the policy allows a
- // scan to be performed immediately.
- virtual int InitialInterval() = 0;
-
- // Use PollingInterval to schedule a new scan after the previous scan results
- // are available. Only use PollingInterval if WLAN hardware is available and
- // can perform scans for nearby access points. If the current interval is
- // complete, PollingInterval returns the duration for a new interval starting
- // at the current time.
- virtual int PollingInterval() = 0;
-
- // Use NoWifiInterval to schedule a new scan after the previous scan results
- // are available. NoWifiInterval is typically shorter than PollingInterval
- // and should not be used if wifi scanning is available in order to conserve
- // power. If the current interval is complete, NoWifiInterval returns the
- // duration for a new interval starting at the current time.
- virtual int NoWifiInterval() = 0;
-
- protected:
- WifiPollingPolicy() = default;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(WifiPollingPolicy);
-};
-
-// Generic polling policy, constants are compile-time parameterized to allow
-// tuning on a per-platform basis.
-template <int DEFAULT_INTERVAL,
- int NO_CHANGE_INTERVAL,
- int TWO_NO_CHANGE_INTERVAL,
- int NO_WIFI_INTERVAL>
-class GenericWifiPollingPolicy : public WifiPollingPolicy {
- public:
- GenericWifiPollingPolicy() = default;
-
- // WifiPollingPolicy
- void UpdatePollingInterval(bool scan_results_differ) override {
- if (scan_results_differ) {
- polling_interval_ = DEFAULT_INTERVAL;
- } else if (polling_interval_ == DEFAULT_INTERVAL) {
- polling_interval_ = NO_CHANGE_INTERVAL;
- } else {
- DCHECK(polling_interval_ == NO_CHANGE_INTERVAL ||
- polling_interval_ == TWO_NO_CHANGE_INTERVAL);
- polling_interval_ = TWO_NO_CHANGE_INTERVAL;
- }
- }
- int InitialInterval() override { return ComputeInterval(polling_interval_); }
- int PollingInterval() override {
- int interval = ComputeInterval(polling_interval_);
- return interval <= 0 ? polling_interval_ : interval;
- }
- int NoWifiInterval() override {
- int interval = ComputeInterval(NO_WIFI_INTERVAL);
- return interval <= 0 ? NO_WIFI_INTERVAL : interval;
- }
-
- private:
- int ComputeInterval(int polling_interval) {
- base::Time now = base::Time::Now();
-
- int64_t remaining_millis = 0;
- if (!interval_start_.is_null()) {
- // If the new interval duration differs from the initial duration, use the
- // shorter duration.
- if (polling_interval < interval_duration_)
- interval_duration_ = polling_interval;
-
- // Compute the remaining duration of the current interval. If the interval
- // is not yet complete, we will schedule a scan to occur once it is.
- base::TimeDelta remaining =
- interval_start_ +
- base::TimeDelta::FromMilliseconds(interval_duration_) - now;
- remaining_millis = remaining.InMilliseconds();
- }
-
- // If the current interval is complete (or if this is our first scan),
- // start a new interval beginning now.
- if (remaining_millis <= 0) {
- interval_start_ = now;
- interval_duration_ = polling_interval;
- remaining_millis = 0;
- }
-
- return remaining_millis;
- }
-
- // The current duration of the polling interval. When wifi data is
- // substantially the same from one scan to the next, this may be increased to
- // reduce the frequency of wifi scanning.
- int polling_interval_ = DEFAULT_INTERVAL;
-
- // The start time for the most recent interval. Initialized to the "null" time
- // value.
- base::Time interval_start_;
-
- // Duration for the interval starting at |interval_start_|.
- int interval_duration_ = DEFAULT_INTERVAL;
-};
-
-} // namespace device
-
-#endif // DEVICE_GEOLOCATION_WIFI_POLLING_POLICY_H_
diff --git a/chromium/device/geolocation/wifi_polling_policy_unittest.cc b/chromium/device/geolocation/wifi_polling_policy_unittest.cc
deleted file mode 100644
index 18c364f302e..00000000000
--- a/chromium/device/geolocation/wifi_polling_policy_unittest.cc
+++ /dev/null
@@ -1,145 +0,0 @@
-// 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/geolocation/wifi_polling_policy.h"
-
-#include <memory>
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace device {
-
-namespace {
-
-const int kDefaultIntervalMillis = 200;
-const int kNoChangeIntervalMillis = 300;
-const int kTwoNoChangeIntervalMillis = 400;
-const int kNoWifiIntervalMillis = 100;
-
-} // namespace
-
-// Main test fixture
-class GeolocationWifiPollingPolicyTest : public testing::Test {
- public:
- void SetUp() override {
- WifiPollingPolicy::Initialize(
- std::make_unique<GenericWifiPollingPolicy<
- kDefaultIntervalMillis, kNoChangeIntervalMillis,
- kTwoNoChangeIntervalMillis, kNoWifiIntervalMillis>>());
- polling_policy_ = WifiPollingPolicy::Get();
- }
-
- void TearDown() override {
- polling_policy_ = nullptr;
- WifiPollingPolicy::Shutdown();
- }
-
- protected:
- WifiPollingPolicy* polling_policy_ = nullptr;
-};
-
-TEST_F(GeolocationWifiPollingPolicyTest, CreateDestroy) {
- // Test fixture members were SetUp correctly.
- EXPECT_TRUE(polling_policy_);
-}
-
-// Tests that the InitialInterval is zero when the policy is first created.
-TEST_F(GeolocationWifiPollingPolicyTest, InitialIntervalZero) {
- // The first call should return zero, indicating we may scan immediately.
- // Internally, the policy starts a new interval at the current time.
- EXPECT_EQ(0, polling_policy_->InitialInterval());
-
- // The second call should return the non-zero remainder of the interval
- // created by the first call.
- int interval = polling_policy_->InitialInterval();
- EXPECT_GT(interval, 0);
- EXPECT_LE(interval, kDefaultIntervalMillis);
-}
-
-// Tests that the PollingInterval is equal to the default polling interval when
-// the policy is first created.
-TEST_F(GeolocationWifiPollingPolicyTest, PollingIntervalNonZero) {
- // PollingInterval assumes it is only called immediately following a wifi
- // scan. The first call should start a new interval at the current time and
- // return the full duration of the new interval.
- EXPECT_EQ(kDefaultIntervalMillis, polling_policy_->PollingInterval());
-
- // The second call should return the non-zero remainder of the interval
- // created by the first call.
- int interval = polling_policy_->PollingInterval();
- EXPECT_GT(interval, 0);
- EXPECT_LE(interval, kDefaultIntervalMillis);
-}
-
-// Tests that the NoWifiInterval is equal to the default no-wifi interval when
-// the policy is first created.
-TEST_F(GeolocationWifiPollingPolicyTest, NoWifiIntervalNonZero) {
- // NoWifiInterval assumes it is only called immediately following a failed
- // attempt at a wifi scan. The first call should start a new interval at the
- // current time and return the full duration of the new interval.
- EXPECT_EQ(kNoWifiIntervalMillis, polling_policy_->NoWifiInterval());
-
- // The second call should return the non-zero remainder of the interval
- // created by the first call.
- int interval = polling_policy_->NoWifiInterval();
- EXPECT_GT(interval, 0);
- EXPECT_LE(interval, kNoWifiIntervalMillis);
-}
-
-// Calls UpdatePollingInterval once with unchanged scan results. Verifies that
-// the no-change interval is used.
-TEST_F(GeolocationWifiPollingPolicyTest, UpdatePollingIntervalOnce) {
- polling_policy_->UpdatePollingInterval(false);
- EXPECT_EQ(kNoChangeIntervalMillis, polling_policy_->PollingInterval());
-}
-
-// Calls UpdatePollingInterval twice with unchanged scan results. Verifies that
-// the two-no-change interval is used.
-TEST_F(GeolocationWifiPollingPolicyTest, UpdatePollingIntervalTwice) {
- polling_policy_->UpdatePollingInterval(false);
- polling_policy_->UpdatePollingInterval(false);
- EXPECT_EQ(kTwoNoChangeIntervalMillis, polling_policy_->PollingInterval());
-}
-
-// Calls UpdatePollingInterval three times with unchanged scan results. This
-// should have the same effect as calling it twice.
-TEST_F(GeolocationWifiPollingPolicyTest, UpdatePollingIntervalThrice) {
- polling_policy_->UpdatePollingInterval(false);
- polling_policy_->UpdatePollingInterval(false);
- polling_policy_->UpdatePollingInterval(false);
- EXPECT_EQ(kTwoNoChangeIntervalMillis, polling_policy_->PollingInterval());
-}
-
-// Calls UpdatePollingInterval twice with unchanged scan results and then once
-// with differing results. Verifies that the default interval is used.
-TEST_F(GeolocationWifiPollingPolicyTest, UpdatePollingIntervalResultsDiffer) {
- polling_policy_->UpdatePollingInterval(false);
- polling_policy_->UpdatePollingInterval(false);
- polling_policy_->UpdatePollingInterval(true);
- EXPECT_EQ(kDefaultIntervalMillis, polling_policy_->PollingInterval());
-}
-
-TEST_F(GeolocationWifiPollingPolicyTest, ShorterInterval) {
- // Ask for a polling interval.
- EXPECT_EQ(kDefaultIntervalMillis, polling_policy_->PollingInterval());
-
- // Now ask for a no-wifi interval, which is shorter. The returned interval
- // must be no longer than the shorter of the two intervals.
- int interval = polling_policy_->NoWifiInterval();
- EXPECT_GT(interval, 0);
- EXPECT_LE(interval, kNoWifiIntervalMillis);
-}
-
-TEST_F(GeolocationWifiPollingPolicyTest, LongerInterval) {
- // Ask for a no-wifi interval.
- EXPECT_EQ(kNoWifiIntervalMillis, polling_policy_->NoWifiInterval());
-
- // Now ask for a polling interval, which is longer. The returned interval
- // must be no longer than the shorter of the two intervals.
- int interval = polling_policy_->PollingInterval();
- EXPECT_GT(interval, 0);
- EXPECT_LE(interval, kNoWifiIntervalMillis);
-}
-
-} // namespace device
diff --git a/chromium/device/vr/BUILD.gn b/chromium/device/vr/BUILD.gn
index 7a50c06d5d6..f0a7cb1fe1f 100644
--- a/chromium/device/vr/BUILD.gn
+++ b/chromium/device/vr/BUILD.gn
@@ -90,6 +90,8 @@ if (enable_vr) {
"//third_party/openvr:openvr",
]
sources += [
+ "openvr/openvr_api_wrapper.cc",
+ "openvr/openvr_api_wrapper.h",
"openvr/openvr_device.cc",
"openvr/openvr_device.h",
"openvr/openvr_device_provider.cc",
@@ -100,6 +102,7 @@ if (enable_vr) {
"openvr/openvr_render_loop.h",
"openvr/openvr_type_converters.cc",
"openvr/openvr_type_converters.h",
+ "openvr/test/test_hook.h",
]
}
@@ -190,7 +193,9 @@ if (enable_openvr) {
sources = [
"openvr/test/fake_openvr_impl_api.cc",
- "openvr/test/fake_openvr_log.h",
+ "openvr/test/test_helper.cc",
+ "openvr/test/test_helper.h",
+ "openvr/test/test_hook.h",
]
libs = [
diff --git a/chromium/device/vr/OWNERS b/chromium/device/vr/OWNERS
index 30239eea943..1638f0768ae 100644
--- a/chromium/device/vr/OWNERS
+++ b/chromium/device/vr/OWNERS
@@ -5,4 +5,5 @@ mthiesse@chromium.org
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
-# COMPONENT: Internals>VR
+# TEAM: xr-dev@chromium.org
+# COMPONENT: Internals>XR
diff --git a/chromium/device/vr/PRESUBMIT.py b/chromium/device/vr/PRESUBMIT.py
new file mode 100644
index 00000000000..efd7ca550a0
--- /dev/null
+++ b/chromium/device/vr/PRESUBMIT.py
@@ -0,0 +1,24 @@
+# Copyright (c) 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.
+
+"""Top-level presubmit script for device/vr.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+
+def PostUploadHook(cl, change, output_api):
+ """git cl upload will call this hook after the issue is created/modified.
+
+ This hook modifies the CL description in order to run extra GPU
+ tests (in particular, WebXR and WebVR browser tests) in addition
+ to the regular CQ try bots. This test suite is too large to run
+ against all Chromium commits, but should be run against changes
+ likely to affect these tests.
+ """
+ return output_api.EnsureCQIncludeTrybotsAreAdded(
+ cl,
+ ['luci.chromium.try:win_optional_gpu_tests_rel'],
+ 'Automatically added optional GPU tests to run on CQ.')
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 ab9a2ff5e71..03e7d72dece 100644
--- a/chromium/device/vr/android/gvr/cardboard_gamepad_data_fetcher.cc
+++ b/chromium/device/vr/android/gvr/cardboard_gamepad_data_fetcher.cc
@@ -98,7 +98,7 @@ void CardboardGamepadDataFetcher::GetGamepadData(bool devices_changed_hint) {
pad.hand = GamepadHand::kNone;
}
- pad.timestamp = provided_data.timestamp;
+ pad.timestamp = CurrentTimeInMicroseconds();
bool pressed = provided_data.is_screen_touching;
pad.buttons[0].touched = pressed;
diff --git a/chromium/device/vr/android/gvr/gvr_delegate_provider.h b/chromium/device/vr/android/gvr/gvr_delegate_provider.h
index 72855b1e4aa..9bd7708c62b 100644
--- a/chromium/device/vr/android/gvr/gvr_delegate_provider.h
+++ b/chromium/device/vr/android/gvr/gvr_delegate_provider.h
@@ -8,6 +8,7 @@
#include "base/macros.h"
#include "device/vr/android/gvr/gvr_device_provider.h"
#include "device/vr/public/mojom/vr_service.mojom.h"
+#include "device/vr/vr_device.h"
#include "device/vr/vr_export.h"
namespace device {
@@ -19,12 +20,10 @@ class DEVICE_VR_EXPORT GvrDelegateProvider {
GvrDelegateProvider() = default;
virtual bool ShouldDisableGvrDevice() = 0;
virtual void SetDeviceId(unsigned int device_id) = 0;
- virtual void RequestWebVRPresent(
- mojom::VRSubmitFrameClientPtr submit_client,
- mojom::VRPresentationProviderRequest request,
+ virtual void StartWebXRPresentation(
mojom::VRDisplayInfoPtr display_info,
- device::mojom::VRRequestPresentOptionsPtr present_options,
- device::mojom::VRDisplayHost::RequestPresentCallback callback) = 0;
+ mojom::XRDeviceRuntimeSessionOptionsPtr 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 b713ae1b532..20166c658f6 100644
--- a/chromium/device/vr/android/gvr/gvr_device.cc
+++ b/chromium/device/vr/android/gvr/gvr_device.cc
@@ -142,7 +142,10 @@ std::unique_ptr<GvrDevice> GvrDevice::Create() {
return device;
}
-GvrDevice::GvrDevice() : weak_ptr_factory_(this) {
+GvrDevice::GvrDevice()
+ : VRDeviceBase(VRDeviceId::GVR_DEVICE_ID),
+ exclusive_controller_binding_(this),
+ weak_ptr_factory_(this) {
GvrDelegateProvider* delegate_provider = GetGvrDelegateProvider();
if (!delegate_provider || delegate_provider->ShouldDisableGvrDevice())
return;
@@ -159,6 +162,13 @@ GvrDevice::GvrDevice() : weak_ptr_factory_(this) {
}
GvrDevice::~GvrDevice() {
+ if (HasExclusiveSession()) {
+ // We potentially could be destroyed during a navigation before processing
+ // the exclusive session connection error handler. In this case, the
+ // delegate thinks we are still presenting.
+ StopPresenting();
+ }
+
GvrDelegateProviderFactory::SetDevice(nullptr);
if (!non_presenting_context_.obj())
return;
@@ -166,46 +176,81 @@ GvrDevice::~GvrDevice() {
Java_NonPresentingGvrContext_shutdown(env, non_presenting_context_);
}
-void GvrDevice::RequestPresent(
- mojom::VRSubmitFrameClientPtr submit_client,
- mojom::VRPresentationProviderRequest request,
- mojom::VRRequestPresentOptionsPtr present_options,
- mojom::VRDisplayHost::RequestPresentCallback callback) {
+void GvrDevice::RequestSession(
+ mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ mojom::XRRuntime::RequestSessionCallback callback) {
GvrDelegateProvider* delegate_provider = GetGvrDelegateProvider();
if (!delegate_provider) {
- std::move(callback).Run(false, nullptr);
+ 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();
+ }
+}
+
+void GvrDevice::OnStartPresentResult(
+ mojom::XRRuntime::RequestSessionCallback callback,
+ mojom::XRSessionPtr session) {
+ if (!session || !session->connection) {
+ std::move(callback).Run(nullptr, nullptr);
return;
}
- // RequestWebVRPresent is async as we may trigger a DON (Device ON) flow that
- // pauses Chrome.
- delegate_provider->RequestWebVRPresent(
- std::move(submit_client), std::move(request), GetVRDisplayInfo(),
- std::move(present_options),
- base::BindOnce(&GvrDevice::OnRequestPresentResult,
- weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+ OnStartPresenting();
+
+ mojom::XRSessionControllerPtr session_controller;
+ // Close the binding to ensure any previous sessions were closed.
+ // TODO(billorr): Only do this in OnPresentingControllerMojoConnectionError.
+ exclusive_controller_binding_.Close();
+ exclusive_controller_binding_.Bind(mojo::MakeRequest(&session_controller));
+
+ // Unretained is safe because the error handler won't be called after the
+ // binding has been destroyed.
+ exclusive_controller_binding_.set_connection_error_handler(
+ base::BindOnce(&GvrDevice::OnPresentingControllerMojoConnectionError,
+ base::Unretained(this)));
+
+ std::move(callback).Run(std::move(session->connection),
+ std::move(session_controller));
+}
+
+// XRSessionController
+void GvrDevice::SetFrameDataRestricted(bool restricted) {
+ // Presentation sessions can not currently be restricted.
+ DCHECK(false);
}
-void GvrDevice::OnRequestPresentResult(
- mojom::VRDisplayHost::RequestPresentCallback callback,
- bool result,
- mojom::VRDisplayFrameTransportOptionsPtr transport_options) {
- if (result)
- SetIsPresenting();
- std::move(callback).Run(result, std::move(transport_options));
+void GvrDevice::OnPresentingControllerMojoConnectionError() {
+ StopPresenting();
}
-void GvrDevice::ExitPresent() {
+void GvrDevice::StopPresenting() {
GvrDelegateProvider* delegate_provider = GetGvrDelegateProvider();
if (delegate_provider)
delegate_provider->ExitWebVRPresent();
OnExitPresent();
+ exclusive_controller_binding_.Close();
}
-void GvrDevice::OnMagicWindowPoseRequest(
- mojom::VRMagicWindowProvider::GetPoseCallback callback) {
- std::move(callback).Run(
- GvrDelegate::GetVRPosePtrWithNeckModel(gvr_api_.get(), nullptr));
+void GvrDevice::OnMagicWindowFrameDataRequest(
+ mojom::VRMagicWindowProvider::GetFrameDataCallback callback) {
+ mojom::XRFrameDataPtr frame_data = mojom::XRFrameData::New();
+ frame_data->pose =
+ GvrDelegate::GetVRPosePtrWithNeckModel(gvr_api_.get(), nullptr);
+ std::move(callback).Run(std::move(frame_data));
}
void GvrDevice::OnListeningForActivate(bool listening) {
diff --git a/chromium/device/vr/android/gvr/gvr_device.h b/chromium/device/vr/android/gvr/gvr_device.h
index 3376beff589..6061fcbed79 100644
--- a/chromium/device/vr/android/gvr/gvr_device.h
+++ b/chromium/device/vr/android/gvr/gvr_device.h
@@ -18,19 +18,16 @@ namespace device {
class GvrDelegateProvider;
-// TODO(mthiesse, crbug.com/769373): Remove DEVICE_VR_EXPORT.
-class DEVICE_VR_EXPORT GvrDevice : public VRDeviceBase {
+class DEVICE_VR_EXPORT GvrDevice : public VRDeviceBase,
+ public mojom::XRSessionController {
public:
static std::unique_ptr<GvrDevice> Create();
~GvrDevice() override;
// VRDeviceBase
- void RequestPresent(
- mojom::VRSubmitFrameClientPtr submit_client,
- mojom::VRPresentationProviderRequest request,
- mojom::VRRequestPresentOptionsPtr present_options,
- mojom::VRDisplayHost::RequestPresentCallback callback) override;
- void ExitPresent() override;
+ void RequestSession(
+ mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ mojom::XRRuntime::RequestSessionCallback callback) override;
void PauseTracking() override;
void ResumeTracking() override;
@@ -44,13 +41,17 @@ class DEVICE_VR_EXPORT GvrDevice : public VRDeviceBase {
private:
// VRDeviceBase
void OnListeningForActivate(bool listening) override;
- void OnMagicWindowPoseRequest(
- mojom::VRMagicWindowProvider::GetPoseCallback callback) override;
+ void OnMagicWindowFrameDataRequest(
+ mojom::VRMagicWindowProvider::GetFrameDataCallback callback) override;
- void OnRequestPresentResult(
- mojom::VRDisplayHost::RequestPresentCallback callback,
- bool result,
- mojom::VRDisplayFrameTransportOptionsPtr transport_options);
+ void OnStartPresentResult(mojom::XRRuntime::RequestSessionCallback callback,
+ mojom::XRSessionPtr session);
+
+ // XRSessionController
+ void SetFrameDataRestricted(bool restricted) override;
+
+ void OnPresentingControllerMojoConnectionError();
+ void StopPresenting();
GvrDevice();
GvrDelegateProvider* GetGvrDelegateProvider();
@@ -58,6 +59,8 @@ class DEVICE_VR_EXPORT GvrDevice : public VRDeviceBase {
base::android::ScopedJavaGlobalRef<jobject> non_presenting_context_;
std::unique_ptr<gvr::GvrApi> gvr_api_;
+ mojo::Binding<mojom::XRSessionController> exclusive_controller_binding_;
+
base::WeakPtrFactory<GvrDevice> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(GvrDevice);
diff --git a/chromium/device/vr/android/gvr/gvr_device_provider.cc b/chromium/device/vr/android/gvr/gvr_device_provider.cc
index b979c4c0c82..ea77025b1e8 100644
--- a/chromium/device/vr/android/gvr/gvr_device_provider.cc
+++ b/chromium/device/vr/android/gvr/gvr_device_provider.cc
@@ -12,12 +12,15 @@ GvrDeviceProvider::GvrDeviceProvider() = default;
GvrDeviceProvider::~GvrDeviceProvider() = default;
void GvrDeviceProvider::Initialize(
- base::Callback<void(VRDevice*)> add_device_callback,
- base::Callback<void(VRDevice*)> remove_device_callback,
+ base::RepeatingCallback<void(unsigned int,
+ mojom::VRDisplayInfoPtr,
+ mojom::XRRuntimePtr)> add_device_callback,
+ base::RepeatingCallback<void(unsigned int)> remove_device_callback,
base::OnceClosure initialization_complete) {
vr_device_ = GvrDevice::Create();
if (vr_device_)
- add_device_callback.Run(vr_device_.get());
+ add_device_callback.Run(vr_device_->GetId(), vr_device_->GetVRDisplayInfo(),
+ vr_device_->BindXRRuntimePtr());
initialized_ = true;
std::move(initialization_complete).Run();
}
diff --git a/chromium/device/vr/android/gvr/gvr_device_provider.h b/chromium/device/vr/android/gvr/gvr_device_provider.h
index 8bde25885f9..c833e473404 100644
--- a/chromium/device/vr/android/gvr/gvr_device_provider.h
+++ b/chromium/device/vr/android/gvr/gvr_device_provider.h
@@ -20,9 +20,12 @@ class DEVICE_VR_EXPORT GvrDeviceProvider : public VRDeviceProvider {
GvrDeviceProvider();
~GvrDeviceProvider() override;
- void Initialize(base::Callback<void(VRDevice*)> add_device_callback,
- base::Callback<void(VRDevice*)> remove_device_callback,
- base::OnceClosure initialization_complete) override;
+ void Initialize(
+ base::RepeatingCallback<void(unsigned int,
+ mojom::VRDisplayInfoPtr,
+ mojom::XRRuntimePtr)> add_device_callback,
+ base::RepeatingCallback<void(unsigned int)> 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 bebab399036..f48fcd31133 100644
--- a/chromium/device/vr/android/gvr/gvr_gamepad_data_fetcher.cc
+++ b/chromium/device/vr/android/gvr/gvr_gamepad_data_fetcher.cc
@@ -102,7 +102,7 @@ void GvrGamepadDataFetcher::GetGamepadData(bool devices_changed_hint) {
}
pad.connected = provided_data.connected;
- pad.timestamp = provided_data.timestamp;
+ pad.timestamp = CurrentTimeInMicroseconds();
if (provided_data.is_touching) {
gfx::Vector2dF touch_position = provided_data.touch_pos;
diff --git a/chromium/device/vr/buildflags/buildflags.gni b/chromium/device/vr/buildflags/buildflags.gni
index 368caa16284..7e663a3e315 100644
--- a/chromium/device/vr/buildflags/buildflags.gni
+++ b/chromium/device/vr/buildflags/buildflags.gni
@@ -30,17 +30,24 @@ declare_args() {
# Whether to include VR extras like test APKs in non-VR-specific targets
include_vr_data = false
+}
+declare_args() {
# TODO(crbug.com/837999, crbug.com/841389): We currently only support arm and
# we are limiting to canary and dev until binary size issues are resolved.
- # TODO(crbug.com/845080): add android_channel == "dev" || "canary"
- package_arcore = is_android && !is_chromecast && current_cpu == "arm" &&
- android_channel == "default"
+ # 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")
# 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
# not support all channels. This flag governs the inclusion of code that must
# be identical across configs.
- enable_arcore = is_android && !is_chromecast &&
+ # TODO(crbug.com/836524): once we've refactored AR code out from vr
+ # directories, we can stop requiring |enable_vr| here.
+ enable_arcore = enable_vr && is_android && !is_chromecast &&
(current_cpu == "arm" || current_cpu == "arm64")
}
diff --git a/chromium/device/vr/oculus/OWNERS b/chromium/device/vr/oculus/OWNERS
index a1660982293..5b1397a961f 100644
--- a/chromium/device/vr/oculus/OWNERS
+++ b/chromium/device/vr/oculus/OWNERS
@@ -1,2 +1,5 @@
per-file *_type_converter*.*=set noparent
per-file *_type_converter*.*=file://ipc/SECURITY_OWNERS
+
+# TEAM: xr-dev@chromium.org
+# COMPONENT: Internals>XR>VR
diff --git a/chromium/device/vr/oculus/oculus_device.cc b/chromium/device/vr/oculus/oculus_device.cc
index 9819906c7e2..f3d816d1e94 100644
--- a/chromium/device/vr/oculus/oculus_device.cc
+++ b/chromium/device/vr/oculus/oculus_device.cc
@@ -83,60 +83,170 @@ mojom::VRDisplayInfoPtr CreateVRDisplayInfo(unsigned int id,
} // namespace
-OculusDevice::OculusDevice(ovrSession session, ovrGraphicsLuid luid)
- : session_(session),
+OculusDevice::OculusDevice()
+ : VRDeviceBase(VRDeviceId::OCULUS_DEVICE_ID),
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ exclusive_controller_binding_(this),
weak_ptr_factory_(this) {
+ StartOvrSession();
+ if (!session_) {
+ return;
+ }
+
SetVRDisplayInfo(CreateVRDisplayInfo(GetId(), session_));
- render_loop_ = std::make_unique<OculusRenderLoop>(session_, luid);
+ 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();
+
+ // 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;
}
-OculusDevice::~OculusDevice() {}
+void OculusDevice::RequestSession(
+ mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ mojom::XRRuntime::RequestSessionCallback callback) {
+ StopOvrSession();
-void OculusDevice::RequestPresent(
- mojom::VRSubmitFrameClientPtr submit_client,
- mojom::VRPresentationProviderRequest request,
- mojom::VRRequestPresentOptionsPtr present_options,
- mojom::VRDisplayHost::RequestPresentCallback callback) {
if (!render_loop_->IsRunning())
render_loop_->Start();
if (!render_loop_->IsRunning()) {
- std::move(callback).Run(false, nullptr);
+ std::move(callback).Run(nullptr, nullptr);
+ StartOvrSession();
return;
}
auto on_request_present_result =
- base::BindOnce(&OculusDevice::OnRequestPresentResult,
+ base::BindOnce(&OculusDevice::OnRequestSessionResult,
weak_ptr_factory_.GetWeakPtr(), std::move(callback));
+
render_loop_->task_runner()->PostTask(
- FROM_HERE,
- base::BindOnce(&OculusRenderLoop::RequestPresent,
- render_loop_->GetWeakPtr(), submit_client.PassInterface(),
- std::move(request), std::move(present_options),
- std::move(on_request_present_result)));
+ FROM_HERE, base::BindOnce(&OculusRenderLoop::RequestSession,
+ render_loop_->GetWeakPtr(), std::move(options),
+ std::move(on_request_present_result)));
}
-void OculusDevice::OnRequestPresentResult(
- mojom::VRDisplayHost::RequestPresentCallback callback,
+void OculusDevice::OnRequestSessionResult(
+ mojom::XRRuntime::RequestSessionCallback callback,
bool result,
+ mojom::VRSubmitFrameClientRequest request,
+ mojom::VRPresentationProviderPtrInfo provider_info,
mojom::VRDisplayFrameTransportOptionsPtr transport_options) {
- std::move(callback).Run(result, std::move(transport_options));
+ if (!result) {
+ std::move(callback).Run(nullptr, nullptr);
+
+ // Start magic window again.
+ StartOvrSession();
+ return;
+ }
+
+ 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));
+
+ // Unretained is safe because the error handler won't be called after the
+ // binding has been destroyed.
+ exclusive_controller_binding_.set_connection_error_handler(
+ base::BindOnce(&OculusDevice::OnPresentingControllerMojoConnectionError,
+ base::Unretained(this)));
+
+ std::move(callback).Run(std::move(connection), std::move(session_controller));
+
+ 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});
}
-void OculusDevice::ExitPresent() {
+// XRSessionController
+void OculusDevice::SetFrameDataRestricted(bool restricted) {
+ // Presentation sessions can not currently be restricted.
+ DCHECK(false);
+}
+
+void OculusDevice::OnPresentingControllerMojoConnectionError() {
render_loop_->task_runner()->PostTask(
FROM_HERE, base::BindOnce(&OculusRenderLoop::ExitPresent,
render_loop_->GetWeakPtr()));
OnExitPresent();
+ exclusive_controller_binding_.Close();
+}
+
+void OculusDevice::OnPresentationEnded() {
+ StartOvrSession();
+}
+
+void OculusDevice::StartOvrSession() {
+ ovrInitParams initParams = {ovrInit_RequestVersion | ovrInit_Invisible,
+ OVR_MINOR_VERSION, NULL, 0, 0};
+ ovrResult result = ovr_Initialize(&initParams);
+ if (OVR_FAILURE(result)) {
+ return;
+ }
+
+ ovrGraphicsLuid luid;
+ result = ovr_Create(&session_, &luid);
+ if (OVR_FAILURE(result)) {
+ return;
+ }
}
-void OculusDevice::OnMagicWindowPoseRequest(
- mojom::VRMagicWindowProvider::GetPoseCallback callback) {
+void OculusDevice::StopOvrSession() {
+ if (session_) {
+ // Shut down our current session so the presentation session can begin.
+ ovr_Destroy(session_);
+ session_ = nullptr;
+ ovr_Shutdown();
+ }
+}
+
+void OculusDevice::OnMagicWindowFrameDataRequest(
+ mojom::VRMagicWindowProvider::GetFrameDataCallback callback) {
+ if (!session_) {
+ std::move(callback).Run(nullptr);
+ return;
+ }
+
ovrTrackingState state = ovr_GetTrackingState(session_, 0, false);
- std::move(callback).Run(
- mojo::ConvertTo<mojom::VRPosePtr>(state.HeadPose.ThePose));
+
+ mojom::XRFrameDataPtr frame_data = mojom::XRFrameData::New();
+ frame_data->pose = mojo::ConvertTo<mojom::VRPosePtr>(state.HeadPose.ThePose);
+ std::move(callback).Run(std::move(frame_data));
+}
+
+void OculusDevice::RegisterDataFetcher(OculusGamepadDataFetcher* data_fetcher) {
+ data_fetcher_ = data_fetcher;
}
} // namespace device
diff --git a/chromium/device/vr/oculus/oculus_device.h b/chromium/device/vr/oculus/oculus_device.h
index 414d83b1899..cff84ccdc6a 100644
--- a/chromium/device/vr/oculus/oculus_device.h
+++ b/chromium/device/vr/oculus/oculus_device.h
@@ -9,6 +9,7 @@
#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"
@@ -18,33 +19,56 @@ namespace device {
class OculusRenderLoop;
-class OculusDevice : public VRDeviceBase {
+class OculusDevice : public VRDeviceBase,
+ public mojom::XRSessionController,
+ public OculusGamepadDataProvider {
public:
- explicit OculusDevice(ovrSession session, ovrGraphicsLuid luid);
+ explicit OculusDevice();
~OculusDevice() override;
// VRDeviceBase
- void RequestPresent(
- mojom::VRSubmitFrameClientPtr submit_client,
- mojom::VRPresentationProviderRequest request,
- mojom::VRRequestPresentOptionsPtr present_options,
- mojom::VRDisplayHost::RequestPresentCallback callback) override;
- void ExitPresent() override;
- void OnMagicWindowPoseRequest(
- mojom::VRMagicWindowProvider::GetPoseCallback callback) override;
-
- void OnRequestPresentResult(
- mojom::VRDisplayHost::RequestPresentCallback callback,
+ void RequestSession(
+ mojom::XRDeviceRuntimeSessionOptionsPtr 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);
+ bool IsInitialized() { return !!session_; }
+
private:
+ // XRSessionController
+ void SetFrameDataRestricted(bool restricted) override;
+
+ void OnPresentingControllerMojoConnectionError();
+
+ // OculusGamepadDataProvider
+ void RegisterDataFetcher(OculusGamepadDataFetcher*) 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_;
- mojom::VRSubmitFrameClientPtr submit_client_;
+ OculusGamepadDataFetcher* data_fetcher_ = nullptr;
mojom::VRDisplayInfoPtr display_info_;
- ovrSession session_;
+ ovrSession session_ = nullptr;
+ OculusGamepadDataFetcher::Factory* oculus_gamepad_factory_ = nullptr;
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
+ mojo::Binding<mojom::XRSessionController> exclusive_controller_binding_;
+
base::WeakPtrFactory<OculusDevice> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(OculusDevice);
diff --git a/chromium/device/vr/oculus/oculus_device_provider.cc b/chromium/device/vr/oculus/oculus_device_provider.cc
index 0bd1d1e878e..cda8a0a898f 100644
--- a/chromium/device/vr/oculus/oculus_device_provider.cc
+++ b/chromium/device/vr/oculus/oculus_device_provider.cc
@@ -14,44 +14,30 @@ namespace device {
OculusVRDeviceProvider::OculusVRDeviceProvider() : initialized_(false) {}
OculusVRDeviceProvider::~OculusVRDeviceProvider() {
- device::GamepadDataFetcherManager::GetInstance()->RemoveSourceFactory(
- device::GAMEPAD_SOURCE_OCULUS);
-
- if (session_)
- ovr_Destroy(session_);
- ovr_Shutdown();
}
void OculusVRDeviceProvider::Initialize(
- base::RepeatingCallback<void(VRDevice*)> add_device_callback,
- base::RepeatingCallback<void(VRDevice*)> remove_device_callback,
+ base::RepeatingCallback<void(unsigned int,
+ mojom::VRDisplayInfoPtr,
+ mojom::XRRuntimePtr)> add_device_callback,
+ base::RepeatingCallback<void(unsigned int)> remove_device_callback,
base::OnceClosure initialization_complete) {
CreateDevice();
if (device_)
- add_device_callback.Run(device_.get());
+ add_device_callback.Run(device_->GetId(), device_->GetVRDisplayInfo(),
+ device_->BindXRRuntimePtr());
initialized_ = true;
std::move(initialization_complete).Run();
}
void OculusVRDeviceProvider::CreateDevice() {
// TODO(billorr): Check for headset presence without starting runtime.
- ovrInitParams initParams = {ovrInit_RequestVersion, OVR_MINOR_VERSION, NULL,
- 0, 0};
- ovrResult result = ovr_Initialize(&initParams);
- if (OVR_FAILURE(result)) {
- return;
- }
+ device_ = std::make_unique<OculusDevice>();
- // TODO(792657): luid should be used to handle multi-gpu machines.
- ovrGraphicsLuid luid;
- result = ovr_Create(&session_, &luid);
- if (OVR_FAILURE(result)) {
- return;
+ // If the device failed to inialize, don't return it.
+ if (!device_->IsInitialized()) {
+ device_ = nullptr;
}
-
- device_ = std::make_unique<OculusDevice>(session_, luid);
- GamepadDataFetcherManager::GetInstance()->AddFactory(
- new OculusGamepadDataFetcher::Factory(device_->GetId(), session_));
}
bool OculusVRDeviceProvider::Initialized() {
diff --git a/chromium/device/vr/oculus/oculus_device_provider.h b/chromium/device/vr/oculus/oculus_device_provider.h
index 8a52526228d..24791e1d617 100644
--- a/chromium/device/vr/oculus/oculus_device_provider.h
+++ b/chromium/device/vr/oculus/oculus_device_provider.h
@@ -24,8 +24,10 @@ class DEVICE_VR_EXPORT OculusVRDeviceProvider : public VRDeviceProvider {
~OculusVRDeviceProvider() override;
void Initialize(
- base::RepeatingCallback<void(VRDevice*)> add_device_callback,
- base::RepeatingCallback<void(VRDevice*)> remove_device_callback,
+ base::RepeatingCallback<void(unsigned int,
+ mojom::VRDisplayInfoPtr,
+ mojom::XRRuntimePtr)> add_device_callback,
+ base::RepeatingCallback<void(unsigned int)> remove_device_callback,
base::OnceClosure initialization_complete) override;
bool Initialized() override;
@@ -34,7 +36,6 @@ class DEVICE_VR_EXPORT OculusVRDeviceProvider : public VRDeviceProvider {
void CreateDevice();
bool initialized_;
- ovrSession session_ = nullptr;
std::unique_ptr<OculusDevice> device_;
DISALLOW_COPY_AND_ASSIGN(OculusVRDeviceProvider);
diff --git a/chromium/device/vr/oculus/oculus_gamepad_data_fetcher.cc b/chromium/device/vr/oculus/oculus_gamepad_data_fetcher.cc
index b9a78ff48bb..aec6d3c16fe 100644
--- a/chromium/device/vr/oculus/oculus_gamepad_data_fetcher.cc
+++ b/chromium/device/vr/oculus/oculus_gamepad_data_fetcher.cc
@@ -96,7 +96,7 @@ void SetTouchData(PadState* state,
return;
}
}
- pad.timestamp = input_state.TimeInSeconds;
+ pad.timestamp = OculusGamepadDataFetcher::CurrentTimeInMicroseconds();
pad.axes_length = 0;
pad.buttons_length = 0;
pad.axes[pad.axes_length++] = input_state.Thumbstick[hand].x;
@@ -170,8 +170,8 @@ void SetTouchData(PadState* state,
} // namespace
OculusGamepadDataFetcher::Factory::Factory(unsigned int display_id,
- ovrSession session)
- : display_id_(display_id), session_(session) {
+ OculusGamepadDataProvider* provider)
+ : display_id_(display_id), provider_(provider) {
DVLOG(1) << __FUNCTION__ << "=" << this;
}
@@ -181,17 +181,21 @@ OculusGamepadDataFetcher::Factory::~Factory() {
std::unique_ptr<GamepadDataFetcher>
OculusGamepadDataFetcher::Factory::CreateDataFetcher() {
- return std::make_unique<OculusGamepadDataFetcher>(display_id_, session_);
+ return std::make_unique<OculusGamepadDataFetcher>(display_id_, provider_);
}
GamepadSource OculusGamepadDataFetcher::Factory::source() {
return GAMEPAD_SOURCE_OCULUS;
}
-OculusGamepadDataFetcher::OculusGamepadDataFetcher(unsigned int display_id,
- ovrSession session)
- : display_id_(display_id), session_(session) {
+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() {
@@ -205,20 +209,17 @@ GamepadSource OculusGamepadDataFetcher::source() {
void OculusGamepadDataFetcher::OnAddedToProvider() {}
void OculusGamepadDataFetcher::GetGamepadData(bool devices_changed_hint) {
- ovrInputState input_state;
- if ((OVR_SUCCESS(ovr_GetInputState(session_, ovrControllerType_Touch,
- &input_state)))) {
- ovrTrackingState tracking_state = ovr_GetTrackingState(session_, 0, false);
+ base::AutoLock lock(lock_);
+ if (data_.have_input_touch) {
SetTouchData(GetPadState(ovrControllerType_LTouch),
- tracking_state.HandPoses[ovrHand_Left], input_state,
+ data_.tracking.HandPoses[ovrHand_Left], data_.input_touch,
ovrHand_Left, display_id_);
SetTouchData(GetPadState(ovrControllerType_RTouch),
- tracking_state.HandPoses[ovrHand_Right], input_state,
+ data_.tracking.HandPoses[ovrHand_Right], data_.input_touch,
ovrHand_Right, display_id_);
}
- if ((OVR_SUCCESS(ovr_GetInputState(session_, ovrControllerType_Remote,
- &input_state)))) {
+ if (data_.have_input_remote) {
PadState* state = GetPadState(ovrControllerType_Remote);
if (state) {
Gamepad& pad = state->data;
@@ -229,19 +230,24 @@ void OculusGamepadDataFetcher::GetGamepadData(bool devices_changed_hint) {
pad.is_xr = true;
pad.display_id = display_id_;
}
- pad.timestamp = input_state.TimeInSeconds;
+ pad.timestamp = CurrentTimeInMicroseconds();
pad.axes_length = 0;
pad.buttons_length = 0;
- SetGamepadButton(&pad, input_state, ovrButton_Enter);
- SetGamepadButton(&pad, input_state, ovrButton_Back);
- SetGamepadButton(&pad, input_state, ovrButton_Up);
- SetGamepadButton(&pad, input_state, ovrButton_Down);
- SetGamepadButton(&pad, input_state, ovrButton_Left);
- SetGamepadButton(&pad, input_state, ovrButton_Right);
+ 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
index b89ebf37811..e3430e6163b 100644
--- a/chromium/device/vr/oculus/oculus_gamepad_data_fetcher.h
+++ b/chromium/device/vr/oculus/oculus_gamepad_data_fetcher.h
@@ -5,26 +5,43 @@
#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, ovrSession session);
+ Factory(unsigned int display_id, OculusGamepadDataProvider* provider);
~Factory() override;
std::unique_ptr<GamepadDataFetcher> CreateDataFetcher() override;
GamepadSource source() override;
private:
unsigned int display_id_;
- ovrSession session_;
+ OculusGamepadDataProvider* provider_;
};
- OculusGamepadDataFetcher(unsigned int display_id, ovrSession session);
+ OculusGamepadDataFetcher(unsigned int display_id,
+ OculusGamepadDataProvider* provider);
~OculusGamepadDataFetcher() override;
GamepadSource source() override;
@@ -33,9 +50,19 @@ class OculusGamepadDataFetcher : public GamepadDataFetcher {
void PauseHint(bool paused) override;
void OnAddedToProvider() override;
+ void UpdateGamepadData(OculusInputData data); // Called on UI thread.
+
private:
unsigned int display_id_;
- ovrSession session_;
+
+ // 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);
};
diff --git a/chromium/device/vr/oculus/oculus_render_loop.cc b/chromium/device/vr/oculus/oculus_render_loop.cc
index 24c3a8269a2..80a37b6ec6e 100644
--- a/chromium/device/vr/oculus/oculus_render_loop.cc
+++ b/chromium/device/vr/oculus/oculus_render_loop.cc
@@ -41,12 +41,16 @@ gfx::Transform PoseToTransform(const ovrPosef& pose) {
} // namespace
-OculusRenderLoop::OculusRenderLoop(ovrSession session, ovrGraphicsLuid luid)
+OculusRenderLoop::OculusRenderLoop(
+ base::RepeatingCallback<void()> on_presentation_ended,
+ base::RepeatingCallback<
+ void(ovrInputState, ovrInputState, ovrTrackingState, bool, bool)>
+ on_controller_updated)
: base::Thread("OculusRenderLoop"),
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
- session_(session),
- luid_(luid),
binding_(this),
+ on_presentation_ended_(on_presentation_ended),
+ on_controller_updated_(on_controller_updated),
weak_ptr_factory_(this) {
DCHECK(main_thread_task_runner_);
}
@@ -55,8 +59,16 @@ OculusRenderLoop::~OculusRenderLoop() {
Stop();
}
+void OculusRenderLoop::ClearPendingFrame() {
+ has_outstanding_frame_ = false;
+ if (delayed_get_frame_data_callback_) {
+ base::ResetAndReturn(&delayed_get_frame_data_callback_).Run();
+ }
+}
+
void OculusRenderLoop::CleanUp() {
submit_client_ = nullptr;
+ StopOvrSession();
binding_.Close();
}
@@ -64,6 +76,7 @@ void OculusRenderLoop::SubmitFrameMissing(int16_t frame_index,
const gpu::SyncToken& sync_token) {
// Nothing to do. It's OK to start the next frame even if the current
// one didn't get sent to the ovrSession.
+ ClearPendingFrame();
}
void OculusRenderLoop::SubmitFrame(int16_t frame_index,
@@ -82,6 +95,29 @@ void OculusRenderLoop::SubmitFrameDrawnIntoTexture(
NOTREACHED();
}
+void OculusRenderLoop::CreateOvrSwapChain() {
+ ovrTextureSwapChainDesc desc = {};
+ desc.Type = ovrTexture_2D;
+ desc.ArraySize = 1;
+ desc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB;
+ desc.Width = source_size_.width();
+ desc.Height = source_size_.height();
+ desc.MipLevels = 1;
+ desc.SampleCount = 1;
+ desc.StaticImage = ovrFalse;
+ desc.MiscFlags = ovrTextureMisc_DX_Typeless;
+ desc.BindFlags = ovrTextureBind_DX_RenderTarget;
+ ovr_CreateTextureSwapChainDX(session_, texture_helper_.GetDevice().Get(),
+ &desc, &texture_swap_chain_);
+}
+
+void OculusRenderLoop::DestroyOvrSwapChain() {
+ if (texture_swap_chain_) {
+ ovr_DestroyTextureSwapChain(session_, texture_swap_chain_);
+ texture_swap_chain_ = 0;
+ }
+}
+
void OculusRenderLoop::SubmitFrameWithTextureHandle(
int16_t frame_index,
mojo::ScopedHandle texture_handle) {
@@ -93,27 +129,17 @@ void OculusRenderLoop::SubmitFrameWithTextureHandle(
platform_handle.struct_size = sizeof(platform_handle);
MojoResult result = MojoUnwrapPlatformHandle(texture_handle.release().value(),
nullptr, &platform_handle);
- if (result != MOJO_RESULT_OK)
+ if (result != MOJO_RESULT_OK) {
+ ClearPendingFrame();
return;
+ }
texture_helper_.SetSourceTexture(
base::win::ScopedHandle(reinterpret_cast<HANDLE>(platform_handle.value)));
// Create swap chain on demand.
if (!texture_swap_chain_) {
- ovrTextureSwapChainDesc desc = {};
- desc.Type = ovrTexture_2D;
- desc.ArraySize = 1;
- desc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB;
- desc.Width = source_size_.width();
- desc.Height = source_size_.height();
- desc.MipLevels = 1;
- desc.SampleCount = 1;
- desc.StaticImage = ovrFalse;
- desc.MiscFlags = ovrTextureMisc_DX_Typeless;
- desc.BindFlags = ovrTextureBind_DX_RenderTarget;
- ovr_CreateTextureSwapChainDX(session_, texture_helper_.GetDevice().Get(),
- &desc, &texture_swap_chain_);
+ CreateOvrSwapChain();
}
bool copy_succeeded = false;
@@ -169,7 +195,16 @@ void OculusRenderLoop::SubmitFrameWithTextureHandle(
ovrResult result =
ovr_SubmitFrame(session_, ovr_frame_index_, &view_scale_desc,
layer_headers, layer_count);
- DCHECK(OVR_SUCCESS(result));
+ if (!OVR_SUCCESS(result)) {
+ copy_succeeded = false;
+
+ // We failed to present. Create a new swap chain.
+ StopOvrSession();
+ StartOvrSession();
+ if (!session_) {
+ ExitPresent();
+ }
+ }
ovr_frame_index_++;
}
}
@@ -177,6 +212,7 @@ void OculusRenderLoop::SubmitFrameWithTextureHandle(
submit_client_->OnSubmitFrameTransferred(copy_succeeded);
submit_client_->OnSubmitFrameRendered();
#endif
+ ClearPendingFrame();
}
void OculusRenderLoop::UpdateLayerBounds(int16_t frame_id,
@@ -189,26 +225,30 @@ void OculusRenderLoop::UpdateLayerBounds(int16_t frame_id,
right_bounds_ = right_bounds;
source_size_ = source_size;
- // TODO(billorr): Recreate texture_swap_chain_ when source_size_ has changed.
+ DestroyOvrSwapChain();
};
-void OculusRenderLoop::RequestPresent(
- mojom::VRSubmitFrameClientPtrInfo submit_client_info,
- mojom::VRPresentationProviderRequest request,
- device::mojom::VRRequestPresentOptionsPtr present_options,
- device::mojom::VRDisplayHost::RequestPresentCallback callback) {
+void OculusRenderLoop::RequestSession(
+ mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ RequestSessionCallback callback) {
+ DCHECK(options->immersive);
+
+ StartOvrSession();
+ if (!session_
#if defined(OS_WIN)
- if (!texture_helper_.SetAdapterLUID(*reinterpret_cast<LUID*>(&luid_)) ||
- !texture_helper_.EnsureInitialized()) {
+ || !texture_helper_.SetAdapterLUID(*reinterpret_cast<LUID*>(&luid_)) ||
+ !texture_helper_.EnsureInitialized()
+#endif
+ ) {
main_thread_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(std::move(callback), false, nullptr));
+ FROM_HERE,
+ base::BindOnce(std::move(callback), false, nullptr, nullptr, nullptr));
return;
}
-#endif
- submit_client_.Bind(std::move(submit_client_info));
binding_.Close();
- binding_.Bind(std::move(request));
+ device::mojom::VRPresentationProviderPtr provider;
+ binding_.Bind(mojo::MakeRequest(&provider));
device::mojom::VRDisplayFrameTransportOptionsPtr transport_options =
device::mojom::VRDisplayFrameTransportOptions::New();
@@ -218,18 +258,51 @@ void OculusRenderLoop::RequestPresent(
// able to safely ignore ones that our implementation doesn't care about.
transport_options->wait_for_transfer_notification = true;
- report_webxr_input_ = present_options->webxr_input;
-
main_thread_task_runner_->PostTask(
FROM_HERE,
- base::BindOnce(std::move(callback), true, std::move(transport_options)));
+ base::BindOnce(std::move(callback), true,
+ mojo::MakeRequest(&submit_client_),
+ provider.PassInterface(), std::move(transport_options)));
is_presenting_ = true;
}
+void OculusRenderLoop::StartOvrSession() {
+ ovrInitParams initParams = {ovrInit_RequestVersion | ovrInit_MixedRendering,
+ OVR_MINOR_VERSION, NULL, 0, 0};
+ ovrResult result = ovr_Initialize(&initParams);
+ if (OVR_FAILURE(result)) {
+ return;
+ }
+
+ result = ovr_Create(&session_, &luid_);
+ if (OVR_FAILURE(result)) {
+ return;
+ }
+}
+
+void OculusRenderLoop::StopOvrSession() {
+ DestroyOvrSwapChain();
+ if (session_) {
+ // Shut down our current session so the presentation session can begin.
+ ovr_Destroy(session_);
+ session_ = nullptr;
+ ovr_Shutdown();
+ }
+
+ texture_helper_.Reset();
+ texture_swap_chain_ = 0;
+ ovr_frame_index_ = 0;
+}
+
void OculusRenderLoop::ExitPresent() {
is_presenting_ = false;
binding_.Close();
submit_client_ = nullptr;
+ ClearPendingFrame();
+
+ StopOvrSession();
+
+ main_thread_task_runner_->PostTask(FROM_HERE, on_presentation_ended_);
}
void OculusRenderLoop::Init() {}
@@ -238,10 +311,22 @@ base::WeakPtr<OculusRenderLoop> OculusRenderLoop::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
-void OculusRenderLoop::GetVSync(
- mojom::VRPresentationProvider::GetVSyncCallback callback) {
+void OculusRenderLoop::GetFrameData(
+ mojom::VRPresentationProvider::GetFrameDataCallback callback) {
DCHECK(is_presenting_);
- int16_t frame = next_frame_id_;
+
+ if (has_outstanding_frame_) {
+ DCHECK(!delayed_get_frame_data_callback_);
+ delayed_get_frame_data_callback_ =
+ base::BindOnce(&OculusRenderLoop::GetFrameData, base::Unretained(this),
+ std::move(callback));
+ return;
+ }
+ has_outstanding_frame_ = true;
+
+ mojom::XRFrameDataPtr frame_data = mojom::XRFrameData::New();
+
+ frame_data->frame_id = next_frame_id_;
next_frame_id_ += 1;
if (next_frame_id_ < 0) {
next_frame_id_ = 0;
@@ -251,19 +336,20 @@ void OculusRenderLoop::GetVSync(
ovr_GetPredictedDisplayTime(session_, ovr_frame_index_ + 1);
ovrTrackingState state = ovr_GetTrackingState(session_, predicted_time, true);
sensor_time_ = ovr_GetTimeInSeconds();
+ frame_data->time_delta = base::TimeDelta::FromSecondsD(predicted_time);
+
mojom::VRPosePtr pose =
mojo::ConvertTo<mojom::VRPosePtr>(state.HeadPose.ThePose);
last_render_pose_ = state.HeadPose.ThePose;
- if (pose && report_webxr_input_) {
- pose->input_state = GetInputState(state);
- }
+ DCHECK(pose);
+ pose->input_state = GetInputState(state);
+ frame_data->pose = std::move(pose);
- base::TimeDelta time = base::TimeDelta::FromSecondsD(predicted_time);
+ // Update gamepad controllers.
+ UpdateControllerState();
- std::move(callback).Run(std::move(pose), time, frame,
- mojom::VRPresentationProvider::VSyncStatus::SUCCESS,
- base::nullopt);
+ std::move(callback).Run(std::move(frame_data));
}
std::vector<mojom::XRInputSourceStatePtr> OculusRenderLoop::GetInputState(
@@ -318,6 +404,31 @@ std::vector<mojom::XRInputSourceStatePtr> OculusRenderLoop::GetInputState(
return input_states;
}
+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));
+ }
+
+ 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);
+
+ main_thread_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(on_controller_updated_, input_touch, input_remote,
+ tracking, have_touch, have_remote));
+}
+
device::mojom::XRInputSourceStatePtr OculusRenderLoop::GetTouchData(
ovrControllerType type,
const ovrPoseStatef& pose,
@@ -341,7 +452,7 @@ device::mojom::XRInputSourceStatePtr OculusRenderLoop::GetTouchData(
device::mojom::XRInputSourceDescription::New();
// It's a handheld pointing device.
- desc->pointer_origin = device::mojom::XRPointerOrigin::HAND;
+ desc->target_ray_mode = device::mojom::XRTargetRayMode::POINTING;
// Set handedness.
switch (hand) {
diff --git a/chromium/device/vr/oculus/oculus_render_loop.h b/chromium/device/vr/oculus/oculus_render_loop.h
index af8a349f381..c8186b71aea 100644
--- a/chromium/device/vr/oculus/oculus_render_loop.h
+++ b/chromium/device/vr/oculus/oculus_render_loop.h
@@ -9,6 +9,7 @@
#include "base/threading/thread.h"
#include "build/build_config.h"
#include "device/vr/public/mojom/vr_service.mojom.h"
+#include "device/vr/vr_device.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "third_party/libovr/src/Include/OVR_CAPI.h"
@@ -24,14 +25,21 @@ const int kMaxOculusRenderLoopInputId = (ovrControllerType_Remote + 1);
class OculusRenderLoop : public base::Thread, mojom::VRPresentationProvider {
public:
- OculusRenderLoop(ovrSession session, ovrGraphicsLuid luid);
+ 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);
~OculusRenderLoop() override;
- void RequestPresent(
- mojom::VRSubmitFrameClientPtrInfo submit_client_info,
- mojom::VRPresentationProviderRequest request,
- device::mojom::VRRequestPresentOptionsPtr present_options,
- device::mojom::VRDisplayHost::RequestPresentCallback callback);
+ void RequestSession(mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ RequestSessionCallback callback);
void ExitPresent();
base::WeakPtr<OculusRenderLoop> GetWeakPtr();
@@ -49,13 +57,22 @@ class OculusRenderLoop : public base::Thread, mojom::VRPresentationProvider {
const gfx::RectF& left_bounds,
const gfx::RectF& right_bounds,
const gfx::Size& source_size) override;
- void GetVSync(GetVSyncCallback callback) override;
+ void GetFrameData(
+ mojom::VRPresentationProvider::GetFrameDataCallback callback) override;
private:
// base::Thread overrides:
void Init() override;
void CleanUp() override;
+ void ClearPendingFrame();
+ void StartOvrSession();
+ void StopOvrSession();
+ void CreateOvrSwapChain();
+ void DestroyOvrSwapChain();
+
+ void UpdateControllerState();
+
mojom::VRPosePtr GetPose();
std::vector<mojom::XRInputSourceStatePtr> GetInputState(
@@ -71,6 +88,9 @@ class OculusRenderLoop : public base::Thread, mojom::VRPresentationProvider {
D3D11TextureHelper texture_helper_;
#endif
+ base::OnceCallback<void()> delayed_get_frame_data_callback_;
+ bool has_outstanding_frame_ = false;
+
long long ovr_frame_index_ = 0;
int16_t next_frame_id_ = 0;
bool is_presenting_ = false;
@@ -79,14 +99,17 @@ class OculusRenderLoop : public base::Thread, mojom::VRPresentationProvider {
gfx::Size source_size_;
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
mojom::VRSubmitFrameClientPtr submit_client_;
- ovrSession session_;
- ovrGraphicsLuid luid_;
+ ovrSession session_ = nullptr;
+ ovrGraphicsLuid luid_ = {};
ovrPosef last_render_pose_;
ovrTextureSwapChain texture_swap_chain_ = 0;
double sensor_time_;
mojo::Binding<mojom::VRPresentationProvider> binding_;
- bool report_webxr_input_ = false;
bool primary_input_pressed[kMaxOculusRenderLoopInputId];
+ base::RepeatingCallback<void()> on_presentation_ended_;
+ base::RepeatingCallback<
+ void(ovrInputState, ovrInputState, ovrTrackingState, bool, bool)>
+ on_controller_updated_;
base::WeakPtrFactory<OculusRenderLoop> weak_ptr_factory_;
diff --git a/chromium/device/vr/openvr/OWNERS b/chromium/device/vr/openvr/OWNERS
index a1660982293..5b1397a961f 100644
--- a/chromium/device/vr/openvr/OWNERS
+++ b/chromium/device/vr/openvr/OWNERS
@@ -1,2 +1,5 @@
per-file *_type_converter*.*=set noparent
per-file *_type_converter*.*=file://ipc/SECURITY_OWNERS
+
+# TEAM: xr-dev@chromium.org
+# COMPONENT: Internals>XR>VR
diff --git a/chromium/device/vr/openvr/openvr_api_wrapper.cc b/chromium/device/vr/openvr/openvr_api_wrapper.cc
new file mode 100644
index 00000000000..98160a834e0
--- /dev/null
+++ b/chromium/device/vr/openvr/openvr_api_wrapper.cc
@@ -0,0 +1,113 @@
+// 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/openvr/openvr_api_wrapper.h"
+
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "device/vr/openvr/test/test_hook.h"
+
+namespace device {
+
+OpenVRWrapper::OpenVRWrapper(bool for_rendering) {
+ initialized_ = Initialize(for_rendering);
+}
+
+OpenVRWrapper::~OpenVRWrapper() {
+ if (initialized_)
+ Uninitialize();
+}
+
+vr::IVRCompositor* OpenVRWrapper::GetCompositor() {
+ DCHECK(current_task_runner_->BelongsToCurrentThread());
+ return compositor_;
+}
+
+vr::IVRSystem* OpenVRWrapper::GetSystem() {
+ DCHECK(current_task_runner_->BelongsToCurrentThread());
+ return system_;
+}
+
+void OpenVRWrapper::SetTestHook(OpenVRTestHook* hook) {
+ // This may be called from any thread - tests are responsible for
+ // maintaining thread safety, typically by not changing the test hook
+ // while presenting.
+ test_hook_ = hook;
+ if (test_hook_registration_) {
+ test_hook_registration_->SetTestHook(test_hook_);
+ }
+}
+
+bool OpenVRWrapper::Initialize(bool for_rendering) {
+ DCHECK(!any_initialized_);
+ any_initialized_ = true;
+
+ // device can only be used on this thread once initailized
+ vr::EVRInitError init_error = vr::VRInitError_None;
+ system_ =
+ vr::VR_Init(&init_error, vr::EVRApplicationType::VRApplication_Scene);
+
+ if (init_error != vr::VRInitError_None) {
+ LOG(ERROR) << vr::VR_GetVRInitErrorAsEnglishDescription(init_error);
+ any_initialized_ = false;
+ return false;
+ }
+
+ current_task_runner_ = base::ThreadTaskRunnerHandle::Get();
+
+ if (for_rendering) {
+ compositor_ = vr::VRCompositor();
+ }
+
+ if (test_hook_) {
+ // Allow our mock implementation of OpenVR to be controlled by tests.
+ // Note that SetTestHook must be called before CreateDevice, or
+ // test_hook_registration_s will remain null. This is a good pattern for
+ // tests anyway, since the alternative is we start mocking part-way through
+ // using the device, and end up with race conditions for when we started
+ // controlling things.
+ vr::EVRInitError eError;
+ test_hook_registration_ = reinterpret_cast<TestHookRegistration*>(
+ vr::VR_GetGenericInterface(kChromeOpenVRTestHookAPI, &eError));
+ if (test_hook_registration_) {
+ test_hook_registration_->SetTestHook(test_hook_);
+ }
+ }
+
+ return true;
+}
+
+void OpenVRWrapper::Uninitialize() {
+ DCHECK(initialized_);
+ initialized_ = false;
+ system_ = nullptr;
+ compositor_ = nullptr;
+ test_hook_registration_ = nullptr;
+ current_task_runner_ = nullptr;
+ vr::VR_Shutdown();
+
+ any_initialized_ = false;
+}
+
+OpenVRTestHook* OpenVRWrapper::test_hook_ = nullptr;
+bool OpenVRWrapper::any_initialized_ = false;
+TestHookRegistration* OpenVRWrapper::test_hook_registration_ = nullptr;
+
+std::string GetOpenVRString(vr::IVRSystem* vr_system,
+ vr::TrackedDeviceProperty prop) {
+ std::string out;
+
+ vr::TrackedPropertyError error = vr::TrackedProp_Success;
+ char openvr_string[vr::k_unMaxPropertyStringSize];
+ vr_system->GetStringTrackedDeviceProperty(
+ vr::k_unTrackedDeviceIndex_Hmd, prop, openvr_string,
+ vr::k_unMaxPropertyStringSize, &error);
+
+ if (error == vr::TrackedProp_Success)
+ out = openvr_string;
+
+ return out;
+}
+
+} // namespace device \ No newline at end of file
diff --git a/chromium/device/vr/openvr/openvr_api_wrapper.h b/chromium/device/vr/openvr/openvr_api_wrapper.h
new file mode 100644
index 00000000000..72751df6f9d
--- /dev/null
+++ b/chromium/device/vr/openvr/openvr_api_wrapper.h
@@ -0,0 +1,52 @@
+// 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_OPENVR_OPENVR_API_WRAPPER_H_
+#define DEVICE_VR_OPENVR_OPENVR_API_WRAPPER_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/openvr/src/headers/openvr.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace device {
+class OpenVRTestHook;
+class TestHookRegistration;
+
+class OpenVRWrapper {
+ public:
+ OpenVRWrapper(bool for_rendering);
+ ~OpenVRWrapper();
+
+ bool IsInitialized() { return initialized_; }
+
+ // Gets the OpenVR API objects.
+ // Ensures that they are used in a single thread at a time.
+ vr::IVRCompositor* GetCompositor();
+ vr::IVRSystem* GetSystem();
+
+ static void SetTestHook(OpenVRTestHook* hook);
+
+ private:
+ bool Initialize(bool for_rendering);
+ void Uninitialize();
+
+ vr::IVRSystem* system_ = nullptr;
+ vr::IVRCompositor* compositor_ = nullptr;
+ scoped_refptr<base::SingleThreadTaskRunner> current_task_runner_;
+ bool initialized_ = false;
+
+ static TestHookRegistration* test_hook_registration_;
+ static OpenVRTestHook* test_hook_;
+ static bool any_initialized_;
+};
+
+std::string GetOpenVRString(vr::IVRSystem* vr_system,
+ vr::TrackedDeviceProperty prop);
+
+} // namespace device
+
+#endif // DEVICE_VR_OPENVR_OPENVR_API_WRAPPER_H_ \ No newline at end of file
diff --git a/chromium/device/vr/openvr/openvr_device.cc b/chromium/device/vr/openvr/openvr_device.cc
index 8aee0efd125..cbab3b27b52 100644
--- a/chromium/device/vr/openvr/openvr_device.cc
+++ b/chromium/device/vr/openvr/openvr_device.cc
@@ -8,9 +8,9 @@
#include "base/macros.h"
#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_functions.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"
@@ -39,22 +39,6 @@ mojom::VRFieldOfViewPtr OpenVRFovToWebVRFov(vr::IVRSystem* vr_system,
return out;
}
-std::string GetOpenVRString(vr::IVRSystem* vr_system,
- vr::TrackedDeviceProperty prop) {
- std::string out;
-
- vr::TrackedPropertyError error = vr::TrackedProp_Success;
- char openvr_string[vr::k_unMaxPropertyStringSize];
- vr_system->GetStringTrackedDeviceProperty(
- vr::k_unTrackedDeviceIndex_Hmd, prop, openvr_string,
- vr::k_unMaxPropertyStringSize, &error);
-
- if (error == vr::TrackedProp_Success)
- out = openvr_string;
-
- return out;
-}
-
std::vector<float> HmdMatrix34ToWebVRTransformMatrix(
const vr::HmdMatrix34_t& mat) {
std::vector<float> transform;
@@ -144,18 +128,36 @@ mojom::VRDisplayInfoPtr CreateVRDisplayInfo(vr::IVRSystem* vr_system,
} // namespace
-OpenVRDevice::OpenVRDevice(vr::IVRSystem* vr)
- : vr_system_(vr),
+OpenVRDevice::OpenVRDevice()
+ : VRDeviceBase(VRDeviceId::OPENVR_DEVICE_ID),
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ exclusive_controller_binding_(this),
weak_ptr_factory_(this) {
- DCHECK(vr_system_);
- SetVRDisplayInfo(CreateVRDisplayInfo(vr_system_, GetId()));
+ // Initialize OpenVR.
+ openvr_ = std::make_unique<OpenVRWrapper>(false /* presenting */);
+ if (!openvr_->IsInitialized()) {
+ openvr_ = nullptr;
+ return;
+ }
- render_loop_ = std::make_unique<OpenVRRenderLoop>(vr);
+ SetVRDisplayInfo(CreateVRDisplayInfo(openvr_->GetSystem(), GetId()));
+
+ render_loop_ = std::make_unique<OpenVRRenderLoop>(base::BindRepeating(
+ &OpenVRDevice::OnGamepadUpdated, weak_ptr_factory_.GetWeakPtr()));
OnPollingEvents();
}
+void OpenVRDevice::OnGamepadUpdated(OpenVRGamepadState state) {
+ if (gamepad_data_fetcher_) {
+ gamepad_data_fetcher_->UpdateGamepadData(state);
+ }
+}
+
+void OpenVRDevice::RegisterDataFetcher(OpenVRGamepadDataFetcher* fetcher) {
+ gamepad_data_fetcher_ = fetcher;
+}
+
OpenVRDevice::~OpenVRDevice() {
Shutdown();
}
@@ -164,86 +166,119 @@ void OpenVRDevice::Shutdown() {
// Wait for the render loop to stop before completing destruction. This will
// ensure that the IVRSystem doesn't get shutdown until the render loop is no
// longer referencing it.
- if (render_loop_->IsRunning())
+ if (render_loop_ && render_loop_->IsRunning())
render_loop_->Stop();
}
-void OpenVRDevice::RequestPresent(
- mojom::VRSubmitFrameClientPtr submit_client,
- mojom::VRPresentationProviderRequest request,
- mojom::VRRequestPresentOptionsPtr present_options,
- mojom::VRDisplayHost::RequestPresentCallback callback) {
+void OpenVRDevice::RequestSession(
+ mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ mojom::XRRuntime::RequestSessionCallback callback) {
if (!render_loop_->IsRunning())
render_loop_->Start();
if (!render_loop_->IsRunning()) {
- std::move(callback).Run(false, nullptr);
+ std::move(callback).Run(nullptr, nullptr);
return;
}
+ // We are done using OpenVR until the presentation session ends.
+ openvr_ = nullptr;
+
auto my_callback =
- base::BindOnce(&OpenVRDevice::OnRequestPresentResult,
+ base::BindOnce(&OpenVRDevice::OnRequestSessionResult,
weak_ptr_factory_.GetWeakPtr(), std::move(callback));
+
+ auto on_presentation_ended = base::BindOnce(
+ &OpenVRDevice::OnPresentationEnded, weak_ptr_factory_.GetWeakPtr());
+
render_loop_->task_runner()->PostTask(
- FROM_HERE,
- base::BindOnce(&OpenVRRenderLoop::RequestPresent,
- render_loop_->GetWeakPtr(), submit_client.PassInterface(),
- std::move(request), std::move(present_options),
- std::move(my_callback)));
+ FROM_HERE, base::BindOnce(&OpenVRRenderLoop::RequestSession,
+ render_loop_->GetWeakPtr(),
+ std::move(on_presentation_ended),
+ std::move(options), std::move(my_callback)));
+}
+
+void OpenVRDevice::OnPresentationEnded() {
+ if (!openvr_) {
+ openvr_ = std::make_unique<OpenVRWrapper>(false /* presenting */);
+ if (!openvr_->IsInitialized()) {
+ openvr_ = nullptr;
+ return;
+ }
+ }
}
-void OpenVRDevice::OnRequestPresentResult(
- mojom::VRDisplayHost::RequestPresentCallback callback,
+void OpenVRDevice::OnRequestSessionResult(
+ mojom::XRRuntime::RequestSessionCallback callback,
bool result,
+ mojom::VRSubmitFrameClientRequest request,
+ mojom::VRPresentationProviderPtrInfo provider_info,
mojom::VRDisplayFrameTransportOptionsPtr transport_options) {
- std::move(callback).Run(result, std::move(transport_options));
-
- if (result) {
- using ViewerMap = std::map<std::string, VrViewerType>;
- CR_DEFINE_STATIC_LOCAL(
- ViewerMap, viewer_types,
- ({
- {"Oculus Rift CV1", VrViewerType::OPENVR_RIFT_CV1},
- {"Vive MV", VrViewerType::OPENVR_VIVE},
- }));
-
- VrViewerType type = VrViewerType::OPENVR_UNKNOWN;
- std::string model =
- GetOpenVRString(vr_system_, vr::Prop_ModelNumber_String);
- auto it = viewer_types.find(model);
- if (it != viewer_types.end())
- type = it->second;
-
- base::UmaHistogramSparse("VRViewerType", static_cast<int>(type));
+ if (!result) {
+ OnPresentationEnded();
+ std::move(callback).Run(nullptr, nullptr);
+ return;
}
+
+ 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));
+
+ // Use of Unretained is safe because the callback will only occur if the
+ // binding is not destroyed.
+ exclusive_controller_binding_.set_connection_error_handler(
+ base::BindOnce(&OpenVRDevice::OnPresentingControllerMojoConnectionError,
+ base::Unretained(this)));
+
+ std::move(callback).Run(std::move(connection), std::move(session_controller));
}
-void OpenVRDevice::ExitPresent() {
+// XRSessionController
+void OpenVRDevice::SetFrameDataRestricted(bool restricted) {
+ // Presentation sessions can not currently be restricted.
+ DCHECK(false);
+}
+
+void OpenVRDevice::OnPresentingControllerMojoConnectionError() {
render_loop_->task_runner()->PostTask(
FROM_HERE,
base::Bind(&OpenVRRenderLoop::ExitPresent, render_loop_->GetWeakPtr()));
render_loop_->Stop();
OnExitPresent();
+ exclusive_controller_binding_.Close();
}
-void OpenVRDevice::OnMagicWindowPoseRequest(
- mojom::VRMagicWindowProvider::GetPoseCallback callback) {
+void OpenVRDevice::OnMagicWindowFrameDataRequest(
+ mojom::VRPresentationProvider::GetFrameDataCallback callback) {
+ if (!openvr_) {
+ std::move(callback).Run(nullptr);
+ return;
+ }
+ const float kPredictionTimeSeconds = 0.03f;
vr::TrackedDevicePose_t rendering_poses[vr::k_unMaxTrackedDeviceCount];
- vr_system_->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseSeated, 0.03f,
- rendering_poses,
- vr::k_unMaxTrackedDeviceCount);
- std::move(callback).Run(mojo::ConvertTo<mojom::VRPosePtr>(
- rendering_poses[vr::k_unTrackedDeviceIndex_Hmd]));
+ openvr_->GetSystem()->GetDeviceToAbsoluteTrackingPose(
+ vr::TrackingUniverseSeated, kPredictionTimeSeconds, rendering_poses,
+ vr::k_unMaxTrackedDeviceCount);
+ mojom::XRFrameDataPtr data = mojom::XRFrameData::New();
+ data->pose = mojo::ConvertTo<mojom::VRPosePtr>(
+ rendering_poses[vr::k_unTrackedDeviceIndex_Hmd]);
+ std::move(callback).Run(std::move(data));
}
// Only deal with events that will cause displayInfo changes for now.
void OpenVRDevice::OnPollingEvents() {
- if (!vr_system_)
+ if (!openvr_)
return;
vr::VREvent_t event;
bool is_changed = false;
- while (vr_system_->PollNextEvent(&event, sizeof(event))) {
+ while (openvr_->GetSystem()->PollNextEvent(&event, sizeof(event))) {
if (event.trackedDeviceIndex != vr::k_unTrackedDeviceIndex_Hmd &&
event.trackedDeviceIndex != vr::k_unTrackedDeviceIndexInvalid) {
continue;
@@ -264,7 +299,7 @@ void OpenVRDevice::OnPollingEvents() {
}
if (is_changed)
- SetVRDisplayInfo(CreateVRDisplayInfo(vr_system_, GetId()));
+ SetVRDisplayInfo(CreateVRDisplayInfo(openvr_->GetSystem(), GetId()));
main_thread_task_runner_->PostDelayedTask(
FROM_HERE,
diff --git a/chromium/device/vr/openvr/openvr_device.h b/chromium/device/vr/openvr/openvr_device.h
index 3a19299352b..c5af92f2451 100644
--- a/chromium/device/vr/openvr/openvr_device.h
+++ b/chromium/device/vr/openvr/openvr_device.h
@@ -9,54 +9,64 @@
#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"
-namespace vr {
-class IVRSystem;
-} // namespace vr
-
namespace device {
class OpenVRRenderLoop;
+struct OpenVRGamepadState;
-class OpenVRDevice : public VRDeviceBase {
+class OpenVRDevice : public VRDeviceBase,
+ public mojom::XRSessionController,
+ public OpenVRGamepadDataProvider {
public:
- OpenVRDevice(vr::IVRSystem* vr);
+ OpenVRDevice();
~OpenVRDevice() override;
void Shutdown();
// VRDeviceBase
- void RequestPresent(
- mojom::VRSubmitFrameClientPtr submit_client,
- mojom::VRPresentationProviderRequest request,
- mojom::VRRequestPresentOptionsPtr present_options,
- mojom::VRDisplayHost::RequestPresentCallback callback) override;
- void ExitPresent() override;
+ void RequestSession(
+ mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ mojom::XRRuntime::RequestSessionCallback callback) override;
void OnPollingEvents();
- void OnRequestPresentResult(
- mojom::VRDisplayHost::RequestPresentCallback callback,
+ void OnRequestSessionResult(
+ mojom::XRRuntime::RequestSessionCallback callback,
bool result,
+ mojom::VRSubmitFrameClientRequest request,
+ mojom::VRPresentationProviderPtrInfo provider_info,
mojom::VRDisplayFrameTransportOptionsPtr transport_options);
+ bool IsInitialized() { return !!openvr_; }
+
private:
// VRDeviceBase
- void OnMagicWindowPoseRequest(
- mojom::VRMagicWindowProvider::GetPoseCallback callback) override;
+ void OnMagicWindowFrameDataRequest(
+ mojom::VRMagicWindowProvider::GetFrameDataCallback callback) override;
+
+ // XRSessionController
+ void SetFrameDataRestricted(bool restricted) override;
+
+ void OnPresentingControllerMojoConnectionError();
+ void OnPresentationEnded();
+
+ void RegisterDataFetcher(OpenVRGamepadDataFetcher*) override;
+ void OnGamepadUpdated(OpenVRGamepadState state);
- // TODO (BillOrr): This should not be a unique_ptr because the render_loop_
- // binds to VRVSyncProvider requests, so its lifetime should be tied to the
- // lifetime of that binding.
std::unique_ptr<OpenVRRenderLoop> render_loop_;
- mojom::VRSubmitFrameClientPtr submit_client_;
mojom::VRDisplayInfoPtr display_info_;
- vr::IVRSystem* vr_system_;
+ 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;
+
base::WeakPtrFactory<OpenVRDevice> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(OpenVRDevice);
diff --git a/chromium/device/vr/openvr/openvr_device_provider.cc b/chromium/device/vr/openvr/openvr_device_provider.cc
index b54b77b7dc9..2e1e55a7e73 100644
--- a/chromium/device/vr/openvr/openvr_device_provider.cc
+++ b/chromium/device/vr/openvr/openvr_device_provider.cc
@@ -6,8 +6,10 @@
#include "base/metrics/histogram_macros.h"
#include "device/gamepad/gamepad_data_fetcher_manager.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"
namespace device {
@@ -25,43 +27,44 @@ OpenVRDeviceProvider::OpenVRDeviceProvider() = default;
OpenVRDeviceProvider::~OpenVRDeviceProvider() {
device::GamepadDataFetcherManager::GetInstance()->RemoveSourceFactory(
device::GAMEPAD_SOURCE_OPENVR);
- // We must shutdown device_ and set it to null before calling VR_Shutdown,
- // because VR_Shutdown will unload OpenVR's dll, and device_ (or its render
- // loop) are potentially still using it.
+
if (device_) {
device_->Shutdown();
device_ = nullptr;
}
- vr::VR_Shutdown();
+ OpenVRWrapper::SetTestHook(nullptr);
}
void OpenVRDeviceProvider::Initialize(
- base::Callback<void(VRDevice*)> add_device_callback,
- base::Callback<void(VRDevice*)> remove_device_callback,
+ base::RepeatingCallback<void(unsigned int,
+ mojom::VRDisplayInfoPtr,
+ mojom::XRRuntimePtr)> add_device_callback,
+ base::RepeatingCallback<void(unsigned int)> remove_device_callback,
base::OnceClosure initialization_complete) {
CreateDevice();
if (device_)
- add_device_callback.Run(device_.get());
+ add_device_callback.Run(device_->GetId(), device_->GetVRDisplayInfo(),
+ device_->BindXRRuntimePtr());
initialized_ = true;
std::move(initialization_complete).Run();
}
+void OpenVRDeviceProvider::SetTestHook(OpenVRTestHook* test_hook) {
+ OpenVRWrapper::SetTestHook(test_hook);
+}
+
void OpenVRDeviceProvider::CreateDevice() {
if (!vr::VR_IsRuntimeInstalled() || !vr::VR_IsHmdPresent())
return;
- vr::EVRInitError init_error = vr::VRInitError_None;
- vr::IVRSystem* vr_system =
- vr::VR_Init(&init_error, vr::EVRApplicationType::VRApplication_Scene);
-
- if (init_error != vr::VRInitError_None) {
- LOG(ERROR) << vr::VR_GetVRInitErrorAsEnglishDescription(init_error);
- return;
+ device_ = std::make_unique<OpenVRDevice>();
+ if (device_->IsInitialized()) {
+ GamepadDataFetcherManager::GetInstance()->AddFactory(
+ new OpenVRGamepadDataFetcher::Factory(device_->GetId(), device_.get()));
+ } else {
+ device_ = nullptr;
}
- device_ = std::make_unique<OpenVRDevice>(vr_system);
- GamepadDataFetcherManager::GetInstance()->AddFactory(
- new OpenVRGamepadDataFetcher::Factory(device_->GetId(), vr_system));
}
bool OpenVRDeviceProvider::Initialized() {
diff --git a/chromium/device/vr/openvr/openvr_device_provider.h b/chromium/device/vr/openvr/openvr_device_provider.h
index 3d2ee82c251..7dc8229ddc4 100644
--- a/chromium/device/vr/openvr/openvr_device_provider.h
+++ b/chromium/device/vr/openvr/openvr_device_provider.h
@@ -15,20 +15,26 @@
namespace device {
class OpenVRDevice;
+class OpenVRTestHook;
class DEVICE_VR_EXPORT OpenVRDeviceProvider : public VRDeviceProvider {
public:
OpenVRDeviceProvider();
~OpenVRDeviceProvider() override;
- void Initialize(base::Callback<void(VRDevice*)> add_device_callback,
- base::Callback<void(VRDevice*)> remove_device_callback,
- base::OnceClosure initialization_complete) override;
+ void Initialize(
+ base::RepeatingCallback<void(unsigned int,
+ mojom::VRDisplayInfoPtr,
+ mojom::XRRuntimePtr)> add_device_callback,
+ base::RepeatingCallback<void(unsigned int)> remove_device_callback,
+ base::OnceClosure initialization_complete) override;
bool Initialized() override;
static void RecordRuntimeAvailability();
+ static void SetTestHook(OpenVRTestHook*);
+
private:
void CreateDevice();
diff --git a/chromium/device/vr/openvr/openvr_gamepad_data_fetcher.cc b/chromium/device/vr/openvr/openvr_gamepad_data_fetcher.cc
index b2b1ce42c63..0f301411ee0 100644
--- a/chromium/device/vr/openvr/openvr_gamepad_data_fetcher.cc
+++ b/chromium/device/vr/openvr/openvr_gamepad_data_fetcher.cc
@@ -35,8 +35,8 @@ void SetGamepadButton(Gamepad* pad,
} // namespace
OpenVRGamepadDataFetcher::Factory::Factory(unsigned int display_id,
- vr::IVRSystem* vr)
- : display_id_(display_id), vr_system_(vr) {
+ OpenVRGamepadDataProvider* provider)
+ : display_id_(display_id), provider_(provider) {
DVLOG(1) << __FUNCTION__ << "=" << this;
}
@@ -46,17 +46,21 @@ OpenVRGamepadDataFetcher::Factory::~Factory() {
std::unique_ptr<GamepadDataFetcher>
OpenVRGamepadDataFetcher::Factory::CreateDataFetcher() {
- return std::make_unique<OpenVRGamepadDataFetcher>(display_id_, vr_system_);
+ return std::make_unique<OpenVRGamepadDataFetcher>(display_id_, provider_);
}
GamepadSource OpenVRGamepadDataFetcher::Factory::source() {
return GAMEPAD_SOURCE_OPENVR;
}
-OpenVRGamepadDataFetcher::OpenVRGamepadDataFetcher(unsigned int display_id,
- vr::IVRSystem* vr)
- : display_id_(display_id), vr_system_(vr) {
+OpenVRGamepadDataFetcher::OpenVRGamepadDataFetcher(
+ unsigned int display_id,
+ OpenVRGamepadDataProvider* provider)
+ : display_id_(display_id) {
DVLOG(1) << __FUNCTION__ << "=" << this;
+
+ // Register for updates.
+ provider->RegisterDataFetcher(this);
}
OpenVRGamepadDataFetcher::~OpenVRGamepadDataFetcher() {
@@ -70,17 +74,11 @@ GamepadSource OpenVRGamepadDataFetcher::source() {
void OpenVRGamepadDataFetcher::OnAddedToProvider() {}
void OpenVRGamepadDataFetcher::GetGamepadData(bool devices_changed_hint) {
- if (!vr_system_)
- return;
-
- vr::TrackedDevicePose_t tracked_devices_poses[vr::k_unMaxTrackedDeviceCount];
- vr_system_->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseSeated, 0.0f,
- tracked_devices_poses,
- vr::k_unMaxTrackedDeviceCount);
+ 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 (vr_system_->GetTrackedDeviceClass(i) !=
- vr::TrackedDeviceClass_Controller)
+ if (data_.device_class[i] != vr::TrackedDeviceClass_Controller)
continue;
PadState* state = GetPadState(i);
@@ -89,10 +87,9 @@ void OpenVRGamepadDataFetcher::GetGamepadData(bool devices_changed_hint) {
Gamepad& pad = state->data;
- vr::VRControllerState_t controller_state;
- if (vr_system_->GetControllerState(i, &controller_state,
- sizeof(controller_state))) {
- pad.timestamp = controller_state.unPacketNum;
+ 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;
@@ -108,8 +105,7 @@ void OpenVRGamepadDataFetcher::GetGamepadData(bool devices_changed_hint) {
pad.display_id = display_id_;
- vr::ETrackedControllerRole hand =
- vr_system_->GetControllerRoleForTrackedDeviceIndex(i);
+ vr::ETrackedControllerRole hand = data_.hand[i];
switch (hand) {
case vr::TrackedControllerRole_Invalid:
@@ -123,16 +119,13 @@ void OpenVRGamepadDataFetcher::GetGamepadData(bool devices_changed_hint) {
break;
}
- uint64_t supported_buttons = vr_system_->GetUint64TrackedDeviceProperty(
- i, vr::Prop_SupportedButtons_Uint64);
+ 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 = vr_system_->GetInt32TrackedDeviceProperty(
- i, static_cast<vr::TrackedDeviceProperty>(vr::Prop_Axis0Type_Int32 +
- j));
+ int32_t axis_type = data_.axis_type[i][j];
switch (axis_type) {
case vr::k_eControllerAxis_Joystick:
case vr::k_eControllerAxis_TrackPad:
@@ -215,6 +208,11 @@ void OpenVRGamepadDataFetcher::GetGamepadData(bool devices_changed_hint) {
}
}
+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
index 37544a34e13..4df8cb612b1 100644
--- a/chromium/device/vr/openvr/openvr_gamepad_data_fetcher.h
+++ b/chromium/device/vr/openvr/openvr_gamepad_data_fetcher.h
@@ -6,28 +6,47 @@
#define DEVICE_VR_OPENVR_GAMEPAD_DATA_FETCHER_H_
#include "device/gamepad/gamepad_data_fetcher.h"
-
-namespace vr {
-class IVRSystem;
-} // namespace vr
+#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, vr::IVRSystem* vr);
+ Factory(unsigned int display_id, OpenVRGamepadDataProvider*);
~Factory() override;
std::unique_ptr<GamepadDataFetcher> CreateDataFetcher() override;
GamepadSource source() override;
private:
unsigned int display_id_;
- vr::IVRSystem* vr_system_;
+ OpenVRGamepadDataProvider* provider_;
};
- OpenVRGamepadDataFetcher(unsigned int display_id, vr::IVRSystem* vr);
+ OpenVRGamepadDataFetcher(unsigned int display_id, OpenVRGamepadDataProvider*);
~OpenVRGamepadDataFetcher() override;
GamepadSource source() override;
@@ -36,9 +55,15 @@ class OpenVRGamepadDataFetcher : public GamepadDataFetcher {
void PauseHint(bool paused) override;
void OnAddedToProvider() override;
+ void UpdateGamepadData(OpenVRGamepadState); // Called on UI thread.
+
private:
unsigned int display_id_;
- vr::IVRSystem* vr_system_;
+
+ // Protects access to data_, which is read/written on different threads.
+ base::Lock lock_;
+
+ OpenVRGamepadState data_;
DISALLOW_COPY_AND_ASSIGN(OpenVRGamepadDataFetcher);
};
diff --git a/chromium/device/vr/openvr/openvr_render_loop.cc b/chromium/device/vr/openvr/openvr_render_loop.cc
index 3cd7c8b4958..fa37e4db58e 100644
--- a/chromium/device/vr/openvr/openvr_render_loop.cc
+++ b/chromium/device/vr/openvr/openvr_render_loop.cc
@@ -4,6 +4,9 @@
#include "device/vr/openvr/openvr_render_loop.h"
+#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_type_converters.h"
#include "ui/gfx/geometry/angle_conversions.h"
#include "ui/gfx/transform.h"
@@ -42,10 +45,11 @@ gfx::Transform HmdMatrix34ToTransform(const vr::HmdMatrix34_t& mat) {
} // namespace
-OpenVRRenderLoop::OpenVRRenderLoop(vr::IVRSystem* vr)
+OpenVRRenderLoop::OpenVRRenderLoop(
+ base::RepeatingCallback<void(OpenVRGamepadState)> update_gamepad)
: base::Thread("OpenVRRenderLoop"),
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
- vr_system_(vr),
+ update_gamepad_(std::move(update_gamepad)),
binding_(this),
weak_ptr_factory_(this) {
DCHECK(main_thread_task_runner_);
@@ -55,10 +59,18 @@ OpenVRRenderLoop::~OpenVRRenderLoop() {
Stop();
}
+void OpenVRRenderLoop::ClearPendingFrame() {
+ has_outstanding_frame_ = false;
+ if (delayed_get_frame_data_callback_) {
+ base::ResetAndReturn(&delayed_get_frame_data_callback_).Run();
+ }
+}
+
void OpenVRRenderLoop::SubmitFrameMissing(int16_t frame_index,
const gpu::SyncToken& sync_token) {
// Nothing to do. It's OK to start the next frame even if the current
// one didn't get sent to OpenVR.
+ ClearPendingFrame();
}
void OpenVRRenderLoop::SubmitFrame(int16_t frame_index,
@@ -78,16 +90,21 @@ void OpenVRRenderLoop::SubmitFrameDrawnIntoTexture(
void OpenVRRenderLoop::SubmitFrameWithTextureHandle(
int16_t frame_index,
mojo::ScopedHandle texture_handle) {
+ DCHECK(openvr_);
+ vr::IVRCompositor* vr_compositor = openvr_->GetCompositor();
+ DCHECK(vr_compositor);
+
TRACE_EVENT1("gpu", "SubmitFrameWithTextureHandle", "frameIndex",
frame_index);
-
#if defined(OS_WIN)
MojoPlatformHandle platform_handle;
platform_handle.struct_size = sizeof(platform_handle);
MojoResult result = MojoUnwrapPlatformHandle(texture_handle.release().value(),
nullptr, &platform_handle);
- if (result != MOJO_RESULT_OK)
+ if (result != MOJO_RESULT_OK) {
+ ClearPendingFrame();
return;
+ }
texture_helper_.SetSourceTexture(
base::win::ScopedHandle(reinterpret_cast<HANDLE>(platform_handle.value)));
@@ -108,23 +125,25 @@ void OpenVRRenderLoop::SubmitFrameWithTextureHandle(
right_bounds_.height() + right_bounds_.y()};
vr::EVRCompositorError error =
- vr_compositor_->Submit(vr::EVREye::Eye_Left, &texture, &bounds[0]);
+ vr_compositor->Submit(vr::EVREye::Eye_Left, &texture, &bounds[0]);
if (error != vr::VRCompositorError_None) {
ExitPresent();
return;
}
- error = vr_compositor_->Submit(vr::EVREye::Eye_Right, &texture, &bounds[1]);
+ error = vr_compositor->Submit(vr::EVREye::Eye_Right, &texture, &bounds[1]);
if (error != vr::VRCompositorError_None) {
ExitPresent();
return;
}
- vr_compositor_->PostPresentHandoff();
+ vr_compositor->PostPresentHandoff();
}
// Tell WebVR that we are done with the texture.
submit_client_->OnSubmitFrameTransferred(copy_successful);
submit_client_->OnSubmitFrameRendered();
#endif
+
+ ClearPendingFrame();
}
void OpenVRRenderLoop::CleanUp() {
@@ -145,25 +164,44 @@ void OpenVRRenderLoop::UpdateLayerBounds(int16_t frame_id,
source_size_ = source_size;
};
-void OpenVRRenderLoop::RequestPresent(
- mojom::VRSubmitFrameClientPtrInfo submit_client_info,
- mojom::VRPresentationProviderRequest request,
- device::mojom::VRRequestPresentOptionsPtr present_options,
- device::mojom::VRDisplayHost::RequestPresentCallback callback) {
+void OpenVRRenderLoop::RequestSession(
+ base::OnceCallback<void()> on_presentation_ended,
+ mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ RequestSessionCallback callback) {
+ DCHECK(options->immersive);
+ 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));
+ return;
+ }
+
+ openvr_->GetCompositor()->SuspendRendering(true);
+ openvr_->GetCompositor()->SetTrackingSpace(
+ vr::ETrackingUniverseOrigin::TrackingUniverseSeated);
+ }
+
#if defined(OS_WIN)
int32_t adapter_index;
- vr::VRSystem()->GetDXGIOutputInfo(&adapter_index);
+ openvr_->GetSystem()->GetDXGIOutputInfo(&adapter_index);
if (!texture_helper_.SetAdapterIndex(adapter_index) ||
!texture_helper_.EnsureInitialized()) {
+ openvr_ = nullptr;
main_thread_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(std::move(callback), false, nullptr));
+ FROM_HERE,
+ base::BindOnce(std::move(callback), false, nullptr, nullptr, nullptr));
return;
}
#endif
- submit_client_.Bind(std::move(submit_client_info));
-
- binding_.Close();
- binding_.Bind(std::move(request));
+ 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();
@@ -173,89 +211,156 @@ void OpenVRRenderLoop::RequestPresent(
// able to safely ignore ones that our implementation doesn't care about.
transport_options->wait_for_transfer_notification = true;
- report_webxr_input_ = present_options->webxr_input;
- if (report_webxr_input_) {
- // Reset the active states for all the controllers.
- for (uint32_t i = 0; i < vr::k_unMaxTrackedDeviceCount; ++i) {
- InputActiveState& input_active_state = input_active_states_[i];
- input_active_state.active = false;
- input_active_state.primary_input_pressed = false;
- input_active_state.device_class = vr::TrackedDeviceClass_Invalid;
- input_active_state.controller_role = vr::TrackedControllerRole_Invalid;
- }
+ // Reset the active states for all the controllers.
+ for (uint32_t i = 0; i < vr::k_unMaxTrackedDeviceCount; ++i) {
+ InputActiveState& input_active_state = input_active_states_[i];
+ input_active_state.active = false;
+ input_active_state.primary_input_pressed = false;
+ input_active_state.device_class = vr::TrackedDeviceClass_Invalid;
+ input_active_state.controller_role = vr::TrackedControllerRole_Invalid;
}
main_thread_task_runner_->PostTask(
FROM_HERE,
- base::BindOnce(std::move(callback), true, std::move(transport_options)));
+ base::BindOnce(std::move(callback), true,
+ mojo::MakeRequest(&submit_client_),
+ provider.PassInterface(), std::move(transport_options)));
is_presenting_ = true;
- vr_compositor_->SuspendRendering(false);
+ openvr_->GetCompositor()->SuspendRendering(false);
+
+ // Measure the VrViewerType we are presenting with.
+ using ViewerMap = std::map<std::string, VrViewerType>;
+ CR_DEFINE_STATIC_LOCAL(ViewerMap, viewer_types,
+ ({
+ {"Oculus Rift CV1", VrViewerType::OPENVR_RIFT_CV1},
+ {"Vive MV", VrViewerType::OPENVR_VIVE},
+ }));
+ VrViewerType type = VrViewerType::OPENVR_UNKNOWN;
+ std::string model =
+ GetOpenVRString(openvr_->GetSystem(), vr::Prop_ModelNumber_String);
+ auto it = viewer_types.find(model);
+ if (it != viewer_types.end())
+ type = it->second;
+
+ base::UmaHistogramSparse("VRViewerType", static_cast<int>(type));
}
void OpenVRRenderLoop::ExitPresent() {
is_presenting_ = false;
- report_webxr_input_ = false;
binding_.Close();
submit_client_ = nullptr;
- vr_compositor_->SuspendRendering(true);
+ if (openvr_)
+ openvr_->GetCompositor()->SuspendRendering(true);
+ openvr_ = nullptr;
+
+ has_outstanding_frame_ = false;
+ delayed_get_frame_data_callback_.Reset();
+
+ // Push out one more controller update so we don't have stale controllers.
+ UpdateControllerState();
+
+ if (on_presentation_ended_) {
+ main_thread_task_runner_->PostTask(FROM_HERE,
+ std::move(on_presentation_ended_));
+ }
+}
+
+void OpenVRRenderLoop::UpdateControllerState() {
+ OpenVRGamepadState state = {};
+
+ 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));
+ }
+ }
+ }
+ }
+ }
+
+ main_thread_task_runner_->PostTask(FROM_HERE,
+ base::BindOnce(update_gamepad_, state));
}
mojom::VRPosePtr OpenVRRenderLoop::GetPose() {
vr::TrackedDevicePose_t rendering_poses[vr::k_unMaxTrackedDeviceCount];
TRACE_EVENT0("gpu", "WaitGetPoses");
- vr_compositor_->WaitGetPoses(rendering_poses, vr::k_unMaxTrackedDeviceCount,
- nullptr, 0);
+ openvr_->GetCompositor()->WaitGetPoses(
+ rendering_poses, vr::k_unMaxTrackedDeviceCount, nullptr, 0);
mojom::VRPosePtr pose = mojo::ConvertTo<mojom::VRPosePtr>(
rendering_poses[vr::k_unTrackedDeviceIndex_Hmd]);
// Update WebXR input sources.
- if (pose && report_webxr_input_) {
- pose->input_state =
- GetInputState(rendering_poses, vr::k_unMaxTrackedDeviceCount);
- }
+ DCHECK(pose);
+ pose->input_state =
+ GetInputState(rendering_poses, vr::k_unMaxTrackedDeviceCount);
return pose;
}
void OpenVRRenderLoop::Init() {
- vr_compositor_ = vr::VRCompositor();
- if (vr_compositor_ == nullptr) {
- DLOG(ERROR) << "Failed to initialize compositor.";
- return;
- }
-
- vr_compositor_->SuspendRendering(true);
- vr_compositor_->SetTrackingSpace(
- vr::ETrackingUniverseOrigin::TrackingUniverseSeated);
}
base::WeakPtr<OpenVRRenderLoop> OpenVRRenderLoop::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
-void OpenVRRenderLoop::GetVSync(
- mojom::VRPresentationProvider::GetVSyncCallback callback) {
+void OpenVRRenderLoop::GetFrameData(
+ mojom::VRPresentationProvider::GetFrameDataCallback callback) {
DCHECK(is_presenting_);
- int16_t frame = next_frame_id_;
+
+ if (has_outstanding_frame_) {
+ DCHECK(!delayed_get_frame_data_callback_);
+ delayed_get_frame_data_callback_ =
+ base::BindOnce(&OpenVRRenderLoop::GetFrameData, base::Unretained(this),
+ std::move(callback));
+ return;
+ }
+
+ has_outstanding_frame_ = true;
+
+ mojom::XRFrameDataPtr frame_data = mojom::XRFrameData::New();
+
+ frame_data->frame_id = next_frame_id_;
next_frame_id_ += 1;
if (next_frame_id_ < 0) {
next_frame_id_ = 0;
}
- mojom::VRPosePtr pose = GetPose();
+ if (openvr_) {
+ frame_data->pose = GetPose();
+ vr::Compositor_FrameTiming timing;
+ timing.m_nSize = sizeof(vr::Compositor_FrameTiming);
+ bool valid_time = openvr_->GetCompositor()->GetFrameTiming(&timing);
+ if (valid_time) {
+ frame_data->time_delta =
+ base::TimeDelta::FromSecondsD(timing.m_flSystemTimeInSeconds);
+ }
+ }
- vr::Compositor_FrameTiming timing;
- timing.m_nSize = sizeof(vr::Compositor_FrameTiming);
- bool valid_time = vr_compositor_->GetFrameTiming(&timing);
- base::TimeDelta time =
- valid_time ? base::TimeDelta::FromSecondsD(timing.m_flSystemTimeInSeconds)
- : base::TimeDelta();
+ // Update gamepad controllers.
+ UpdateControllerState();
- std::move(callback).Run(std::move(pose), time, frame,
- mojom::VRPresentationProvider::VSyncStatus::SUCCESS,
- base::nullopt);
+ std::move(callback).Run(std::move(frame_data));
}
std::vector<mojom::XRInputSourceStatePtr> OpenVRRenderLoop::GetInputState(
@@ -263,7 +368,7 @@ std::vector<mojom::XRInputSourceStatePtr> OpenVRRenderLoop::GetInputState(
uint32_t count) {
std::vector<mojom::XRInputSourceStatePtr> input_states;
- if (!vr_system_)
+ if (!openvr_)
return input_states;
// Loop through every device pose and determine which are controllers
@@ -287,7 +392,8 @@ std::vector<mojom::XRInputSourceStatePtr> OpenVRRenderLoop::GetInputState(
bool newly_active = false;
if (!input_active_state.active) {
input_active_state.active = true;
- input_active_state.device_class = vr_system_->GetTrackedDeviceClass(i);
+ input_active_state.device_class =
+ openvr_->GetSystem()->GetTrackedDeviceClass(i);
newly_active = true;
}
@@ -300,8 +406,8 @@ std::vector<mojom::XRInputSourceStatePtr> OpenVRRenderLoop::GetInputState(
device::mojom::XRInputSourceState::New();
vr::VRControllerState_t controller_state;
- vr_system_->GetControllerState(i, &controller_state,
- sizeof(vr::VRControllerState_t));
+ openvr_->GetSystem()->GetControllerState(i, &controller_state,
+ sizeof(vr::VRControllerState_t));
bool pressed = controller_state.ulButtonPressed &
vr::ButtonMaskFromId(vr::k_EButton_SteamVR_Trigger);
@@ -321,7 +427,7 @@ std::vector<mojom::XRInputSourceStatePtr> OpenVRRenderLoop::GetInputState(
// Poll controller roll per-frame, since OpenVR controllers can swap hands.
vr::ETrackedControllerRole controller_role =
- vr_system_->GetControllerRoleForTrackedDeviceIndex(i);
+ openvr_->GetSystem()->GetControllerRoleForTrackedDeviceIndex(i);
// If this is a newly active controller or if the handedness has changed
// since the last update, re-send the controller's description.
@@ -330,7 +436,7 @@ std::vector<mojom::XRInputSourceStatePtr> OpenVRRenderLoop::GetInputState(
device::mojom::XRInputSourceDescription::New();
// It's a handheld pointing device.
- desc->pointer_origin = device::mojom::XRPointerOrigin::HAND;
+ desc->target_ray_mode = device::mojom::XRTargetRayMode::POINTING;
// Set handedness.
switch (controller_role) {
diff --git a/chromium/device/vr/openvr/openvr_render_loop.h b/chromium/device/vr/openvr/openvr_render_loop.h
index d62abb8ffe1..df7fdc35e1f 100644
--- a/chromium/device/vr/openvr/openvr_render_loop.h
+++ b/chromium/device/vr/openvr/openvr_render_loop.h
@@ -10,6 +10,7 @@
#include "base/time/time.h"
#include "build/build_config.h"
#include "device/vr/public/mojom/vr_service.mojom.h"
+#include "device/vr/vr_device.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "third_party/openvr/src/headers/openvr.h"
@@ -21,16 +22,24 @@
namespace device {
+class OpenVRWrapper;
+struct OpenVRGamepadState;
+
class OpenVRRenderLoop : public base::Thread, mojom::VRPresentationProvider {
public:
- OpenVRRenderLoop(vr::IVRSystem* vr);
+ using RequestSessionCallback =
+ base::OnceCallback<void(bool result,
+ mojom::VRSubmitFrameClientRequest,
+ mojom::VRPresentationProviderPtrInfo,
+ mojom::VRDisplayFrameTransportOptionsPtr)>;
+
+ OpenVRRenderLoop(
+ base::RepeatingCallback<void(OpenVRGamepadState)> update_gamepad);
~OpenVRRenderLoop() override;
- void RequestPresent(
- mojom::VRSubmitFrameClientPtrInfo submit_client_info,
- mojom::VRPresentationProviderRequest request,
- device::mojom::VRRequestPresentOptionsPtr present_options,
- device::mojom::VRDisplayHost::RequestPresentCallback callback);
+ void RequestSession(base::OnceCallback<void()> on_presentation_ended,
+ mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ RequestSessionCallback callback);
void ExitPresent();
base::WeakPtr<OpenVRRenderLoop> GetWeakPtr();
@@ -48,13 +57,17 @@ class OpenVRRenderLoop : public base::Thread, mojom::VRPresentationProvider {
const gfx::RectF& left_bounds,
const gfx::RectF& right_bounds,
const gfx::Size& source_size) override;
- void GetVSync(GetVSyncCallback callback) override;
+ void GetFrameData(
+ VRPresentationProvider::GetFrameDataCallback callback) override;
private:
// base::Thread overrides:
void Init() override;
void CleanUp() override;
+ void ClearPendingFrame();
+ void UpdateControllerState();
+
mojom::VRPosePtr GetPose();
std::vector<mojom::XRInputSourceStatePtr> GetInputState(
vr::TrackedDevicePose_t* poses,
@@ -71,17 +84,20 @@ class OpenVRRenderLoop : public base::Thread, mojom::VRPresentationProvider {
D3D11TextureHelper texture_helper_;
#endif
+ base::OnceCallback<void()> delayed_get_frame_data_callback_;
+ bool has_outstanding_frame_ = false;
+
int16_t next_frame_id_ = 0;
bool is_presenting_ = false;
- bool report_webxr_input_ = false;
InputActiveState input_active_states_[vr::k_unMaxTrackedDeviceCount];
gfx::RectF left_bounds_;
gfx::RectF right_bounds_;
gfx::Size source_size_;
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
- vr::IVRSystem* vr_system_;
- vr::IVRCompositor* vr_compositor_;
mojom::VRSubmitFrameClientPtr submit_client_;
+ base::RepeatingCallback<void(OpenVRGamepadState)> update_gamepad_;
+ base::OnceCallback<void()> on_presentation_ended_;
+ std::unique_ptr<OpenVRWrapper> openvr_;
mojo::Binding<mojom::VRPresentationProvider> binding_;
base::WeakPtrFactory<OpenVRRenderLoop> weak_ptr_factory_;
diff --git a/chromium/device/vr/openvr/openvr_type_converters.cc b/chromium/device/vr/openvr/openvr_type_converters.cc
index 44c57b7f937..c4005e03113 100644
--- a/chromium/device/vr/openvr/openvr_type_converters.cc
+++ b/chromium/device/vr/openvr/openvr_type_converters.cc
@@ -10,6 +10,7 @@
#include "device/vr/public/mojom/vr_service.mojom.h"
#include "third_party/openvr/src/headers/openvr.h"
+#include "ui/gfx/transform_util.h"
namespace mojo {
@@ -22,11 +23,17 @@ TypeConverter<device::mojom::VRPosePtr, vr::TrackedDevicePose_t>::Convert(
if (hmd_pose.bPoseIsValid && hmd_pose.bDeviceIsConnected) {
const float(&m)[3][4] = hmd_pose.mDeviceToAbsoluteTracking.m;
- float w = sqrt(1 + m[0][0] + m[1][1] + m[2][2]) / 2;
- pose->orientation.value()[0] = (m[2][1] - m[1][2]) / (4 * w);
- pose->orientation.value()[1] = (m[0][2] - m[2][0]) / (4 * w);
- pose->orientation.value()[2] = (m[1][0] - m[0][1]) / (4 * w);
- pose->orientation.value()[3] = w;
+
+ gfx::Transform transform = gfx::Transform(
+ m[0][0], m[0][1], m[0][2], 0.0f, m[1][0], m[1][1], m[1][2], 0.0f,
+ m[2][0], m[2][1], m[2][2], 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
+ gfx::DecomposedTransform decomposed;
+ if (gfx::DecomposeTransform(&decomposed, transform)) {
+ pose->orientation.value()[0] = decomposed.quaternion.x();
+ pose->orientation.value()[1] = decomposed.quaternion.y();
+ pose->orientation.value()[2] = decomposed.quaternion.z();
+ pose->orientation.value()[3] = decomposed.quaternion.w();
+ }
pose->position.value()[0] = m[0][3];
pose->position.value()[1] = m[1][3];
diff --git a/chromium/device/vr/orientation/orientation_device.cc b/chromium/device/vr/orientation/orientation_device.cc
index 730814b5315..ee322ddddc6 100644
--- a/chromium/device/vr/orientation/orientation_device.cc
+++ b/chromium/device/vr/orientation/orientation_device.cc
@@ -52,7 +52,9 @@ display::Display::Rotation GetRotation() {
VROrientationDevice::VROrientationDevice(
mojom::SensorProviderPtr* sensor_provider,
base::OnceClosure ready_callback)
- : ready_callback_(std::move(ready_callback)), binding_(this) {
+ : VRDeviceBase(VRDeviceId::ORIENTATION_DEVICE_ID),
+ ready_callback_(std::move(ready_callback)),
+ binding_(this) {
(*sensor_provider)
->GetSensor(kOrientationSensorType,
base::BindOnce(&VROrientationDevice::SensorReady,
@@ -136,8 +138,17 @@ void VROrientationDevice::HandleSensorError() {
binding_.Close();
}
-void VROrientationDevice::OnMagicWindowPoseRequest(
- mojom::VRMagicWindowProvider::GetPoseCallback callback) {
+void VROrientationDevice::RequestSession(
+ mojom::XRDeviceRuntimeSessionOptionsPtr 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);
+}
+
+void VROrientationDevice::OnMagicWindowFrameDataRequest(
+ mojom::VRMagicWindowProvider::GetFrameDataCallback callback) {
mojom::VRPosePtr pose = mojom::VRPose::New();
pose->orientation.emplace(4);
@@ -158,7 +169,10 @@ void VROrientationDevice::OnMagicWindowPoseRequest(
pose->orientation.value()[2] = latest_pose_.z();
pose->orientation.value()[3] = latest_pose_.w();
- std::move(callback).Run(std::move(pose));
+ mojom::XRFrameDataPtr frame_data = mojom::XRFrameData::New();
+ frame_data->pose = std::move(pose);
+
+ std::move(callback).Run(std::move(frame_data));
}
Quaternion VROrientationDevice::SensorSpaceToWorldSpace(Quaternion q) {
@@ -204,8 +218,4 @@ Quaternion VROrientationDevice::WorldSpaceToUserOrientedSpace(Quaternion q) {
return q;
}
-bool VROrientationDevice::IsFallbackDevice() {
- return true;
-};
-
} // namespace device
diff --git a/chromium/device/vr/orientation/orientation_device.h b/chromium/device/vr/orientation/orientation_device.h
index dde90f77cbc..f9f3b19a41e 100644
--- a/chromium/device/vr/orientation/orientation_device.h
+++ b/chromium/device/vr/orientation/orientation_device.h
@@ -42,11 +42,14 @@ class DEVICE_VR_EXPORT VROrientationDevice : public VRDeviceBase,
base::OnceClosure ready_callback);
~VROrientationDevice() override;
- // VRDeviceBase
- void OnMagicWindowPoseRequest(
- mojom::VRMagicWindowProvider::GetPoseCallback callback) override;
+ // VRDevice
+ void RequestSession(
+ mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ mojom::XRRuntime::RequestSessionCallback callback) override;
- bool IsFallbackDevice() override;
+ // VRDeviceBase
+ void OnMagicWindowFrameDataRequest(
+ mojom::VRMagicWindowProvider::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 abb962bc10b..7f3271569be 100644
--- a/chromium/device/vr/orientation/orientation_device_provider.cc
+++ b/chromium/device/vr/orientation/orientation_device_provider.cc
@@ -21,11 +21,14 @@ VROrientationDeviceProvider::VROrientationDeviceProvider(
VROrientationDeviceProvider::~VROrientationDeviceProvider() = default;
void VROrientationDeviceProvider::Initialize(
- base::RepeatingCallback<void(VRDevice*)> add_device_callback,
- base::RepeatingCallback<void(VRDevice*)> remove_device_callback,
+ base::RepeatingCallback<void(unsigned int,
+ mojom::VRDisplayInfoPtr,
+ mojom::XRRuntimePtr)> add_device_callback,
+ base::RepeatingCallback<void(unsigned int)> remove_device_callback,
base::OnceClosure initialization_complete) {
if (device_ && device_->IsAvailable()) {
- add_device_callback.Run(device_.get());
+ add_device_callback.Run(device_->GetId(), device_->GetVRDisplayInfo(),
+ device_->BindXRRuntimePtr());
return;
}
@@ -51,7 +54,8 @@ void VROrientationDeviceProvider::DeviceInitialized() {
// If the device successfully connected to the orientation APIs, provide it.
if (device_->IsAvailable()) {
- add_device_callback_.Run(device_.get());
+ add_device_callback_.Run(device_->GetId(), device_->GetVRDisplayInfo(),
+ device_->BindXRRuntimePtr());
}
initialized_ = true;
diff --git a/chromium/device/vr/orientation/orientation_device_provider.h b/chromium/device/vr/orientation/orientation_device_provider.h
index 0d1e38c94a9..d7772325430 100644
--- a/chromium/device/vr/orientation/orientation_device_provider.h
+++ b/chromium/device/vr/orientation/orientation_device_provider.h
@@ -24,8 +24,10 @@ class DEVICE_VR_EXPORT VROrientationDeviceProvider : public VRDeviceProvider {
~VROrientationDeviceProvider() override;
void Initialize(
- base::RepeatingCallback<void(VRDevice*)> add_device_callback,
- base::RepeatingCallback<void(VRDevice*)> remove_device_callback,
+ base::RepeatingCallback<void(unsigned int,
+ mojom::VRDisplayInfoPtr,
+ mojom::XRRuntimePtr)> add_device_callback,
+ base::RepeatingCallback<void(unsigned int)> remove_device_callback,
base::OnceClosure initialization_complete) override;
bool Initialized() override;
@@ -39,7 +41,9 @@ class DEVICE_VR_EXPORT VROrientationDeviceProvider : public VRDeviceProvider {
std::unique_ptr<VROrientationDevice> device_;
- base::RepeatingCallback<void(VRDevice*)> add_device_callback_;
+ base::RepeatingCallback<
+ void(unsigned int, mojom::VRDisplayInfoPtr, mojom::XRRuntimePtr)>
+ add_device_callback_;
base::OnceClosure initialized_callback_;
DISALLOW_COPY_AND_ASSIGN(VROrientationDeviceProvider);
diff --git a/chromium/device/vr/orientation/orientation_device_provider_unittest.cc b/chromium/device/vr/orientation/orientation_device_provider_unittest.cc
index 3af37569e8f..bccfdc37de1 100644
--- a/chromium/device/vr/orientation/orientation_device_provider_unittest.cc
+++ b/chromium/device/vr/orientation/orientation_device_provider_unittest.cc
@@ -83,15 +83,34 @@ class VROrientationDeviceProviderTest : public testing::Test {
return init_params;
}
- base::RepeatingCallback<void(VRDevice*)> DeviceCallbackFailIfCalled() {
- return base::BindRepeating([](VRDevice* device) { FAIL(); });
+ base::RepeatingCallback<
+ void(unsigned int, mojom::VRDisplayInfoPtr, mojom::XRRuntimePtr device)>
+ DeviceAndIdCallbackFailIfCalled() {
+ return base::BindRepeating([](unsigned int id, mojom::VRDisplayInfoPtr,
+ mojom::XRRuntimePtr device) { FAIL(); });
};
- base::RepeatingCallback<void(VRDevice*)> DeviceCallbackMustBeCalled(
- base::RunLoop* loop) {
+ base::RepeatingCallback<void(unsigned int)> DeviceIdCallbackFailIfCalled() {
+ return base::BindRepeating([](unsigned int id) { FAIL(); });
+ };
+
+ base::RepeatingCallback<
+ void(unsigned int, mojom::VRDisplayInfoPtr, mojom::XRRuntimePtr device)>
+ DeviceAndIdCallbackMustBeCalled(base::RunLoop* loop) {
return base::BindRepeating(
- [](base::OnceClosure quit_closure, VRDevice* device) {
+ [](base::OnceClosure quit_closure, unsigned int id,
+ mojom::VRDisplayInfoPtr info, mojom::XRRuntimePtr device) {
ASSERT_TRUE(device);
+ ASSERT_TRUE(info);
+ std::move(quit_closure).Run();
+ },
+ loop->QuitClosure());
+ };
+
+ base::RepeatingCallback<void(unsigned int)> DeviceIdCallbackMustBeCalled(
+ base::RunLoop* loop) {
+ return base::BindRepeating(
+ [](base::OnceClosure quit_closure, unsigned int id) {
std::move(quit_closure).Run();
},
loop->QuitClosure());
@@ -135,8 +154,8 @@ TEST_F(VROrientationDeviceProviderTest, InitializationCallbackSuccessTest) {
base::RunLoop wait_for_device;
base::RunLoop wait_for_init;
- provider_->Initialize(DeviceCallbackMustBeCalled(&wait_for_device),
- DeviceCallbackFailIfCalled(),
+ provider_->Initialize(DeviceAndIdCallbackMustBeCalled(&wait_for_device),
+ DeviceIdCallbackFailIfCalled(),
ClosureMustBeCalled(&wait_for_init));
InitializeDevice(FakeInitParams());
@@ -150,8 +169,8 @@ TEST_F(VROrientationDeviceProviderTest, InitializationCallbackSuccessTest) {
TEST_F(VROrientationDeviceProviderTest, InitializationCallbackFailureTest) {
base::RunLoop wait_for_init;
- provider_->Initialize(DeviceCallbackFailIfCalled(),
- DeviceCallbackFailIfCalled(),
+ provider_->Initialize(DeviceAndIdCallbackFailIfCalled(),
+ DeviceIdCallbackFailIfCalled(),
ClosureMustBeCalled(&wait_for_init));
InitializeDevice(nullptr);
@@ -161,47 +180,4 @@ TEST_F(VROrientationDeviceProviderTest, InitializationCallbackFailureTest) {
EXPECT_TRUE(provider_->Initialized());
}
-TEST_F(VROrientationDeviceProviderTest, SecondInitializationSuccessTest) {
- base::RunLoop wait_for_device;
- base::RunLoop wait_for_init;
-
- provider_->Initialize(DeviceCallbackMustBeCalled(&wait_for_device),
- DeviceCallbackFailIfCalled(),
- ClosureMustBeCalled(&wait_for_init));
-
- InitializeDevice(FakeInitParams());
-
- // Wait for the initialization to finish.
- wait_for_init.Run();
- wait_for_device.Run();
-
- base::RunLoop second_wait_for_device;
-
- EXPECT_TRUE(provider_->Initialized());
-
- // If we run initialize again, we should only call add device.
- provider_->Initialize(DeviceCallbackMustBeCalled(&second_wait_for_device),
- DeviceCallbackFailIfCalled(), ClosureFailIfCalled());
-
- second_wait_for_device.Run();
-}
-
-TEST_F(VROrientationDeviceProviderTest, SecondInitializationFailureTest) {
- base::RunLoop wait_for_init;
-
- provider_->Initialize(DeviceCallbackFailIfCalled(),
- DeviceCallbackFailIfCalled(),
- ClosureMustBeCalled(&wait_for_init));
-
- InitializeDevice(nullptr);
-
- wait_for_init.Run();
-
- EXPECT_TRUE(provider_->Initialized());
-
- // If we call again on a failure, nothing should be called.
- provider_->Initialize(DeviceCallbackFailIfCalled(),
- DeviceCallbackFailIfCalled(), ClosureFailIfCalled());
-}
-
} // namespace device
diff --git a/chromium/device/vr/orientation/orientation_device_unittest.cc b/chromium/device/vr/orientation/orientation_device_unittest.cc
index db07c16bcfa..96cc3a81936 100644
--- a/chromium/device/vr/orientation/orientation_device_unittest.cc
+++ b/chromium/device/vr/orientation/orientation_device_unittest.cc
@@ -134,11 +134,11 @@ class VROrientationDeviceTest : public testing::Test {
base::RunLoop loop;
- device_->OnMagicWindowPoseRequest(base::BindOnce(
+ device_->OnMagicWindowFrameDataRequest(base::BindOnce(
[](base::OnceClosure quit_closure,
base::OnceCallback<void(mojom::VRPosePtr)> callback,
- mojom::VRPosePtr ptr) {
- std::move(callback).Run(std::move(ptr));
+ mojom::XRFrameDataPtr ptr) {
+ std::move(callback).Run(std::move(ptr->pose));
std::move(quit_closure).Run();
},
loop.QuitClosure(), std::move(callback)));
@@ -233,7 +233,7 @@ TEST_F(VROrientationDeviceTest, SensorIsAvailableTest) {
}
TEST_F(VROrientationDeviceTest, GetOrientationTest) {
- // Tests that OnMagicWindowPoseRequest returns a pose ptr without mishap.
+ // Tests that OnMagicWindowFrameDataRequest returns a pose ptr without mishap.
InitializeDevice(FakeInitParams());
diff --git a/chromium/device/vr/public/mojom/BUILD.gn b/chromium/device/vr/public/mojom/BUILD.gn
index c9befd61ef3..06cc4f2320d 100644
--- a/chromium/device/vr/public/mojom/BUILD.gn
+++ b/chromium/device/vr/public/mojom/BUILD.gn
@@ -9,6 +9,7 @@ mojom_component("mojom") {
macro_prefix = "DEVICE_VR_MOJO_BINDINGS"
sources = [
+ "isolated_xr_service.mojom",
"vr_service.mojom",
]
diff --git a/chromium/device/vr/public/mojom/OWNERS b/chromium/device/vr/public/mojom/OWNERS
index 08850f42120..fd66a0a6acf 100644
--- a/chromium/device/vr/public/mojom/OWNERS
+++ b/chromium/device/vr/public/mojom/OWNERS
@@ -1,2 +1,5 @@
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
+
+# TEAM: xr-dev@chromium.org
+# COMPONENT: Internals>XR
diff --git a/chromium/device/vr/public/mojom/README.md b/chromium/device/vr/public/mojom/README.md
new file mode 100644
index 00000000000..273b0d5e2da
--- /dev/null
+++ b/chromium/device/vr/public/mojom/README.md
@@ -0,0 +1,57 @@
+# Overview
+At a high level, AR/VR (collectively known as XR) APIs are wrapped in
+XRRuntimes.
+
+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.
+
+# 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).
+
+VRServiceClient - lives in the renderer process. Is notified when VRDisplays
+are connected.
+
+VRDisplayClient - lives in the renderer process. Is notified when display
+settings change.
+
+# Renderer <-> Device interfaces (defined in vr_service.mojom)
+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.
+
+VRSubmitFrameClient - lives in the renderer process. Is notified when various
+rendering events occur, so it can reclaim/reuse textures.
+
+## Magic-window related:
+Magic window is a mode where a site may request poses, but renders through the
+normal Chrome compositor pipeline.
+
+VRMagicWindowProvider - lives in the XRDevice process. Provides a way to obtain
+poses.
+
+# Browser <-> Device interfaces (defined in isolated_xr_service.mojom)
+The XRDevice process may be the browser process or an isolated service for
+different devices implementations. A device provider in the browser will choose
+to start the isolated device service when appropriate.
+
+XRRuntime - an abstraction over a XR API. Lives in the XRDevice process.
+Exposes a way for the browser to register for events, and start sessions (Magic
+Window or Presentation).
+
+XRSessionController - Lives in the XRDevice process. Allows the browser to
+pause or stop a session (MagicWindow or Presentation).
+
+XRRuntimeEventListener - Lives in the browser process. Exposes runtime events
+to the browser.
diff --git a/chromium/device/vr/public/mojom/isolated_xr_service.mojom b/chromium/device/vr/public/mojom/isolated_xr_service.mojom
new file mode 100644
index 00000000000..a1bcacda7c0
--- /dev/null
+++ b/chromium/device/vr/public/mojom/isolated_xr_service.mojom
@@ -0,0 +1,83 @@
+// 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.
+
+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.
+// Sessions are stopped by closing the mojo connection.
+interface XRSessionController {
+ // A session may be paused temporarily for example when a non-presenting
+ // tab loses focus. When paused, a session will hand out null poses.
+ // Eventually we may hand out poses at a throttled rate instead.
+ SetFrameDataRestricted(bool restricted);
+};
+
+// The XRRuntimeEventListener lives in the vr device service, and allows the
+// browser to listen to state changes about a device.
+interface XRRuntimeEventListener {
+ // A device has changed its display information.
+ OnDisplayInfoChanged(device.mojom.VRDisplayInfo display_info);
+
+ // A device has indicated that it is in use.
+ OnDeviceActivated(device.mojom.VRDisplayEventReason reason) =>
+ (bool will_not_present);
+
+ // A device has indicated that it is idle.
+ OnDeviceIdle(device.mojom.VRDisplayEventReason reason);
+
+ // Called when the device exits presentation.
+ OnExitPresent();
+};
+
+struct XRDeviceRuntimeSessionOptions {
+ bool immersive;
+ bool provide_passthrough_camera;
+
+ // The following options are used for permission requests.
+ // TODO(crbug.com/854655): remove these fields, and do permission checks in
+ // the browser process before calling out to devices.
+ int32 render_process_id;
+ int32 render_frame_id;
+
+ // A flag to indicate if there has been a user activation when the request
+ // session is made.
+ bool has_user_activation;
+
+ // This flag ensures that render path's that are only supported in WebXR are
+ // not used for WebVR 1.1.
+ bool use_legacy_webvr_render_path;
+};
+
+// 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.
+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,
+ 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) =>
+ (VRDisplayInfo display_info);
+
+ SetListeningForActivate(bool listen_for_activation);
+};
diff --git a/chromium/device/vr/public/mojom/vr_service.mojom b/chromium/device/vr/public/mojom/vr_service.mojom
index 1b5e4e5dd92..108c4291434 100644
--- a/chromium/device/vr/public/mojom/vr_service.mojom
+++ b/chromium/device/vr/public/mojom/vr_service.mojom
@@ -22,14 +22,40 @@ enum XRHandedness {
RIGHT = 2
};
-enum XRPointerOrigin {
- HEAD = 1,
- HAND = 2,
- SCREEN = 3
+enum XRTargetRayMode {
+ GAZING = 1,
+ POINTING = 2,
+ TAPPING = 3
+};
+
+struct XRSessionOptions {
+ bool immersive;
+ bool provide_passthrough_camera;
+
+ // A flag to indicate if there has been a user activation when the request
+ // session is made.
+ bool has_user_activation;
+
+ // This flag ensures that render paths that are only supported in WebXR are
+ // not used for WebVR 1.1.
+ bool use_legacy_webvr_render_path;
+};
+
+// TODO(offenwanger) Rearrange these two interfaces to merge duplicate
+// functionality.
+struct XRSession {
+ VRMagicWindowProvider? magic_window_provider;
+ XRPresentationConnection? connection;
+};
+
+struct XRPresentationConnection {
+ VRSubmitFrameClient& client_request;
+ VRPresentationProvider provider;
+ VRDisplayFrameTransportOptions transport_options;
};
struct XRInputSourceDescription {
- XRPointerOrigin pointer_origin;
+ XRTargetRayMode target_ray_mode;
XRHandedness handedness;
bool emulated_position;
@@ -85,10 +111,24 @@ struct VRPose {
// Indicates that a reset pose event was triggered, either by device specific
// UI or by some other method, and handled on the browser side, and the
- // renderer should now bubble up an event to the WebXRDevice API.
+ // renderer should now bubble up an event to the WebXR Device API.
bool pose_reset;
};
+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;
+};
+
+struct XRHitResult {
+ // A 4x4 transformation matrix representing the position of the object hit
+ // and the orientation of the normal of the object at the hit location.
+ // TODO(https://crbug.com/845293): use gfx.mojom.Transform.
+ array<float, 16> hit_matrix;
+};
+
struct VRDisplayCapabilities {
bool hasPosition;
bool hasExternalDisplay;
@@ -130,22 +170,6 @@ struct VRDisplayInfo {
float webxr_default_framebuffer_scale = 1.0;
};
-// Options supplied by the Renderer when requesting presentation.
-struct VRRequestPresentOptions {
- // If true, must use a render path that can preserve drawing buffer
- // contents across frames. If false, each frame is drawn independently.
- bool preserve_drawing_buffer;
-
- // If true, indicates that WebXR input poses should be reported on VSync.
- bool webxr_input;
-
- // If true, Renderer has support for using shared buffer draw. Currently true
- // for WebXR. Device-side prerequisite support is checked separately, this
- // flag is an opt-in to ensure it's not used for WebVR 1.1 which lacks
- // the required drawing buffer rebinding support.
- bool shared_buffer_draw_supported;
-};
-
// Frame transport method from the Renderer's point of view.
enum VRDisplayFrameTransportMethod {
NONE = 0,
@@ -172,20 +196,33 @@ struct VRDisplayFrameTransportOptions {
bool wait_for_gpu_fence;
};
-// The data needed for each frame for a magic window experience
-// that uses a background image / projection matrix instead of
-// just a VRPose - ex: non-exclusive AR needs a camera image and
-// to get a projection matrix directly from the backend rather than
-// FOV values to support features like focus.
-struct VRMagicWindowFrameData {
- VRPose pose;
- gpu.mojom.MailboxHolder buffer_holder;
- gfx.mojom.Size buffer_size;
+// The data needed for each animation frame of an XRSession.
+struct XRFrameData {
+ // General XRSession value
+
+ // The pose may be null if the device lost tracking. The XRFrameData can still
+ // have other data, such as pass through camera image.
+ VRPose? pose;
// TODO(https://crbug.com/838515): Is this delta since the last
// frame? OR an unspecified origin? Something else?
mojo_base.mojom.TimeDelta time_delta;
- array<float, 16> projection_matrix;
-
+ // The buffer_holder is used for sending data imagery back and forth across
+ // the process boundary. For application with pass through camera, it holds
+ // the camera image to be passed to the renderer. For immersive sessions, it
+ // is the place for the renderer to draw into to pass imagery to the device
+ // for rendering.
+ gpu.mojom.MailboxHolder? buffer_holder;
+
+ // Exclusive session values
+
+ // The frame_id maps frame data to a frame arriving from the compositor. IDs
+ // will be reused after the frame arrives from the compositor. Negative IDs
+ // imply no mapping.
+ int16 frame_id;
+
+ // Pass through camera values
+ gfx.mojom.Size? buffer_size;
+ array<float, 16>? projection_matrix;
};
enum VRDisplayEventReason {
@@ -208,8 +245,7 @@ interface VRService {
};
interface VRServiceClient {
- OnDisplayConnected(VRMagicWindowProvider magic_window_provider,
- VRDisplayHost display, VRDisplayClient& request,
+ OnDisplayConnected(VRDisplayHost display, VRDisplayClient& request,
VRDisplayInfo display_info);
};
@@ -250,14 +286,15 @@ interface VRSubmitFrameClient {
// Provides a communication channel from the renderer to the browser-side host
// of a (device/) VrDisplayImpl.
interface VRDisplayHost {
- // The returned transport_options is marked optional: it's null for
- // a failure result but must be non-null for a success result.
- RequestPresent(VRSubmitFrameClient client,
- VRPresentationProvider& request,
- VRRequestPresentOptions options,
- bool triggered_by_displayactive) => (
- bool success,
- VRDisplayFrameTransportOptions? transport_options);
+ // 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.
+ RequestSession(
+ XRSessionOptions options,
+ bool triggered_by_displayactive) => (XRSession? session);
+ SupportsSession(XRSessionOptions options) => (bool supports_session);
+
ExitPresent();
};
@@ -267,18 +304,30 @@ interface VRDisplayHost {
// 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 NonExclusiveWindowProvider or
+// TODO(836478): rename VRMagicWindowProvider to NonImmersiveWindowProvider or
// similar.
interface VRMagicWindowProvider {
- GetPose() => (VRPose? pose);
+ // 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.
- // TODO(https://crbug.com/837944): frame_data should not be optional.
- GetFrameData(gfx.mojom.Size frame_size,
- display.mojom.Rotation display_rotation) =>
- (VRMagicWindowFrameData? frame_data);
+ 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.
+ // 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
@@ -286,19 +335,15 @@ interface VRMagicWindowProvider {
// 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 {
- enum VSyncStatus { SUCCESS, CLOSING };
-
- // The frameId maps a VSync to a frame arriving from the compositor. IDs will
- // be reused after the frame arrives from the compositor. Negative IDs imply
- // no mapping.
- GetVSync() => (VRPose? pose, mojo_base.mojom.TimeDelta time, int16 frame_id,
- VSyncStatus status, gpu.mojom.MailboxHolder? buffer_holder);
+ // 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);
UpdateLayerBounds(int16 frame_id, gfx.mojom.RectF left_bounds,
gfx.mojom.RectF right_bounds, gfx.mojom.Size source_size);
// Call this if the animation loop exited without submitting a frame to
- // ensure that every GetVSync has a matching Submit call. This happens for
+ // 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
diff --git a/chromium/device/vr/vr_device.h b/chromium/device/vr/vr_device.h
index 792e923759a..aaa2294fa79 100644
--- a/chromium/device/vr/vr_device.h
+++ b/chromium/device/vr/vr_device.h
@@ -7,6 +7,7 @@
#include "base/callback.h"
#include "base/macros.h"
+#include "device/vr/public/mojom/isolated_xr_service.mojom.h"
#include "device/vr/public/mojom/vr_service.mojom.h"
#include "device/vr/vr_export.h"
@@ -26,6 +27,16 @@ 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 {
@@ -34,52 +45,6 @@ enum class XrRuntimeAvailable {
COUNT,
};
-const unsigned int VR_DEVICE_LAST_ID = 0xFFFFFFFF;
-
-class VRDeviceEventListener {
- public:
- virtual ~VRDeviceEventListener() {}
-
- virtual void OnChanged(mojom::VRDisplayInfoPtr vr_device_info) = 0;
- virtual void OnExitPresent() = 0;
- virtual void OnActivate(mojom::VRDisplayEventReason reason,
- base::OnceCallback<void(bool)> on_handled) = 0;
- virtual void OnDeactivate(mojom::VRDisplayEventReason reason) = 0;
-};
-
-// Represents one of the platform's VR devices. Owned by the respective
-// VRDeviceProvider.
-// TODO(mthiesse, crbug.com/769373): Remove DEVICE_VR_EXPORT.
-class DEVICE_VR_EXPORT VRDevice {
- public:
- virtual ~VRDevice() {}
-
- virtual unsigned int GetId() const = 0;
- virtual void PauseTracking() = 0;
- virtual void ResumeTracking() = 0;
- virtual mojom::VRDisplayInfoPtr GetVRDisplayInfo() = 0;
- virtual void SetMagicWindowEnabled(bool enabled) = 0;
- virtual void ExitPresent() = 0;
- virtual void RequestPresent(
- mojom::VRSubmitFrameClientPtr submit_client,
- mojom::VRPresentationProviderRequest request,
- mojom::VRRequestPresentOptionsPtr present_options,
- mojom::VRDisplayHost::RequestPresentCallback callback) = 0;
- virtual void SetListeningForActivate(bool is_listening) = 0;
-
- // The fallback device should only be provided in lieu of other devices.
- virtual bool IsFallbackDevice() = 0;
-
- // TODO(mthiesse): The browser should handle browser-side exiting of
- // presentation before device/ is even aware presentation is being exited.
- // Then the browser should call ExitPresent() on Device, which does device/
- // exiting of presentation before notifying displays. This is currently messy
- // because browser-side notions of presentation are mostly Android-specific.
- virtual void OnExitPresent() = 0;
-
- virtual void SetVRDeviceEventListener(VRDeviceEventListener* listener) = 0;
-};
-
} // namespace device
#endif // DEVICE_VR_VR_DEVICE_H
diff --git a/chromium/device/vr/vr_device_base.cc b/chromium/device/vr/vr_device_base.cc
index 9cf6c9b733a..1c7703ad6d3 100644
--- a/chromium/device/vr/vr_device_base.cc
+++ b/chromium/device/vr/vr_device_base.cc
@@ -9,13 +9,8 @@
namespace device {
-unsigned int VRDeviceBase::next_id_ = 1;
-
-VRDeviceBase::VRDeviceBase() : id_(next_id_) {
- // Prevent wraparound. Devices with this ID will be treated as invalid.
- if (next_id_ != VR_DEVICE_LAST_ID)
- next_id_++;
-}
+VRDeviceBase::VRDeviceBase(VRDeviceId id)
+ : id_(static_cast<unsigned int>(id)), runtime_binding_(this) {}
VRDeviceBase::~VRDeviceBase() = default;
@@ -27,79 +22,44 @@ void VRDeviceBase::PauseTracking() {}
void VRDeviceBase::ResumeTracking() {}
+mojom::VRDisplayInfoPtr VRDeviceBase::GetVRDisplayInfo() {
+ DCHECK(display_info_);
+ return display_info_.Clone();
+}
+
void VRDeviceBase::OnExitPresent() {
if (listener_)
listener_->OnExitPresent();
presenting_ = false;
}
-void VRDeviceBase::SetIsPresenting() {
+void VRDeviceBase::OnStartPresenting() {
presenting_ = true;
}
-bool VRDeviceBase::IsFallbackDevice() {
- return false;
-};
-
-mojom::VRDisplayInfoPtr VRDeviceBase::GetVRDisplayInfo() {
- DCHECK(display_info_);
- return display_info_.Clone();
-}
-
-void VRDeviceBase::RequestPresent(
- mojom::VRSubmitFrameClientPtr submit_client,
- mojom::VRPresentationProviderRequest request,
- mojom::VRRequestPresentOptionsPtr present_options,
- mojom::VRDisplayHost::RequestPresentCallback callback) {
- std::move(callback).Run(false, nullptr);
-}
-
-void VRDeviceBase::ExitPresent() {
- NOTREACHED();
+bool VRDeviceBase::HasExclusiveSession() {
+ return presenting_;
}
void VRDeviceBase::SetMagicWindowEnabled(bool enabled) {
magic_window_enabled_ = enabled;
}
-void VRDeviceBase::SetVRDeviceEventListener(VRDeviceEventListener* listener) {
- listener_ = listener;
-}
-
-void VRDeviceBase::GetMagicWindowPose(
- mojom::VRMagicWindowProvider::GetPoseCallback callback) {
- if (!magic_window_enabled_) {
- std::move(callback).Run(nullptr);
- return;
- }
-
- OnMagicWindowPoseRequest(std::move(callback));
+void VRDeviceBase::ListenToDeviceChanges(
+ mojom::XRRuntimeEventListenerPtr listener,
+ mojom::XRRuntime::ListenToDeviceChangesCallback callback) {
+ listener_ = std::move(listener);
+ std::move(callback).Run(display_info_.Clone());
}
-void VRDeviceBase::GetMagicWindowFrameData(
- const gfx::Size& frame_size,
- display::Display::Rotation display_rotation,
+void VRDeviceBase::GetFrameData(
mojom::VRMagicWindowProvider::GetFrameDataCallback callback) {
- // TODO(https://crbug.com/836565): rename this boolean.
if (!magic_window_enabled_) {
std::move(callback).Run(nullptr);
return;
}
- OnMagicWindowFrameDataRequest(frame_size, display_rotation,
- std::move(callback));
-}
-
-bool VRDeviceBase::IsAccessAllowed(VRDisplayImpl* display) {
- return !presenting_;
-}
-
-void VRDeviceBase::OnListeningForActivateChanged(VRDisplayImpl* display) {
- UpdateListeningForActivate(display);
-}
-
-void VRDeviceBase::OnFrameFocusChanged(VRDisplayImpl* display) {
- UpdateListeningForActivate(display);
+ OnMagicWindowFrameDataRequest(std::move(callback));
}
void VRDeviceBase::SetVRDisplayInfo(mojom::VRDisplayInfoPtr display_info) {
@@ -113,25 +73,28 @@ void VRDeviceBase::SetVRDisplayInfo(mojom::VRDisplayInfoPtr display_info) {
return;
if (listener_)
- listener_->OnChanged(display_info_.Clone());
+ listener_->OnDisplayInfoChanged(display_info_.Clone());
}
void VRDeviceBase::OnActivate(mojom::VRDisplayEventReason reason,
base::Callback<void(bool)> on_handled) {
if (listener_)
- listener_->OnActivate(reason, std::move(on_handled));
+ listener_->OnDeviceActivated(reason, std::move(on_handled));
}
-void VRDeviceBase::OnListeningForActivate(bool listening) {}
+mojom::XRRuntimePtr VRDeviceBase::BindXRRuntimePtr() {
+ mojom::XRRuntimePtr runtime;
+ runtime_binding_.Bind(mojo::MakeRequest(&runtime));
+ return runtime;
+}
-void VRDeviceBase::OnMagicWindowPoseRequest(
- mojom::VRMagicWindowProvider::GetPoseCallback callback) {
- std::move(callback).Run(nullptr);
+bool VRDeviceBase::ShouldPauseTrackingWhenFrameDataRestricted() {
+ return false;
}
+void VRDeviceBase::OnListeningForActivate(bool listening) {}
+
void VRDeviceBase::OnMagicWindowFrameDataRequest(
- const gfx::Size& frame_size,
- display::Display::Rotation display_rotation,
mojom::VRMagicWindowProvider::GetFrameDataCallback callback) {
std::move(callback).Run(nullptr);
}
@@ -140,16 +103,27 @@ void VRDeviceBase::SetListeningForActivate(bool is_listening) {
OnListeningForActivate(is_listening);
}
-void VRDeviceBase::UpdateListeningForActivate(VRDisplayImpl* display) {
- if (display->ListeningForActivate() && display->InFocusedFrame()) {
- bool was_listening = !!listening_for_activate_diplay_;
- listening_for_activate_diplay_ = display;
- if (!was_listening)
- OnListeningForActivate(true);
- } else if (listening_for_activate_diplay_ == display) {
- listening_for_activate_diplay_ = nullptr;
- OnListeningForActivate(false);
- }
+void VRDeviceBase::RequestHitTest(
+ mojom::XRRayPtr ray,
+ mojom::VRMagicWindowProvider::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;
+ 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));
+}
+
+void VRDeviceBase::EndMagicWindowSession(VRDisplayImpl* session) {
+ base::EraseIf(magic_window_sessions_,
+ [&](const std::unique_ptr<VRDisplayImpl>& item) {
+ return item.get() == session;
+ });
}
} // namespace device
diff --git a/chromium/device/vr/vr_device_base.h b/chromium/device/vr/vr_device_base.h
index e6aa60a6a2e..04f6c5066d6 100644
--- a/chromium/device/vr/vr_device_base.h
+++ b/chromium/device/vr/vr_device_base.h
@@ -10,6 +10,7 @@
#include "device/vr/public/mojom/vr_service.mojom.h"
#include "device/vr/vr_device.h"
#include "device/vr/vr_export.h"
+#include "mojo/public/cpp/bindings/binding.h"
#include "ui/display/display.h"
namespace device {
@@ -19,70 +20,84 @@ class VRDisplayImpl;
// Represents one of the platform's VR devices. Owned by the respective
// VRDeviceProvider.
// TODO(mthiesse, crbug.com/769373): Remove DEVICE_VR_EXPORT.
-class DEVICE_VR_EXPORT VRDeviceBase : public VRDevice {
+class DEVICE_VR_EXPORT VRDeviceBase : public mojom::XRRuntime {
public:
- VRDeviceBase();
+ explicit VRDeviceBase(VRDeviceId id);
~VRDeviceBase() override;
// VRDevice Implementation
- unsigned int GetId() const override;
- void PauseTracking() override;
- void ResumeTracking() override;
- void OnExitPresent() override;
- mojom::VRDisplayInfoPtr GetVRDisplayInfo() final;
- void SetMagicWindowEnabled(bool enabled) final;
- void SetVRDeviceEventListener(VRDeviceEventListener* listener) final;
-
- void RequestPresent(
- mojom::VRSubmitFrameClientPtr submit_client,
- mojom::VRPresentationProviderRequest request,
- mojom::VRRequestPresentOptionsPtr present_options,
- mojom::VRDisplayHost::RequestPresentCallback callback) override;
- void ExitPresent() override;
- bool IsFallbackDevice() override;
-
- bool IsAccessAllowed(VRDisplayImpl* display);
+ void ListenToDeviceChanges(
+ mojom::XRRuntimeEventListenerPtr listener,
+ mojom::XRRuntime::ListenToDeviceChangesCallback callback) final;
void SetListeningForActivate(bool is_listening) override;
- void OnListeningForActivateChanged(VRDisplayImpl* display);
- void OnFrameFocusChanged(VRDisplayImpl* display);
- void GetMagicWindowPose(
- mojom::VRMagicWindowProvider::GetPoseCallback callback);
- // TODO(https://crbug.com/836478): Rename this, and probably
- // GetMagicWindowPose to GetNonExclusiveFrameData.
- void GetMagicWindowFrameData(
- const gfx::Size& frame_size,
- display::Display::Rotation display_rotation,
+
+ void GetFrameData(
mojom::VRMagicWindowProvider::GetFrameDataCallback callback);
+ virtual void RequestHitTest(
+ mojom::XRRayPtr ray,
+ mojom::VRMagicWindowProvider::RequestHitTestCallback callback);
+ unsigned int GetId() const;
+
+ bool HasExclusiveSession();
+ void EndMagicWindowSession(VRDisplayImpl* session);
+
+ // TODO(https://crbug.com/845283): This method is a temporary solution
+ // until a XR related refactor lands. It allows to keep using the
+ // existing PauseTracking/ResumeTracking while not changing the
+ // existing VR functionality.
+ virtual bool ShouldPauseTrackingWhenFrameDataRestricted();
+
+ // Devices may be paused/resumed when focus changes by VRDisplayImpl or
+ // GVR delegate.
+ virtual void PauseTracking();
+ virtual void ResumeTracking();
+ void SetMagicWindowEnabled(bool enabled);
+
+ mojom::VRDisplayInfoPtr GetVRDisplayInfo();
+
+ // Used by providers to bind devices.
+ mojom::XRRuntimePtr BindXRRuntimePtr();
+
+ // TODO(mthiesse): The browser should handle browser-side exiting of
+ // presentation before device/ is even aware presentation is being exited.
+ // Then the browser should call StopSession() on Device, which does device/
+ // exiting of presentation before notifying displays. This is currently messy
+ // because browser-side notions of presentation are mostly Android-specific.
+ virtual void OnExitPresent();
+
protected:
- void SetIsPresenting();
+ // Devices tell VRDeviceBase when they start presenting. It will be paired
+ // with an OnExitPresent when the device stops presenting.
+ void OnStartPresenting();
bool IsPresenting() { return presenting_; } // Exposed for test.
void SetVRDisplayInfo(mojom::VRDisplayInfoPtr display_info);
void OnActivate(mojom::VRDisplayEventReason reason,
base::Callback<void(bool)> on_handled);
+ std::vector<std::unique_ptr<VRDisplayImpl>> magic_window_sessions_;
+
private:
- virtual void UpdateListeningForActivate(VRDisplayImpl* display);
+ // TODO(https://crbug.com/842227): Rename methods to HandleOnXXX
virtual void OnListeningForActivate(bool listening);
- virtual void OnMagicWindowPoseRequest(
- mojom::VRMagicWindowProvider::GetPoseCallback callback);
virtual void OnMagicWindowFrameDataRequest(
- const gfx::Size& frame_size,
- display::Display::Rotation display_rotation,
mojom::VRMagicWindowProvider::GetFrameDataCallback callback);
- VRDeviceEventListener* listener_ = nullptr;
+ // XRRuntime
+ void RequestMagicWindowSession(
+ mojom::XRRuntime::RequestMagicWindowSessionCallback callback) override;
- VRDisplayImpl* listening_for_activate_diplay_ = nullptr;
+ mojom::XRRuntimeEventListenerPtr listener_;
mojom::VRDisplayInfoPtr display_info_;
bool presenting_ = false;
unsigned int id_;
- static unsigned int next_id_;
bool magic_window_enabled_ = true;
+ mojo::Binding<mojom::XRRuntime> runtime_binding_;
+
DISALLOW_COPY_AND_ASSIGN(VRDeviceBase);
};
diff --git a/chromium/device/vr/vr_device_base_unittest.cc b/chromium/device/vr/vr_device_base_unittest.cc
index 29118faa614..5025d3ed0e3 100644
--- a/chromium/device/vr/vr_device_base_unittest.cc
+++ b/chromium/device/vr/vr_device_base_unittest.cc
@@ -21,7 +21,7 @@ namespace {
class VRDeviceBaseForTesting : public VRDeviceBase {
public:
- VRDeviceBaseForTesting() = default;
+ VRDeviceBaseForTesting() : VRDeviceBase(VRDeviceId::FAKE_DEVICE_ID) {}
~VRDeviceBaseForTesting() override = default;
void SetVRDisplayInfoForTest(mojom::VRDisplayInfoPtr display_info) {
@@ -34,6 +34,10 @@ class VRDeviceBaseForTesting : public VRDeviceBase {
bool ListeningForActivate() { return listening_for_activate; }
+ void RequestSession(
+ mojom::XRDeviceRuntimeSessionOptionsPtr options,
+ mojom::XRRuntime::RequestSessionCallback callback) override {}
+
private:
void OnListeningForActivate(bool listening) override {
listening_for_activate = listening;
@@ -44,23 +48,38 @@ class VRDeviceBaseForTesting : public VRDeviceBase {
DISALLOW_COPY_AND_ASSIGN(VRDeviceBaseForTesting);
};
-class StubVRDeviceEventListener : public VRDeviceEventListener {
+class StubVRDeviceEventListener : public mojom::XRRuntimeEventListener {
public:
+ StubVRDeviceEventListener() : binding_(this) {}
~StubVRDeviceEventListener() override {}
MOCK_METHOD1(DoOnChanged, void(mojom::VRDisplayInfo* vr_device_info));
- void OnChanged(mojom::VRDisplayInfoPtr vr_device_info) override {
+ void OnDisplayInfoChanged(mojom::VRDisplayInfoPtr vr_device_info) override {
DoOnChanged(vr_device_info.get());
}
- MOCK_METHOD2(OnActivate,
+ MOCK_METHOD2(DoOnDeviceActivated,
void(mojom::VRDisplayEventReason,
base::OnceCallback<void(bool)>));
+ void OnDeviceActivated(mojom::VRDisplayEventReason reason,
+ base::OnceCallback<void(bool)> callback) override {
+ DoOnDeviceActivated(reason, base::DoNothing());
+ // For now keep the test simple, and just call the callback:
+ std::move(callback).Run(true);
+ }
MOCK_METHOD0(OnExitPresent, void());
MOCK_METHOD0(OnBlur, void());
MOCK_METHOD0(OnFocus, void());
- MOCK_METHOD1(OnDeactivate, void(mojom::VRDisplayEventReason));
+ MOCK_METHOD1(OnDeviceIdle, void(mojom::VRDisplayEventReason));
+
+ mojom::XRRuntimeEventListenerPtr BindPtr() {
+ mojom::XRRuntimeEventListenerPtr ret;
+ binding_.Bind(mojo::MakeRequest(&ret));
+ return ret;
+ }
+
+ mojo::Binding<mojom::XRRuntimeEventListener> binding_;
};
} // namespace
@@ -77,9 +96,10 @@ class VRDeviceTest : public testing::Test {
}
std::unique_ptr<MockVRDisplayImpl> MakeMockDisplay(VRDeviceBase* device) {
- mojom::VRDisplayClientPtr display_client;
+ mojom::VRMagicWindowProviderPtr session;
+ mojom::XRSessionControllerPtr controller;
return std::make_unique<testing::NiceMock<MockVRDisplayImpl>>(
- device, client(), nullptr, nullptr, mojo::MakeRequest(&display_client),
+ device, mojo::MakeRequest(&session), mojo::MakeRequest(&controller),
false);
}
@@ -93,6 +113,7 @@ class VRDeviceTest : public testing::Test {
mojom::VRDisplayInfoPtr MakeVRDisplayInfo(unsigned int device_id) {
mojom::VRDisplayInfoPtr display_info = mojom::VRDisplayInfo::New();
display_info->index = device_id;
+ display_info->capabilities = mojom::VRDisplayCapabilities::New();
return display_info;
}
@@ -110,9 +131,12 @@ class VRDeviceTest : public testing::Test {
TEST_F(VRDeviceTest, DeviceChangedDispatched) {
auto device = MakeVRDevice();
StubVRDeviceEventListener listener;
- device->SetVRDeviceEventListener(&listener);
+ device->ListenToDeviceChanges(
+ listener.BindPtr(),
+ base::DoNothing()); // TODO: consider getting initial info
EXPECT_CALL(listener, DoOnChanged(testing::_)).Times(1);
device->SetVRDisplayInfoForTest(MakeVRDisplayInfo(device->GetId()));
+ base::RunLoop().RunUntilIdle();
}
TEST_F(VRDeviceTest, DisplayActivateRegsitered) {
@@ -120,25 +144,28 @@ TEST_F(VRDeviceTest, DisplayActivateRegsitered) {
device::mojom::VRDisplayEventReason::MOUNTED;
auto device = MakeVRDevice();
StubVRDeviceEventListener listener;
- device->SetVRDeviceEventListener(&listener);
+ device->ListenToDeviceChanges(
+ listener.BindPtr(),
+ base::DoNothing()); // TODO: consider getting initial data
EXPECT_FALSE(device->ListeningForActivate());
device->SetListeningForActivate(true);
EXPECT_TRUE(device->ListeningForActivate());
- EXPECT_CALL(listener, OnActivate(mounted, testing::_)).Times(1);
+ EXPECT_CALL(listener, DoOnDeviceActivated(mounted, testing::_)).Times(1);
device->FireDisplayActivate();
+ base::RunLoop().RunUntilIdle();
}
TEST_F(VRDeviceTest, NoMagicWindowPosesWhileBrowsing) {
- auto device = std::make_unique<FakeVRDevice>();
+ auto device = std::make_unique<FakeVRDevice>(1);
device->SetPose(mojom::VRPose::New());
- device->GetMagicWindowPose(
- base::BindOnce([](mojom::VRPosePtr pose) { EXPECT_TRUE(pose); }));
+ device->GetFrameData(base::BindOnce(
+ [](device::mojom::XRFrameDataPtr data) { EXPECT_TRUE(data); }));
device->SetMagicWindowEnabled(false);
- device->GetMagicWindowPose(
- base::BindOnce([](mojom::VRPosePtr pose) { EXPECT_FALSE(pose); }));
+ device->GetFrameData(base::BindOnce(
+ [](device::mojom::XRFrameDataPtr data) { EXPECT_FALSE(data); }));
}
} // namespace device
diff --git a/chromium/device/vr/vr_device_provider.h b/chromium/device/vr/vr_device_provider.h
index 8bf86944dc1..26732491088 100644
--- a/chromium/device/vr/vr_device_provider.h
+++ b/chromium/device/vr/vr_device_provider.h
@@ -8,11 +8,10 @@
#include <vector>
#include "base/callback.h"
+#include "device/vr/public/mojom/isolated_xr_service.mojom.h"
namespace device {
-class VRDevice;
-
class VRDeviceProvider {
public:
VRDeviceProvider() {}
@@ -20,8 +19,10 @@ class VRDeviceProvider {
// If the VR API requires initialization that should happen here.
virtual void Initialize(
- base::Callback<void(VRDevice*)> add_device_callback,
- base::Callback<void(VRDevice*)> remove_device_callback,
+ base::RepeatingCallback<void(unsigned int,
+ mojom::VRDisplayInfoPtr,
+ mojom::XRRuntimePtr)> add_device_callback,
+ base::RepeatingCallback<void(unsigned int)> 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 aba13dbb42b..b4e482b3de3 100644
--- a/chromium/device/vr/vr_display_impl.cc
+++ b/chromium/device/vr/vr_display_impl.cc
@@ -15,72 +15,78 @@ constexpr int kMaxImageHeightOrWidth = 8000;
namespace device {
-VRDisplayImpl::VRDisplayImpl(VRDevice* device,
- mojom::VRServiceClient* service_client,
- mojom::VRDisplayInfoPtr display_info,
- mojom::VRDisplayHostPtr display_host,
- mojom::VRDisplayClientRequest client_request,
- bool in_focused_frame)
- : binding_(this),
- device_(static_cast<VRDeviceBase*>(device)),
- in_focused_frame_(in_focused_frame) {
- mojom::VRMagicWindowProviderPtr magic_window_provider;
- binding_.Bind(mojo::MakeRequest(&magic_window_provider));
- service_client->OnDisplayConnected(
- std::move(magic_window_provider), std::move(display_host),
- std::move(client_request), std::move(display_info));
+VRDisplayImpl::VRDisplayImpl(
+ VRDeviceBase* device,
+ mojom::VRMagicWindowProviderRequest magic_window_request,
+ mojom::XRSessionControllerRequest session_request)
+ : magic_window_binding_(this),
+ session_controller_binding_(this),
+ 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(
+ &VRDisplayImpl::OnMojoConnectionError, base::Unretained(this)));
}
VRDisplayImpl::~VRDisplayImpl() = default;
-// Gets a pose for magic window sessions.
-void VRDisplayImpl::GetPose(GetPoseCallback callback) {
- if (!device_->IsAccessAllowed(this)) {
+// Gets frame data for sessions.
+void VRDisplayImpl::GetFrameData(
+ mojom::VRMagicWindowProvider::GetFrameDataCallback callback) {
+ if (device_->HasExclusiveSession() || restrict_frame_data_) {
std::move(callback).Run(nullptr);
return;
}
- device_->GetMagicWindowPose(std::move(callback));
-}
-// Gets frame image data for AR magic window sessions.
-void VRDisplayImpl::GetFrameData(const gfx::Size& frame_size,
- display::Display::Rotation rotation,
- GetFrameDataCallback callback) {
- if (!device_->IsAccessAllowed(this)) {
- std::move(callback).Run(nullptr);
- return;
- }
+ device_->GetFrameData(std::move(callback));
+}
+void VRDisplayImpl::UpdateSessionGeometry(const gfx::Size& frame_size,
+ display::Display::Rotation rotation) {
// Check for a valid frame size.
- // While Mojo should handle negative values, we also do not want to allow 0.
- // TODO(https://crbug.com/841062): Reconsider how we check the sizes.
+
+ // TODO(https://crbug.com/841062, https://crbug.com/836496): Reconsider how we
+ // check the sizes.
if (frame_size.width() <= 0 || frame_size.height() <= 0 ||
frame_size.width() > kMaxImageHeightOrWidth ||
frame_size.height() > kMaxImageHeightOrWidth) {
- DLOG(ERROR) << "Invalid frame size passed to GetFrameData().";
- std::move(callback).Run(nullptr);
+ DLOG(ERROR) << "Invalid frame size passed to UpdateSessionGeometry.";
return;
}
- device_->GetMagicWindowFrameData(frame_size, rotation, std::move(callback));
-}
-
-void VRDisplayImpl::SetListeningForActivate(bool listening) {
- listening_for_activate_ = listening;
- device_->OnListeningForActivateChanged(this);
+ session_frame_size_ = frame_size;
+ session_rotation_ = rotation;
}
-void VRDisplayImpl::SetInFocusedFrame(bool in_focused_frame) {
- in_focused_frame_ = in_focused_frame;
- device_->OnFrameFocusChanged(this);
+void VRDisplayImpl::RequestHitTest(
+ mojom::XRRayPtr ray,
+ mojom::VRMagicWindowProvider::RequestHitTestCallback callback) {
+ if (restrict_frame_data_) {
+ std::move(callback).Run(base::nullopt);
+ return;
+ }
+ device_->RequestHitTest(std::move(ray), std::move(callback));
}
-bool VRDisplayImpl::ListeningForActivate() {
- return listening_for_activate_;
+// XRSessionController
+void VRDisplayImpl::SetFrameDataRestricted(bool frame_data_restricted) {
+ restrict_frame_data_ = frame_data_restricted;
+ if (device_->ShouldPauseTrackingWhenFrameDataRestricted()) {
+ if (restrict_frame_data_) {
+ device_->PauseTracking();
+ } else {
+ device_->ResumeTracking();
+ }
+ }
}
-bool VRDisplayImpl::InFocusedFrame() {
- return in_focused_frame_;
+void VRDisplayImpl::OnMojoConnectionError() {
+ magic_window_binding_.Close();
+ session_controller_binding_.Close();
+ device_->EndMagicWindowSession(this); // This call will destroy us.
}
} // namespace device
diff --git a/chromium/device/vr/vr_display_impl.h b/chromium/device/vr/vr_display_impl.h
index 1474337810f..0d0bd8fd080 100644
--- a/chromium/device/vr/vr_display_impl.h
+++ b/chromium/device/vr/vr_display_impl.h
@@ -9,48 +9,55 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
+#include "device/vr/public/mojom/isolated_xr_service.mojom.h"
#include "device/vr/public/mojom/vr_service.mojom.h"
+#include "device/vr/vr_device.h"
#include "device/vr/vr_export.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "ui/display/display.h"
namespace device {
-class VRDevice;
class VRDeviceBase;
-// Browser process representation of a VRDevice within a WebVR site session
-// (see VRServiceImpl). VRDisplayImpl receives/sends VR device events
-// from/to mojom::VRDisplayClient (the render process representation of a VR
-// device).
-// VRDisplayImpl objects are owned by their respective VRServiceImpl instances.
-// TODO(mthiesse, crbug.com/769373): Remove DEVICE_VR_EXPORT.
-class DEVICE_VR_EXPORT VRDisplayImpl : public mojom::VRMagicWindowProvider {
+// VR device process implementation of a VRMagicWindowProvider 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 {
public:
- VRDisplayImpl(VRDevice* device,
- mojom::VRServiceClient* service_client,
- mojom::VRDisplayInfoPtr display_info,
- mojom::VRDisplayHostPtr display_host,
- mojom::VRDisplayClientRequest client_request,
- bool in_frame_focused);
+ VRDisplayImpl(VRDeviceBase* device,
+ mojom::VRMagicWindowProviderRequest,
+ mojom::XRSessionControllerRequest);
~VRDisplayImpl() override;
- void SetListeningForActivate(bool listening);
- void SetInFocusedFrame(bool in_focused_frame);
- virtual bool ListeningForActivate();
- virtual bool InFocusedFrame();
+ gfx::Size sessionFrameSize() { return session_frame_size_; };
+ display::Display::Rotation sessionRotation() { return session_rotation_; };
- private:
+ device::VRDeviceBase* device() { return device_; };
+
+ // Accessible to tests.
+ protected:
// mojom::VRMagicWindowProvider
- void GetPose(GetPoseCallback callback) override;
- void GetFrameData(const gfx::Size& frame_size,
- display::Display::Rotation rotation,
- GetFrameDataCallback callback) override;
+ void GetFrameData(GetFrameDataCallback callback) override;
+ void UpdateSessionGeometry(const gfx::Size& frame_size,
+ display::Display::Rotation rotation) override;
+ void RequestHitTest(mojom::XRRayPtr ray,
+ RequestHitTestCallback callback) override;
+
+ // mojom::XRSessionController
+ void SetFrameDataRestricted(bool paused) override;
- mojo::Binding<mojom::VRMagicWindowProvider> binding_;
+ void OnMojoConnectionError();
+
+ mojo::Binding<mojom::VRMagicWindowProvider> magic_window_binding_;
+ mojo::Binding<mojom::XRSessionController> session_controller_binding_;
device::VRDeviceBase* device_;
- bool listening_for_activate_ = false;
- bool in_focused_frame_ = false;
+ bool restrict_frame_data_ = true;
+
+ gfx::Size session_frame_size_ = gfx::Size(0, 0);
+ display::Display::Rotation session_rotation_ = display::Display::ROTATE_0;
};
} // namespace device
diff --git a/chromium/device/vr/vr_display_impl_unittest.cc b/chromium/device/vr/vr_display_impl_unittest.cc
index 0d90fb8e269..6735be754f8 100644
--- a/chromium/device/vr/vr_display_impl_unittest.cc
+++ b/chromium/device/vr/vr_display_impl_unittest.cc
@@ -22,38 +22,42 @@ class VRDisplayImplTest : public testing::Test {
~VRDisplayImplTest() override {}
void onDisplaySynced() {}
void onPresentComplete(
- bool success,
- device::mojom::VRDisplayFrameTransportOptionsPtr transport_options) {
- is_request_presenting_success_ = success;
+ 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>();
+ device_ = std::make_unique<FakeVRDevice>(1);
+ device_->SetPose(mojom::VRPose::New());
mojom::VRServiceClientPtr proxy;
client_ = std::make_unique<FakeVRServiceClient>(mojo::MakeRequest(&proxy));
}
- std::unique_ptr<VRDisplayImpl> MakeDisplay() {
- mojom::VRDisplayClientPtr display_client;
- return std::make_unique<VRDisplayImpl>(
- device(), client(), device()->GetVRDisplayInfo(), nullptr,
- mojo::MakeRequest(&display_client), false);
+ std::unique_ptr<VRDisplayImpl> MakeDisplay(
+ mojom::XRSessionControllerPtr* controller) {
+ mojom::VRMagicWindowProviderPtr session;
+ auto display = std::make_unique<VRDisplayImpl>(
+ device(), mojo::MakeRequest(&session), mojo::MakeRequest(controller));
+ static_cast<mojom::XRSessionController*>(display.get())
+ ->SetFrameDataRestricted(true);
+ return display;
}
- void RequestPresent(VRDisplayImpl* display_impl) {
- // TODO(klausw,mthiesse): set up a VRSubmitFrameClient here? Currently,
- // the FakeVRDisplay doesn't access the submit client, so a nullptr
- // is ok.
- device::mojom::VRSubmitFrameClientPtr submit_client = nullptr;
- device::mojom::VRPresentationProviderRequest request = nullptr;
- device_->RequestPresent(
- std::move(submit_client), std::move(request), nullptr,
+ void RequestSession(VRDisplayImpl* display_impl) {
+ device_->RequestSession(
+ mojom::XRDeviceRuntimeSessionOptionsPtr(),
base::BindOnce(&VRDisplayImplTest::onPresentComplete,
base::Unretained(this)));
}
- void ExitPresent() { device_->ExitPresent(); }
+ void ExitPresent() {
+ device_->StopSession();
+ immersive_session_controller_ = nullptr;
+ }
bool presenting() { return device_->IsPresenting(); }
VRDeviceBase* device() { return device_.get(); }
@@ -63,35 +67,77 @@ class VRDisplayImplTest : public testing::Test {
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);
};
TEST_F(VRDisplayImplTest, DevicePresentationIsolation) {
- std::unique_ptr<VRDisplayImpl> display_1 = MakeDisplay();
- std::unique_ptr<VRDisplayImpl> display_2 = MakeDisplay();
+ mojom::XRSessionControllerPtr controller1;
+ 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())
+ ->SetFrameDataRestricted(false);
// When not presenting either service should be able to access the device.
- EXPECT_TRUE(device()->IsAccessAllowed(display_1.get()));
- EXPECT_TRUE(device()->IsAccessAllowed(display_2.get()));
+ EXPECT_FALSE(device()->HasExclusiveSession());
+
+ bool was_called = false;
+ auto callback = [](bool expect_null, bool* was_called,
+ mojom::XRFrameDataPtr data) {
+ *was_called = true;
+ EXPECT_EQ(expect_null, !data);
+ };
+
+ static_cast<mojom::VRMagicWindowProvider*>(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())
+ ->GetFrameData(base::BindOnce(callback, false, &was_called));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(was_called);
+ was_called = false;
// Attempt to present.
- RequestPresent(display_1.get());
+ 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.
- EXPECT_FALSE(device()->IsAccessAllowed(display_1.get()));
- EXPECT_FALSE(device()->IsAccessAllowed(display_2.get()));
+ static_cast<mojom::VRMagicWindowProvider*>(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())
+ ->GetFrameData(base::BindOnce(callback, true, &was_called));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(was_called);
+ was_called = false;
// Service 1 should be able to exit the presentation it initiated.
ExitPresent();
EXPECT_FALSE(presenting());
+ EXPECT_FALSE(device()->HasExclusiveSession());
// Once presentation had ended both services should be able to access the
// device.
- EXPECT_TRUE(device()->IsAccessAllowed(display_1.get()));
- EXPECT_TRUE(device()->IsAccessAllowed(display_2.get()));
+ static_cast<mojom::VRMagicWindowProvider*>(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())
+ ->GetFrameData(base::BindOnce(callback, false, &was_called));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(was_called);
+ was_called = false;
}
}
diff --git a/chromium/device/vr/windows/d3d11_texture_helper.cc b/chromium/device/vr/windows/d3d11_texture_helper.cc
index 04be1536228..86766a48055 100644
--- a/chromium/device/vr/windows/d3d11_texture_helper.cc
+++ b/chromium/device/vr/windows/d3d11_texture_helper.cc
@@ -21,6 +21,10 @@ D3D11TextureHelper::D3D11TextureHelper() {}
D3D11TextureHelper::~D3D11TextureHelper() {}
+void D3D11TextureHelper::Reset() {
+ render_state_ = {};
+}
+
bool D3D11TextureHelper::CopyTextureToBackBuffer(bool flipY) {
if (!EnsureInitialized())
return false;
@@ -210,7 +214,7 @@ void D3D11TextureHelper::AllocateBackBuffer() {
D3D11_TEXTURE2D_DESC desc_target;
render_state_.target_texture_->GetDesc(&desc_target);
// If the target should change size, format, or other properties reallocate
- // a new target.
+ // a new texture and new render target view.
if (desc_source.Width != desc_target.Width ||
desc_source.Height != desc_target.Height ||
desc_source.MipLevels != desc_target.MipLevels ||
@@ -223,6 +227,7 @@ void D3D11TextureHelper::AllocateBackBuffer() {
desc_source.CPUAccessFlags != desc_target.CPUAccessFlags ||
desc_source.MiscFlags != desc_target.MiscFlags) {
render_state_.target_texture_ = nullptr;
+ render_state_.render_target_view_ = nullptr;
}
}
diff --git a/chromium/device/vr/windows/d3d11_texture_helper.h b/chromium/device/vr/windows/d3d11_texture_helper.h
index 61bdd4264df..146188cd982 100644
--- a/chromium/device/vr/windows/d3d11_texture_helper.h
+++ b/chromium/device/vr/windows/d3d11_texture_helper.h
@@ -19,6 +19,8 @@ class D3D11TextureHelper {
D3D11TextureHelper();
~D3D11TextureHelper();
+ void Reset();
+
bool EnsureInitialized();
bool SetAdapterIndex(int32_t index);
bool SetAdapterLUID(const LUID& luid);