diff options
Diffstat (limited to 'chromium/device')
199 files changed, 4353 insertions, 2398 deletions
diff --git a/chromium/device/BUILD.gn b/chromium/device/BUILD.gn index 8bf7a9d5186..3ddcd149be7 100644 --- a/chromium/device/BUILD.gn +++ b/chromium/device/BUILD.gn @@ -366,6 +366,7 @@ if (is_android) { ] generate_jni("bluetooth_test_jni_headers") { + testonly = true sources = bluetooth_java_sources_needing_jni } @@ -376,7 +377,7 @@ if (is_android) { "//base:base_java", "//components/location/android:location_java", "//device/bluetooth:java", - "//third_party/android_deps:com_android_support_support_annotations_java", + "//third_party/android_deps:androidx_annotation_annotation_java", "//third_party/android_sdk:android_test_mock_java", ] @@ -394,7 +395,7 @@ if (is_android) { "//base:base_junit_test_support", "//device/gamepad:java", "//mojo/public/java:bindings_java", - "//third_party/android_deps:com_android_support_support_annotations_java", + "//third_party/android_deps:androidx_annotation_annotation_java", ] srcjar_deps = [ "//device/gamepad:java_enums_srcjar" ] } diff --git a/chromium/device/base/features.cc b/chromium/device/base/features.cc index 1357a223a50..b50ee54a6ef 100644 --- a/chromium/device/base/features.cc +++ b/chromium/device/base/features.cc @@ -17,7 +17,8 @@ const base::Feature kNewBLEWinImplementation{"NewBLEWinImplementation", #if defined(OS_CHROMEOS) // Enables or disables the use of Bluetooth dispatcher daemon on Chrome OS. -const base::Feature kNewblueDaemon{"Newblue", base::FEATURE_ENABLED_BY_DEFAULT}; +const base::Feature kNewblueDaemon{"Newblue", + base::FEATURE_DISABLED_BY_DEFAULT}; #endif // defined(OS_CHROMEOS) #if BUILDFLAG(ENABLE_VR) diff --git a/chromium/device/bluetooth/BUILD.gn b/chromium/device/bluetooth/BUILD.gn index 5dfd6535122..7dcfcd0ab28 100644 --- a/chromium/device/bluetooth/BUILD.gn +++ b/chromium/device/bluetooth/BUILD.gn @@ -522,7 +522,9 @@ if (is_android) { java_files = java_sources_needing_jni deps = [ "//base:base_java", + "//base:jni_java", "//components/location/android:location_java", ] + annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] } } diff --git a/chromium/device/bluetooth/adapter.cc b/chromium/device/bluetooth/adapter.cc index dd2091cf5e3..5393ecc12e3 100644 --- a/chromium/device/bluetooth/adapter.cc +++ b/chromium/device/bluetooth/adapter.cc @@ -12,12 +12,13 @@ #include "device/bluetooth/device.h" #include "device/bluetooth/discovery_session.h" #include "device/bluetooth/public/mojom/connect_result_type_converter.h" -#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/self_owned_receiver.h" namespace bluetooth { Adapter::Adapter(scoped_refptr<device::BluetoothAdapter> adapter) - : adapter_(std::move(adapter)), client_(nullptr) { + : adapter_(std::move(adapter)) { adapter_->AddObserver(this); } @@ -32,7 +33,7 @@ void Adapter::ConnectToDevice(const std::string& address, if (!device) { std::move(callback).Run(mojom::ConnectResult::DEVICE_NO_LONGER_IN_RANGE, - nullptr /* device */); + /* device */ mojo::NullRemote()); return; } @@ -68,8 +69,8 @@ void Adapter::GetInfo(GetInfoCallback callback) { std::move(callback).Run(std::move(adapter_info)); } -void Adapter::SetClient(mojom::AdapterClientPtr client) { - client_ = std::move(client); +void Adapter::SetClient(mojo::PendingRemote<mojom::AdapterClient> client) { + client_.Bind(std::move(client)); } void Adapter::StartDiscoverySession(StartDiscoverySessionCallback callback) { @@ -132,31 +133,31 @@ void Adapter::DeviceRemoved(device::BluetoothAdapter* adapter, void Adapter::OnGattConnected( ConnectToDeviceCallback callback, std::unique_ptr<device::BluetoothGattConnection> connection) { - mojom::DevicePtr device_ptr; + mojo::PendingRemote<mojom::Device> device; Device::Create(adapter_, std::move(connection), - mojo::MakeRequest(&device_ptr)); - std::move(callback).Run(mojom::ConnectResult::SUCCESS, std::move(device_ptr)); + device.InitWithNewPipeAndPassReceiver()); + std::move(callback).Run(mojom::ConnectResult::SUCCESS, std::move(device)); } void Adapter::OnConnectError( ConnectToDeviceCallback callback, device::BluetoothDevice::ConnectErrorCode error_code) { std::move(callback).Run(mojo::ConvertTo<mojom::ConnectResult>(error_code), - nullptr /* Device */); + /* device */ mojo::NullRemote()); } void Adapter::OnStartDiscoverySession( StartDiscoverySessionCallback callback, std::unique_ptr<device::BluetoothDiscoverySession> session) { - mojom::DiscoverySessionPtr session_ptr; - mojo::MakeStrongBinding( + mojo::PendingRemote<mojom::DiscoverySession> pending_session; + mojo::MakeSelfOwnedReceiver( std::make_unique<DiscoverySession>(std::move(session)), - mojo::MakeRequest(&session_ptr)); - std::move(callback).Run(std::move(session_ptr)); + pending_session.InitWithNewPipeAndPassReceiver()); + std::move(callback).Run(std::move(pending_session)); } void Adapter::OnDiscoverySessionError(StartDiscoverySessionCallback callback) { - std::move(callback).Run(nullptr /* session */); + std::move(callback).Run(mojo::NullRemote() /* session */); } } // namespace bluetooth diff --git a/chromium/device/bluetooth/adapter.h b/chromium/device/bluetooth/adapter.h index 743707ee591..d885cdae99b 100644 --- a/chromium/device/bluetooth/adapter.h +++ b/chromium/device/bluetooth/adapter.h @@ -14,6 +14,8 @@ #include "device/bluetooth/bluetooth_gatt_connection.h" #include "device/bluetooth/public/mojom/adapter.mojom.h" #include "device/bluetooth/public/mojom/device.mojom.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/remote.h" namespace bluetooth { @@ -32,7 +34,7 @@ class Adapter : public mojom::Adapter, ConnectToDeviceCallback callback) override; void GetDevices(GetDevicesCallback callback) override; void GetInfo(GetInfoCallback callback) override; - void SetClient(mojom::AdapterClientPtr client) override; + void SetClient(mojo::PendingRemote<mojom::AdapterClient> client) override; void StartDiscoverySession(StartDiscoverySessionCallback callback) override; // device::BluetoothAdapter::Observer overrides: @@ -69,7 +71,7 @@ class Adapter : public mojom::Adapter, scoped_refptr<device::BluetoothAdapter> adapter_; // The adapter client that listens to this service. - mojom::AdapterClientPtr client_; + mojo::Remote<mojom::AdapterClient> client_; base::WeakPtrFactory<Adapter> weak_ptr_factory_{this}; diff --git a/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothAdapter.java b/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothAdapter.java index b6067bfb59b..d1068444ed8 100644 --- a/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothAdapter.java +++ b/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothAdapter.java @@ -19,6 +19,7 @@ import org.chromium.base.Log; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNIAdditionalImport; import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.NativeMethods; import org.chromium.components.location.LocationUtils; import java.util.List; @@ -288,7 +289,8 @@ final class ChromeBluetoothAdapter extends BroadcastReceiver { // Object can be destroyed, but Android keeps calling onScanResult. if (mNativeBluetoothAdapterAndroid != 0) { - nativeCreateOrUpdateDeviceOnScan(mNativeBluetoothAdapterAndroid, + ChromeBluetoothAdapterJni.get().createOrUpdateDeviceOnScan( + mNativeBluetoothAdapterAndroid, ChromeBluetoothAdapter.this, result.getDevice().getAddress(), result.getDevice(), result.getScanRecord_getDeviceName(), result.getRssi(), uuid_strings, result.getScanRecord_getTxPowerLevel(), serviceDataKeys, serviceDataValues, @@ -299,7 +301,8 @@ final class ChromeBluetoothAdapter extends BroadcastReceiver { @Override public void onScanFailed(int errorCode) { Log.w(TAG, "onScanFailed: %d", errorCode); - nativeOnScanFailed(mNativeBluetoothAdapterAndroid); + ChromeBluetoothAdapterJni.get().onScanFailed( + mNativeBluetoothAdapterAndroid, ChromeBluetoothAdapter.this); } } @@ -315,10 +318,12 @@ final class ChromeBluetoothAdapter extends BroadcastReceiver { switch (state) { case BluetoothAdapter.STATE_ON: - nativeOnAdapterStateChanged(mNativeBluetoothAdapterAndroid, true); + ChromeBluetoothAdapterJni.get().onAdapterStateChanged( + mNativeBluetoothAdapterAndroid, ChromeBluetoothAdapter.this, true); break; case BluetoothAdapter.STATE_OFF: - nativeOnAdapterStateChanged(mNativeBluetoothAdapterAndroid, false); + ChromeBluetoothAdapterJni.get().onAdapterStateChanged( + mNativeBluetoothAdapterAndroid, ChromeBluetoothAdapter.this, false); break; default: // do nothing @@ -342,20 +347,21 @@ final class ChromeBluetoothAdapter extends BroadcastReceiver { } } - // --------------------------------------------------------------------------------------------- - // BluetoothAdapterAndroid C++ methods declared for access from java: - - // Binds to BluetoothAdapterAndroid::OnScanFailed. - private native void nativeOnScanFailed(long nativeBluetoothAdapterAndroid); - - // Binds to BluetoothAdapterAndroid::CreateOrUpdateDeviceOnScan. - private native void nativeCreateOrUpdateDeviceOnScan(long nativeBluetoothAdapterAndroid, - String address, Wrappers.BluetoothDeviceWrapper deviceWrapper, String localName, - int rssi, String[] advertisedUuids, int txPower, String[] serviceDataKeys, - Object[] serviceDataValues, int[] manufacturerDataKeys, - Object[] manufacturerDataValues); - - // Binds to BluetoothAdapterAndroid::nativeOnAdapterStateChanged - private native void nativeOnAdapterStateChanged( - long nativeBluetoothAdapterAndroid, boolean powered); + @NativeMethods + interface Natives { + // Binds to BluetoothAdapterAndroid::OnScanFailed. + void onScanFailed(long nativeBluetoothAdapterAndroid, ChromeBluetoothAdapter caller); + + // Binds to BluetoothAdapterAndroid::CreateOrUpdateDeviceOnScan. + void createOrUpdateDeviceOnScan(long nativeBluetoothAdapterAndroid, + ChromeBluetoothAdapter caller, String address, + Wrappers.BluetoothDeviceWrapper deviceWrapper, String localName, int rssi, + String[] advertisedUuids, int txPower, String[] serviceDataKeys, + Object[] serviceDataValues, int[] manufacturerDataKeys, + Object[] manufacturerDataValues); + + // Binds to BluetoothAdapterAndroid::nativeOnAdapterStateChanged + void onAdapterStateChanged( + long nativeBluetoothAdapterAndroid, ChromeBluetoothAdapter caller, boolean powered); + } } diff --git a/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java b/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java index da726ec8d0e..e134b66cf13 100644 --- a/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java +++ b/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java @@ -13,6 +13,7 @@ import org.chromium.base.Log; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNIAdditionalImport; import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.NativeMethods; import org.chromium.base.metrics.RecordHistogram; import java.util.HashMap; @@ -149,7 +150,8 @@ final class ChromeBluetoothDevice { status); } if (mNativeBluetoothDeviceAndroid != 0) { - nativeOnConnectionStateChange(mNativeBluetoothDeviceAndroid, status, + ChromeBluetoothDeviceJni.get().onConnectionStateChange( + mNativeBluetoothDeviceAndroid, ChromeBluetoothDevice.this, status, newState == android.bluetooth.BluetoothProfile.STATE_CONNECTED); } } @@ -185,10 +187,12 @@ final class ChromeBluetoothDevice { // between service instances with the same UUID on this device. String serviceInstanceId = getAddress() + "/" + service.getUuid().toString() + "," + service.getInstanceId(); - nativeCreateGattRemoteService( - mNativeBluetoothDeviceAndroid, serviceInstanceId, service); + ChromeBluetoothDeviceJni.get().createGattRemoteService( + mNativeBluetoothDeviceAndroid, ChromeBluetoothDevice.this, + serviceInstanceId, service); } - nativeOnGattServicesDiscovered(mNativeBluetoothDeviceAndroid); + ChromeBluetoothDeviceJni.get().onGattServicesDiscovered( + mNativeBluetoothDeviceAndroid, ChromeBluetoothDevice.this); } } }); @@ -304,17 +308,19 @@ final class ChromeBluetoothDevice { } } - // --------------------------------------------------------------------------------------------- - // BluetoothAdapterDevice C++ methods declared for access from java: - - // Binds to BluetoothDeviceAndroid::OnConnectionStateChange. - private native void nativeOnConnectionStateChange( - long nativeBluetoothDeviceAndroid, int status, boolean connected); + @NativeMethods + interface Natives { + // Binds to BluetoothDeviceAndroid::OnConnectionStateChange. + void onConnectionStateChange(long nativeBluetoothDeviceAndroid, + ChromeBluetoothDevice caller, int status, boolean connected); - // Binds to BluetoothDeviceAndroid::CreateGattRemoteService. - private native void nativeCreateGattRemoteService(long nativeBluetoothDeviceAndroid, - String instanceId, Wrappers.BluetoothGattServiceWrapper serviceWrapper); + // Binds to BluetoothDeviceAndroid::CreateGattRemoteService. + void createGattRemoteService(long nativeBluetoothDeviceAndroid, + ChromeBluetoothDevice caller, String instanceId, + Wrappers.BluetoothGattServiceWrapper serviceWrapper); - // Binds to BluetoothDeviceAndroid::GattServicesDiscovered. - private native void nativeOnGattServicesDiscovered(long nativeBluetoothDeviceAndroid); + // Binds to BluetoothDeviceAndroid::GattServicesDiscovered. + void onGattServicesDiscovered( + long nativeBluetoothDeviceAndroid, ChromeBluetoothDevice caller); + } } diff --git a/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattCharacteristic.java b/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattCharacteristic.java index 06b63aaac15..41344a1b708 100644 --- a/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattCharacteristic.java +++ b/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattCharacteristic.java @@ -11,6 +11,7 @@ import org.chromium.base.Log; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNIAdditionalImport; import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.NativeMethods; import java.util.List; @@ -63,7 +64,9 @@ final class ChromeBluetoothRemoteGattCharacteristic { void onCharacteristicChanged(byte[] value) { Log.i(TAG, "onCharacteristicChanged"); if (mNativeBluetoothRemoteGattCharacteristicAndroid != 0) { - nativeOnChanged(mNativeBluetoothRemoteGattCharacteristicAndroid, value); + ChromeBluetoothRemoteGattCharacteristicJni.get().onChanged( + mNativeBluetoothRemoteGattCharacteristicAndroid, + ChromeBluetoothRemoteGattCharacteristic.this, value); } } @@ -71,7 +74,9 @@ final class ChromeBluetoothRemoteGattCharacteristic { Log.i(TAG, "onCharacteristicRead status:%d==%s", status, status == android.bluetooth.BluetoothGatt.GATT_SUCCESS ? "OK" : "Error"); if (mNativeBluetoothRemoteGattCharacteristicAndroid != 0) { - nativeOnRead(mNativeBluetoothRemoteGattCharacteristicAndroid, status, + ChromeBluetoothRemoteGattCharacteristicJni.get().onRead( + mNativeBluetoothRemoteGattCharacteristicAndroid, + ChromeBluetoothRemoteGattCharacteristic.this, status, mCharacteristic.getValue()); } } @@ -80,7 +85,9 @@ final class ChromeBluetoothRemoteGattCharacteristic { Log.i(TAG, "onCharacteristicWrite status:%d==%s", status, status == android.bluetooth.BluetoothGatt.GATT_SUCCESS ? "OK" : "Error"); if (mNativeBluetoothRemoteGattCharacteristicAndroid != 0) { - nativeOnWrite(mNativeBluetoothRemoteGattCharacteristicAndroid, status); + ChromeBluetoothRemoteGattCharacteristicJni.get().onWrite( + mNativeBluetoothRemoteGattCharacteristicAndroid, + ChromeBluetoothRemoteGattCharacteristic.this, status); } } @@ -156,27 +163,31 @@ final class ChromeBluetoothRemoteGattCharacteristic { for (Wrappers.BluetoothGattDescriptorWrapper descriptor : descriptors) { String descriptorInstanceId = mInstanceId + "/" + descriptor.getUuid().toString() + ";" + instanceIdCounter++; - nativeCreateGattRemoteDescriptor(mNativeBluetoothRemoteGattCharacteristicAndroid, - descriptorInstanceId, descriptor, mChromeDevice); + ChromeBluetoothRemoteGattCharacteristicJni.get().createGattRemoteDescriptor( + mNativeBluetoothRemoteGattCharacteristicAndroid, + ChromeBluetoothRemoteGattCharacteristic.this, descriptorInstanceId, descriptor, + mChromeDevice); } } - // --------------------------------------------------------------------------------------------- - // BluetoothAdapterDevice C++ methods declared for access from java: - - // Binds to BluetoothRemoteGattCharacteristicAndroid::OnChanged. - native void nativeOnChanged(long nativeBluetoothRemoteGattCharacteristicAndroid, byte[] value); - - // Binds to BluetoothRemoteGattCharacteristicAndroid::OnRead. - native void nativeOnRead( - long nativeBluetoothRemoteGattCharacteristicAndroid, int status, byte[] value); - - // Binds to BluetoothRemoteGattCharacteristicAndroid::OnWrite. - native void nativeOnWrite(long nativeBluetoothRemoteGattCharacteristicAndroid, int status); - - // Binds to BluetoothRemoteGattCharacteristicAndroid::CreateGattRemoteDescriptor. - private native void nativeCreateGattRemoteDescriptor( - long nativeBluetoothRemoteGattCharacteristicAndroid, String instanceId, - Wrappers.BluetoothGattDescriptorWrapper descriptorWrapper, - ChromeBluetoothDevice chromeBluetoothDevice); + @NativeMethods + interface Natives { + // Binds to BluetoothRemoteGattCharacteristicAndroid::OnChanged. + void onChanged(long nativeBluetoothRemoteGattCharacteristicAndroid, + ChromeBluetoothRemoteGattCharacteristic caller, byte[] value); + + // Binds to BluetoothRemoteGattCharacteristicAndroid::OnRead. + void onRead(long nativeBluetoothRemoteGattCharacteristicAndroid, + ChromeBluetoothRemoteGattCharacteristic caller, int status, byte[] value); + + // Binds to BluetoothRemoteGattCharacteristicAndroid::OnWrite. + void onWrite(long nativeBluetoothRemoteGattCharacteristicAndroid, + ChromeBluetoothRemoteGattCharacteristic caller, int status); + + // Binds to BluetoothRemoteGattCharacteristicAndroid::CreateGattRemoteDescriptor. + void createGattRemoteDescriptor(long nativeBluetoothRemoteGattCharacteristicAndroid, + ChromeBluetoothRemoteGattCharacteristic caller, String instanceId, + Wrappers.BluetoothGattDescriptorWrapper descriptorWrapper, + ChromeBluetoothDevice chromeBluetoothDevice); + } } diff --git a/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattDescriptor.java b/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattDescriptor.java index a90608bad1e..4214c4281be 100644 --- a/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattDescriptor.java +++ b/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattDescriptor.java @@ -8,6 +8,7 @@ import org.chromium.base.Log; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNIAdditionalImport; import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.NativeMethods; /** * Exposes android.bluetooth.BluetoothGattDescriptor as necessary @@ -50,8 +51,9 @@ final class ChromeBluetoothRemoteGattDescriptor { Log.i(TAG, "onDescriptorRead status:%d==%s", status, status == android.bluetooth.BluetoothGatt.GATT_SUCCESS ? "OK" : "Error"); if (mNativeBluetoothRemoteGattDescriptorAndroid != 0) { - nativeOnRead( - mNativeBluetoothRemoteGattDescriptorAndroid, status, mDescriptor.getValue()); + ChromeBluetoothRemoteGattDescriptorJni.get().onRead( + mNativeBluetoothRemoteGattDescriptorAndroid, + ChromeBluetoothRemoteGattDescriptor.this, status, mDescriptor.getValue()); } } @@ -59,7 +61,9 @@ final class ChromeBluetoothRemoteGattDescriptor { Log.i(TAG, "onDescriptorWrite status:%d==%s", status, status == android.bluetooth.BluetoothGatt.GATT_SUCCESS ? "OK" : "Error"); if (mNativeBluetoothRemoteGattDescriptorAndroid != 0) { - nativeOnWrite(mNativeBluetoothRemoteGattDescriptorAndroid, status); + ChromeBluetoothRemoteGattDescriptorJni.get().onWrite( + mNativeBluetoothRemoteGattDescriptorAndroid, + ChromeBluetoothRemoteGattDescriptor.this, status); } } @@ -106,13 +110,14 @@ final class ChromeBluetoothRemoteGattDescriptor { return true; } - // --------------------------------------------------------------------------------------------- - // BluetoothAdapterDevice C++ methods declared for access from java: - - // Binds to BluetoothRemoteGattDescriptorAndroid::OnRead. - native void nativeOnRead( - long nativeBluetoothRemoteGattDescriptorAndroid, int status, byte[] value); + @NativeMethods + interface Natives { + // Binds to BluetoothRemoteGattDescriptorAndroid::OnRead. + void onRead(long nativeBluetoothRemoteGattDescriptorAndroid, + ChromeBluetoothRemoteGattDescriptor caller, int status, byte[] value); - // Binds to BluetoothRemoteGattDescriptorAndroid::OnWrite. - native void nativeOnWrite(long nativeBluetoothRemoteGattDescriptorAndroid, int status); + // Binds to BluetoothRemoteGattDescriptorAndroid::OnWrite. + void onWrite(long nativeBluetoothRemoteGattDescriptorAndroid, + ChromeBluetoothRemoteGattDescriptor caller, int status); + } } diff --git a/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattService.java b/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattService.java index 6f2989ebad2..fd6803e1ece 100644 --- a/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattService.java +++ b/chromium/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattService.java @@ -8,6 +8,7 @@ import org.chromium.base.Log; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNIAdditionalImport; import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.NativeMethods; import java.util.List; @@ -46,9 +47,6 @@ final class ChromeBluetoothRemoteGattService { mNativeBluetoothRemoteGattServiceAndroid = 0; } - // --------------------------------------------------------------------------------------------- - // BluetoothRemoteGattServiceAndroid methods implemented in java: - // Implements BluetoothRemoteGattServiceAndroid::Create. @CalledByNative private static ChromeBluetoothRemoteGattService create( @@ -76,17 +74,18 @@ final class ChromeBluetoothRemoteGattService { // characteristic instances with the same UUID on this service. String characteristicInstanceId = mInstanceId + "/" + characteristic.getUuid().toString() + "," + characteristic.getInstanceId(); - nativeCreateGattRemoteCharacteristic(mNativeBluetoothRemoteGattServiceAndroid, + ChromeBluetoothRemoteGattServiceJni.get().createGattRemoteCharacteristic( + mNativeBluetoothRemoteGattServiceAndroid, ChromeBluetoothRemoteGattService.this, characteristicInstanceId, characteristic, mChromeDevice); } } - // --------------------------------------------------------------------------------------------- - // BluetoothAdapterDevice C++ methods declared for access from java: - - // Binds to BluetoothRemoteGattServiceAndroid::CreateGattRemoteCharacteristic. - private native void nativeCreateGattRemoteCharacteristic( - long nativeBluetoothRemoteGattServiceAndroid, String instanceId, - Wrappers.BluetoothGattCharacteristicWrapper characteristicWrapper, - ChromeBluetoothDevice chromeBluetoothDevice); + @NativeMethods + interface Natives { + // Binds to BluetoothRemoteGattServiceAndroid::CreateGattRemoteCharacteristic. + void createGattRemoteCharacteristic(long nativeBluetoothRemoteGattServiceAndroid, + ChromeBluetoothRemoteGattService caller, String instanceId, + Wrappers.BluetoothGattCharacteristicWrapper characteristicWrapper, + ChromeBluetoothDevice chromeBluetoothDevice); + } } diff --git a/chromium/device/bluetooth/bluetooth_adapter.cc b/chromium/device/bluetooth/bluetooth_adapter.cc index 564cf1a6843..eabda679333 100644 --- a/chromium/device/bluetooth/bluetooth_adapter.cc +++ b/chromium/device/bluetooth/bluetooth_adapter.cc @@ -37,7 +37,7 @@ base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter( #endif // !defined(OS_CHROMEOS) && !defined(OS_WIN) && !defined(OS_MACOSX) base::WeakPtr<BluetoothAdapter> BluetoothAdapter::GetWeakPtrForTesting() { - return weak_ptr_factory_.GetWeakPtr(); + return GetWeakPtr(); } #if defined(OS_LINUX) @@ -446,8 +446,15 @@ void BluetoothAdapter::OnDiscoveryChangeComplete( UMABluetoothDiscoverySessionOutcome outcome) { UpdateDiscoveryState(is_error); + // Take a weak reference to |this| in case a callback frees the adapter. + base::WeakPtr<BluetoothAdapter> self = GetWeakPtr(); + if (is_error) { NotifyDiscoveryError(std::move(callbacks_awaiting_response_)); + + if (!self) + return; + discovery_request_pending_ = false; ProcessDiscoveryQueue(); return; @@ -455,16 +462,20 @@ void BluetoothAdapter::OnDiscoveryChangeComplete( current_discovery_filter_.CopyFrom(filter_being_set_); - while (!callbacks_awaiting_response_.empty()) { + auto callbacks_awaiting_response = std::move(callbacks_awaiting_response_); + while (!callbacks_awaiting_response.empty()) { std::unique_ptr<StartOrStopDiscoveryCallback> callbacks = - std::move(callbacks_awaiting_response_.front()); - callbacks_awaiting_response_.pop(); + std::move(callbacks_awaiting_response.front()); + callbacks_awaiting_response.pop(); if (callbacks->start_callback) std::move(callbacks->start_callback).Run(); - if (callbacks->stop_callback) std::move(callbacks->stop_callback).Run(); } + + if (!self) + return; + discovery_request_pending_ = false; ProcessDiscoveryQueue(); } @@ -501,14 +512,13 @@ void BluetoothAdapter::ProcessDiscoveryQueue() { internal_discovery_state_ = DiscoveryState::kStopping; discovery_request_pending_ = true; StopScan(base::BindOnce(&BluetoothAdapter::OnDiscoveryChangeComplete, - weak_ptr_factory_.GetWeakPtr())); + GetWeakPtr())); return; } - auto result_callback = - base::BindOnce(&BluetoothAdapter::OnDiscoveryChangeComplete, - weak_ptr_factory_.GetWeakPtr()); + auto result_callback = base::BindOnce( + &BluetoothAdapter::OnDiscoveryChangeComplete, GetWeakPtr()); auto new_desired_filter = GetMergedDiscoveryFilter(); discovery_request_pending_ = true; filter_being_set_.CopyFrom(*new_desired_filter.get()); diff --git a/chromium/device/bluetooth/bluetooth_adapter.h b/chromium/device/bluetooth/bluetooth_adapter.h index 143d35b4eda..0cf207feb21 100644 --- a/chromium/device/bluetooth/bluetooth_adapter.h +++ b/chromium/device/bluetooth/bluetooth_adapter.h @@ -671,6 +671,8 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapter BluetoothAdapter(); virtual ~BluetoothAdapter(); + virtual base::WeakPtr<BluetoothAdapter> GetWeakPtr() = 0; + // This method calls into platform specific logic on macOS and Android where // pending SetPowered() callbacks need to be stored explicitly. virtual bool SetPoweredImpl(bool powered) = 0; @@ -818,10 +820,6 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapter // enum used to track our internal discovery state. DiscoveryState internal_discovery_state_ = DiscoveryState::kIdle; - - // 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<BluetoothAdapter> weak_ptr_factory_{this}; }; } // namespace device diff --git a/chromium/device/bluetooth/bluetooth_adapter_android.cc b/chromium/device/bluetooth/bluetooth_adapter_android.cc index d521c57b316..9fa04809d08 100644 --- a/chromium/device/bluetooth/bluetooth_adapter_android.cc +++ b/chromium/device/bluetooth/bluetooth_adapter_android.cc @@ -295,6 +295,10 @@ void BluetoothAdapterAndroid::PurgeTimedOutDevices() { } } +base::WeakPtr<BluetoothAdapter> BluetoothAdapterAndroid::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + bool BluetoothAdapterAndroid::SetPoweredImpl(bool powered) { return Java_ChromeBluetoothAdapter_setPowered(AttachCurrentThread(), j_adapter_, powered); diff --git a/chromium/device/bluetooth/bluetooth_adapter_android.h b/chromium/device/bluetooth/bluetooth_adapter_android.h index 3fbdba25ae5..dc866518978 100644 --- a/chromium/device/bluetooth/bluetooth_adapter_android.h +++ b/chromium/device/bluetooth/bluetooth_adapter_android.h @@ -112,6 +112,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterAndroid final ~BluetoothAdapterAndroid() override; // BluetoothAdapter: + base::WeakPtr<BluetoothAdapter> GetWeakPtr() override; bool SetPoweredImpl(bool powered) override; void StartScanWithFilter( std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter, diff --git a/chromium/device/bluetooth/bluetooth_adapter_factory.h b/chromium/device/bluetooth/bluetooth_adapter_factory.h index d33b22a6e20..e80a0d1205b 100644 --- a/chromium/device/bluetooth/bluetooth_adapter_factory.h +++ b/chromium/device/bluetooth/bluetooth_adapter_factory.h @@ -86,7 +86,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterFactory { static bool HasSharedInstanceForTesting(); #if defined(OS_CHROMEOS) - // Sets the BleScanParserPtr callback used in Get*() below. + // Sets the mojo::Remote<BleScanParser> callback used in Get*() below. static void SetBleScanParserCallback(BleScanParserCallback callback); // Returns a reference to a parser for BLE advertisement packets. // This will be an empty callback until something calls Set*() above. diff --git a/chromium/device/bluetooth/bluetooth_adapter_mac.h b/chromium/device/bluetooth/bluetooth_adapter_mac.h index 61414cc0503..544e868aef2 100644 --- a/chromium/device/bluetooth/bluetooth_adapter_mac.h +++ b/chromium/device/bluetooth/bluetooth_adapter_mac.h @@ -124,6 +124,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterMac base::RepeatingCallback<bool(const std::string& address)>; // BluetoothAdapter override: + base::WeakPtr<BluetoothAdapter> GetWeakPtr() override; bool SetPoweredImpl(bool powered) override; void RemovePairingDelegateInternal( device::BluetoothDevice::PairingDelegate* pairing_delegate) override; diff --git a/chromium/device/bluetooth/bluetooth_adapter_mac.mm b/chromium/device/bluetooth/bluetooth_adapter_mac.mm index 8e4a469b6f0..b2425416b19 100644 --- a/chromium/device/bluetooth/bluetooth_adapter_mac.mm +++ b/chromium/device/bluetooth/bluetooth_adapter_mac.mm @@ -302,6 +302,10 @@ void BluetoothAdapterMac::DeviceConnected(IOBluetoothDevice* device) { ClassicDeviceAdded(device); } +base::WeakPtr<BluetoothAdapter> BluetoothAdapterMac::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + bool BluetoothAdapterMac::SetPoweredImpl(bool powered) { power_state_function_.Run(base::strict_cast<int>(powered)); return true; diff --git a/chromium/device/bluetooth/bluetooth_adapter_mac_unittest.mm b/chromium/device/bluetooth/bluetooth_adapter_mac_unittest.mm index 1468c63e248..817338f5227 100644 --- a/chromium/device/bluetooth/bluetooth_adapter_mac_unittest.mm +++ b/chromium/device/bluetooth/bluetooth_adapter_mac_unittest.mm @@ -306,7 +306,9 @@ TEST_F(BluetoothAdapterMacTest, AddSecondDiscoverySessionWithLowEnergyFilter) { new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE)); // Adding uuid to first discovery session so that there is a change to be made // when starting the second session. - discovery_filter->AddUUID(device::BluetoothUUID("1000")); + BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(device::BluetoothUUID("1000")); + discovery_filter->AddDeviceFilter(device_filter); adapter_mac_->StartDiscoverySessionWithFilter( std::move(discovery_filter), base::BindRepeating( diff --git a/chromium/device/bluetooth/bluetooth_adapter_unittest.cc b/chromium/device/bluetooth/bluetooth_adapter_unittest.cc index 2448a769e2a..04103ea933a 100644 --- a/chromium/device/bluetooth/bluetooth_adapter_unittest.cc +++ b/chromium/device/bluetooth/bluetooth_adapter_unittest.cc @@ -69,9 +69,16 @@ using device::BluetoothDevice; namespace device { +void AddDeviceFilterWithUUID(BluetoothDiscoveryFilter* filter, + BluetoothUUID uuid) { + BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(uuid); + filter->AddDeviceFilter(device_filter); +} + namespace { -class TestBluetoothAdapter : public BluetoothAdapter { +class TestBluetoothAdapter final : public BluetoothAdapter { public: TestBluetoothAdapter() = default; @@ -162,6 +169,11 @@ class TestBluetoothAdapter : public BluetoothAdapter { run_loop_quit.Run(); } + void set_discovery_session_outcome( + UMABluetoothDiscoverySessionOutcome outcome) { + discovery_session_outcome_ = outcome; + } + void StopDiscoverySession(base::Closure run_loop_quit) { discovery_sessions_holder_.front()->Stop( base::BindRepeating(&TestBluetoothAdapter::OnRemoveDiscoverySession, @@ -250,12 +262,17 @@ class TestBluetoothAdapter : public BluetoothAdapter { bool SetPoweredImpl(bool powered) override { return false; } + base::WeakPtr<BluetoothAdapter> GetWeakPtr() override { + return weak_ptr_factory_.GetWeakPtr(); + } + void StartScanWithFilter( std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter, DiscoverySessionResultCallback callback) override { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::BindOnce(&TestBluetoothAdapter::SetFilter, this, + base::BindOnce(&TestBluetoothAdapter::SetFilter, + weak_ptr_factory_.GetWeakPtr(), std::move(discovery_filter), std::move(callback))); } @@ -263,22 +280,27 @@ class TestBluetoothAdapter : public BluetoothAdapter { DiscoverySessionResultCallback callback) override { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::BindOnce(&TestBluetoothAdapter::SetFilter, this, + base::BindOnce(&TestBluetoothAdapter::SetFilter, + weak_ptr_factory_.GetWeakPtr(), std::move(discovery_filter), std::move(callback))); } void SetFilter(std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter, DiscoverySessionResultCallback callback) { - is_discovering_ = true; - current_filter->CopyFrom(*discovery_filter.get()); - std::move(callback).Run(/*is_error=*/false, - UMABluetoothDiscoverySessionOutcome::SUCCESS); + bool is_error = discovery_session_outcome_ != + UMABluetoothDiscoverySessionOutcome::SUCCESS; + if (!is_error) { + is_discovering_ = true; + current_filter->CopyFrom(*discovery_filter.get()); + } + std::move(callback).Run(is_error, discovery_session_outcome_); } void StopScan(DiscoverySessionResultCallback callback) override { base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(&TestBluetoothAdapter::FakeOSStopScan, this, - std::move(callback))); + FROM_HERE, + base::BindOnce(&TestBluetoothAdapter::FakeOSStopScan, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } void FakeOSStopScan(DiscoverySessionResultCallback callback) { @@ -297,6 +319,13 @@ class TestBluetoothAdapter : public BluetoothAdapter { base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback)); } + + UMABluetoothDiscoverySessionOutcome discovery_session_outcome_ = + UMABluetoothDiscoverySessionOutcome::SUCCESS; + + // This must be the last field in the class so that weak pointers are + // invalidated first. + base::WeakPtrFactory<TestBluetoothAdapter> weak_ptr_factory_{this}; }; class TestPairingDelegate : public BluetoothDevice::PairingDelegate { @@ -399,15 +428,13 @@ TEST_F(BluetoothAdapterTest, MAYBE_GetMergedDiscoveryFilterRegular) { } TEST_F(BluetoothAdapterTest, TestQueueingLogic) { - BluetoothDiscoveryFilter* df = - new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE); - df->AddUUID(device::BluetoothUUID("1001")); - std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter(df); + auto discovery_filter = + std::make_unique<BluetoothDiscoveryFilter>(BLUETOOTH_TRANSPORT_LE); + AddDeviceFilterWithUUID(discovery_filter.get(), BluetoothUUID("1001")); - BluetoothDiscoveryFilter* df2 = - new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE); - df->AddUUID(device::BluetoothUUID("1002")); - std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter2(df2); + auto discovery_filter2 = + std::make_unique<BluetoothDiscoveryFilter>(BLUETOOTH_TRANSPORT_LE); + AddDeviceFilterWithUUID(discovery_filter2.get(), BluetoothUUID("1002")); // Start a discovery session base::RunLoop run_loop1; @@ -460,10 +487,9 @@ TEST_F(BluetoothAdapterTest, TestQueueingLogic) { TEST_F(BluetoothAdapterTest, ShortCircuitUpdateTest) { auto discovery_filter_default1 = std::make_unique<BluetoothDiscoveryFilter>(); - BluetoothDiscoveryFilter* df = - new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE); - df->SetRSSI(-30); - std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter(df); + auto discovery_filter = + std::make_unique<BluetoothDiscoveryFilter>(BLUETOOTH_TRANSPORT_LE); + discovery_filter->SetRSSI(-30); base::RunLoop run_loop; adapter_->StartSessionWithFilter(std::move(discovery_filter_default1), @@ -535,15 +561,13 @@ TEST_F(BluetoothAdapterTest, MAYBE_GetMergedDiscoveryFilterRssi) { uint16_t resulting_pathloss; std::unique_ptr<BluetoothDiscoveryFilter> resulting_filter; - BluetoothDiscoveryFilter* df = - new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE); - df->SetRSSI(-30); - std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter(df); + auto discovery_filter = + std::make_unique<BluetoothDiscoveryFilter>(BLUETOOTH_TRANSPORT_LE); + discovery_filter->SetRSSI(-30); - BluetoothDiscoveryFilter* df2 = - new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE); - df2->SetRSSI(-65); - std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter2(df2); + auto discovery_filter2 = + std::make_unique<BluetoothDiscoveryFilter>(BLUETOOTH_TRANSPORT_LE); + discovery_filter2->SetRSSI(-65); // Make sure adapter has one session without filtering. adapter_->InjectFilteredSession(std::move(discovery_filter)); @@ -579,13 +603,11 @@ TEST_F(BluetoothAdapterTest, MAYBE_GetMergedDiscoveryFilterTransport) { scoped_refptr<TestBluetoothAdapter> adapter = new TestBluetoothAdapter(); std::unique_ptr<BluetoothDiscoveryFilter> resulting_filter; - BluetoothDiscoveryFilter* df = - new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_CLASSIC); - std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter(df); + auto discovery_filter = + std::make_unique<BluetoothDiscoveryFilter>(BLUETOOTH_TRANSPORT_CLASSIC); - BluetoothDiscoveryFilter* df2 = - new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE); - std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter2(df2); + auto discovery_filter2 = + std::make_unique<BluetoothDiscoveryFilter>(BLUETOOTH_TRANSPORT_LE); adapter_->InjectFilteredSession(std::move(discovery_filter)); @@ -599,10 +621,10 @@ TEST_F(BluetoothAdapterTest, MAYBE_GetMergedDiscoveryFilterTransport) { resulting_filter = adapter_->GetMergedDiscoveryFilter(); EXPECT_EQ(BLUETOOTH_TRANSPORT_DUAL, resulting_filter->GetTransport()); - BluetoothDiscoveryFilter* df3 = - new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE); - df3->CopyFrom(BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_DUAL)); - std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter3(df3); + auto discovery_filter3 = + std::make_unique<BluetoothDiscoveryFilter>(BLUETOOTH_TRANSPORT_LE); + discovery_filter3->CopyFrom( + BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_DUAL)); // Merging empty filter in should result in empty filter adapter_->InjectFilteredSession(std::move(discovery_filter3)); @@ -617,27 +639,23 @@ TEST_F(BluetoothAdapterTest, MAYBE_GetMergedDiscoveryFilterAllFields) { int16_t resulting_rssi; std::set<device::BluetoothUUID> resulting_uuids; - BluetoothDiscoveryFilter* df = - new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE); - df->SetRSSI(-60); - df->AddUUID(device::BluetoothUUID("1000")); - std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter(df); + auto discovery_filter = + std::make_unique<BluetoothDiscoveryFilter>(BLUETOOTH_TRANSPORT_LE); + discovery_filter->SetRSSI(-60); + AddDeviceFilterWithUUID(discovery_filter.get(), BluetoothUUID("1000")); - BluetoothDiscoveryFilter* df2 = - new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE); - df2->SetRSSI(-85); - df2->SetTransport(BLUETOOTH_TRANSPORT_LE); - df2->AddUUID(device::BluetoothUUID("1020")); - df2->AddUUID(device::BluetoothUUID("1001")); - std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter2(df2); + auto discovery_filter2 = + std::make_unique<BluetoothDiscoveryFilter>(BLUETOOTH_TRANSPORT_LE); + discovery_filter2->SetRSSI(-85); + AddDeviceFilterWithUUID(discovery_filter2.get(), BluetoothUUID("1020")); + AddDeviceFilterWithUUID(discovery_filter2.get(), BluetoothUUID("1001")); - BluetoothDiscoveryFilter* df3 = - new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE); - df3->SetRSSI(-65); - df3->SetTransport(BLUETOOTH_TRANSPORT_CLASSIC); - df3->AddUUID(device::BluetoothUUID("1020")); - df3->AddUUID(device::BluetoothUUID("1003")); - std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter3(df3); + auto discovery_filter3 = + std::make_unique<BluetoothDiscoveryFilter>(BLUETOOTH_TRANSPORT_LE); + discovery_filter3->SetRSSI(-65); + discovery_filter3->SetTransport(BLUETOOTH_TRANSPORT_CLASSIC); + AddDeviceFilterWithUUID(discovery_filter3.get(), BluetoothUUID("1020")); + AddDeviceFilterWithUUID(discovery_filter3.get(), BluetoothUUID("1003")); adapter_->InjectFilteredSession(std::move(discovery_filter)); adapter_->InjectFilteredSession(std::move(discovery_filter2)); @@ -662,6 +680,30 @@ TEST_F(BluetoothAdapterTest, MAYBE_GetMergedDiscoveryFilterAllFields) { adapter_->CleanupSessions(); } +TEST_F(BluetoothAdapterTest, StartDiscoverySession_Destroy) { + base::RunLoop loop; + adapter_->StartDiscoverySession( + base::BindLambdaForTesting( + [&](std::unique_ptr<BluetoothDiscoverySession> session) { + adapter_.reset(); + loop.Quit(); + }), + base::DoNothing()); + loop.Run(); +} + +TEST_F(BluetoothAdapterTest, StartDiscoverySessionError_Destroy) { + base::RunLoop loop; + adapter_->set_discovery_session_outcome( + UMABluetoothDiscoverySessionOutcome::FAILED); + adapter_->StartDiscoverySession(base::DoNothing(), + base::BindLambdaForTesting([&]() { + adapter_.reset(); + loop.Quit(); + })); + loop.Run(); +} + // TODO(scheib): Enable BluetoothTest fixture tests on all platforms. #if defined(OS_ANDROID) || defined(OS_MACOSX) #define MAYBE_ConstructDefaultAdapter ConstructDefaultAdapter @@ -1696,21 +1738,19 @@ TEST_F(BluetoothTest, MAYBE_RegisterLocalGattServices) { base::WeakPtr<BluetoothLocalGattCharacteristic> characteristic1 = BluetoothLocalGattCharacteristic::Create( BluetoothUUID(kTestUUIDGenericAttribute), - device::BluetoothLocalGattCharacteristic::Properties(), - device::BluetoothLocalGattCharacteristic::Permissions(), - service.get()); + BluetoothLocalGattCharacteristic::Properties(), + BluetoothLocalGattCharacteristic::Permissions(), service.get()); base::WeakPtr<BluetoothLocalGattCharacteristic> characteristic2 = BluetoothLocalGattCharacteristic::Create( BluetoothUUID(kTestUUIDGenericAttribute), - device::BluetoothLocalGattCharacteristic::Properties(), - device::BluetoothLocalGattCharacteristic::Permissions(), - service.get()); + BluetoothLocalGattCharacteristic::Properties(), + BluetoothLocalGattCharacteristic::Permissions(), service.get()); base::WeakPtr<BluetoothLocalGattDescriptor> descriptor = BluetoothLocalGattDescriptor::Create( BluetoothUUID(kTestUUIDGenericAttribute), - device::BluetoothLocalGattCharacteristic::Permissions(), + BluetoothLocalGattCharacteristic::Permissions(), characteristic1.get()); service->Register(GetCallback(Call::EXPECTED), @@ -1865,9 +1905,8 @@ TEST_F(BluetoothTest, DiscoverConnectedLowEnergyDeviceWithFilter) { SimulateConnectedLowEnergyDevice(ConnectedDeviceType::GENERIC_DEVICE); SimulateConnectedLowEnergyDevice(ConnectedDeviceType::HEART_RATE_DEVICE); BluetoothDiscoveryFilter discovery_filter(BLUETOOTH_TRANSPORT_LE); - device::BluetoothUUID heart_service_uuid = - device::BluetoothUUID(kTestUUIDHeartRate); - discovery_filter.AddUUID(heart_service_uuid); + BluetoothUUID heart_service_uuid = BluetoothUUID(kTestUUIDHeartRate); + AddDeviceFilterWithUUID(&discovery_filter, heart_service_uuid); std::unordered_map<BluetoothDevice*, BluetoothDevice::UUIDSet> result = adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter( discovery_filter); @@ -1900,7 +1939,7 @@ TEST_F(BluetoothTest, DiscoverConnectedLowEnergyDeviceWithWrongFilter) { SimulateConnectedLowEnergyDevice(ConnectedDeviceType::GENERIC_DEVICE); SimulateConnectedLowEnergyDevice(ConnectedDeviceType::HEART_RATE_DEVICE); BluetoothDiscoveryFilter discovery_filter(BLUETOOTH_TRANSPORT_LE); - discovery_filter.AddUUID(device::BluetoothUUID(kTestUUIDLinkLoss)); + AddDeviceFilterWithUUID(&discovery_filter, BluetoothUUID(kTestUUIDLinkLoss)); std::unordered_map<BluetoothDevice*, BluetoothDevice::UUIDSet> result = adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter( discovery_filter); @@ -1929,12 +1968,10 @@ TEST_F(BluetoothTest, DiscoverConnectedLowEnergyDeviceWithTwoFilters) { SimulateConnectedLowEnergyDevice(ConnectedDeviceType::GENERIC_DEVICE); SimulateConnectedLowEnergyDevice(ConnectedDeviceType::HEART_RATE_DEVICE); BluetoothDiscoveryFilter discovery_filter(BLUETOOTH_TRANSPORT_LE); - device::BluetoothUUID heart_service_uuid = - device::BluetoothUUID(kTestUUIDHeartRate); - discovery_filter.AddUUID(heart_service_uuid); - device::BluetoothUUID generic_service_uuid = - device::BluetoothUUID(kTestUUIDGenericAccess); - discovery_filter.AddUUID(generic_service_uuid); + BluetoothUUID heart_service_uuid = BluetoothUUID(kTestUUIDHeartRate); + AddDeviceFilterWithUUID(&discovery_filter, heart_service_uuid); + BluetoothUUID generic_service_uuid = BluetoothUUID(kTestUUIDGenericAccess); + AddDeviceFilterWithUUID(&discovery_filter, generic_service_uuid); std::unordered_map<BluetoothDevice*, BluetoothDevice::UUIDSet> result = adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter( discovery_filter); @@ -1977,9 +2014,8 @@ TEST_F(BluetoothTest, DiscoverConnectedLowEnergyDeviceTwice) { SimulateConnectedLowEnergyDevice(ConnectedDeviceType::GENERIC_DEVICE); SimulateConnectedLowEnergyDevice(ConnectedDeviceType::HEART_RATE_DEVICE); BluetoothDiscoveryFilter discovery_filter(BLUETOOTH_TRANSPORT_LE); - device::BluetoothUUID heart_service_uuid = - device::BluetoothUUID(kTestUUIDHeartRate); - discovery_filter.AddUUID(heart_service_uuid); + BluetoothUUID heart_service_uuid = BluetoothUUID(kTestUUIDHeartRate); + AddDeviceFilterWithUUID(&discovery_filter, heart_service_uuid); std::unordered_map<BluetoothDevice*, BluetoothDevice::UUIDSet> result = adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter( discovery_filter); diff --git a/chromium/device/bluetooth/bluetooth_adapter_win.cc b/chromium/device/bluetooth/bluetooth_adapter_win.cc index 85a34d42eeb..06eac5b7403 100644 --- a/chromium/device/bluetooth/bluetooth_adapter_win.cc +++ b/chromium/device/bluetooth/bluetooth_adapter_win.cc @@ -279,6 +279,10 @@ void BluetoothAdapterWin::DevicesPolled( } } +base::WeakPtr<BluetoothAdapter> BluetoothAdapterWin::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + // BluetoothAdapterWin should override SetPowered() instead. bool BluetoothAdapterWin::SetPoweredImpl(bool powered) { NOTREACHED(); diff --git a/chromium/device/bluetooth/bluetooth_adapter_win.h b/chromium/device/bluetooth/bluetooth_adapter_win.h index 8fb83752eb7..bd56d4b715a 100644 --- a/chromium/device/bluetooth/bluetooth_adapter_win.h +++ b/chromium/device/bluetooth/bluetooth_adapter_win.h @@ -116,6 +116,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterWin ~BluetoothAdapterWin() override; // BluetoothAdapter: + base::WeakPtr<BluetoothAdapter> GetWeakPtr() override; bool SetPoweredImpl(bool powered) override; void UpdateFilter(std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter, DiscoverySessionResultCallback callback) override; diff --git a/chromium/device/bluetooth/bluetooth_adapter_winrt.cc b/chromium/device/bluetooth/bluetooth_adapter_winrt.cc index 79bf527656e..d2a7b9d7a97 100644 --- a/chromium/device/bluetooth/bluetooth_adapter_winrt.cc +++ b/chromium/device/bluetooth/bluetooth_adapter_winrt.cc @@ -841,6 +841,10 @@ void BluetoothAdapterWinrt::CompleteInit( } } +base::WeakPtr<BluetoothAdapter> BluetoothAdapterWinrt::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + bool BluetoothAdapterWinrt::SetPoweredImpl(bool powered) { // Due to an issue on WoW64 we might fail to obtain the radio in // OnGetRadio(). This is why it can be null here. diff --git a/chromium/device/bluetooth/bluetooth_adapter_winrt.h b/chromium/device/bluetooth/bluetooth_adapter_winrt.h index 1c3019cea36..9474428376b 100644 --- a/chromium/device/bluetooth/bluetooth_adapter_winrt.h +++ b/chromium/device/bluetooth/bluetooth_adapter_winrt.h @@ -92,6 +92,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterWinrt : public BluetoothAdapter { radio_statics); // BluetoothAdapter: + base::WeakPtr<BluetoothAdapter> GetWeakPtr() override; bool SetPoweredImpl(bool powered) override; void UpdateFilter(std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter, DiscoverySessionResultCallback callback) override; diff --git a/chromium/device/bluetooth/bluetooth_discovery_filter.cc b/chromium/device/bluetooth/bluetooth_discovery_filter.cc index f591b63efa9..7a9910599e9 100644 --- a/chromium/device/bluetooth/bluetooth_discovery_filter.cc +++ b/chromium/device/bluetooth/bluetooth_discovery_filter.cc @@ -23,6 +23,23 @@ BluetoothDiscoveryFilter::BluetoothDiscoveryFilter( BluetoothDiscoveryFilter::~BluetoothDiscoveryFilter() = default; +BluetoothDiscoveryFilter::DeviceInfoFilter::DeviceInfoFilter() = default; +BluetoothDiscoveryFilter::DeviceInfoFilter::DeviceInfoFilter( + const DeviceInfoFilter& other) = default; +BluetoothDiscoveryFilter::DeviceInfoFilter::~DeviceInfoFilter() = default; +bool BluetoothDiscoveryFilter::DeviceInfoFilter::operator==( + const BluetoothDiscoveryFilter::DeviceInfoFilter& other) const { + return uuids == other.uuids && name == other.name; +} + +bool BluetoothDiscoveryFilter::DeviceInfoFilter::operator<( + const BluetoothDiscoveryFilter::DeviceInfoFilter& other) const { + if (name == other.name) + return uuids < other.uuids; + + return name < other.name; +} + bool BluetoothDiscoveryFilter::GetRSSI(int16_t* out_rssi) const { DCHECK(out_rssi); if (!rssi_) @@ -61,30 +78,25 @@ void BluetoothDiscoveryFilter::SetTransport(BluetoothTransport transport) { void BluetoothDiscoveryFilter::GetUUIDs( std::set<device::BluetoothUUID>& out_uuids) const { out_uuids.clear(); - - for (const auto& uuid : uuids_) - out_uuids.insert(*uuid); -} - -void BluetoothDiscoveryFilter::AddUUID(const device::BluetoothUUID& uuid) { - DCHECK(uuid.IsValid()); - for (const auto& uuid_it : uuids_) { - if (*uuid_it == uuid) - return; + for (const auto& device_filter : device_filters_) { + for (const auto& uuid : device_filter.uuids) { + out_uuids.insert(uuid); + } } +} - uuids_.push_back(std::make_unique<device::BluetoothUUID>(uuid)); +void BluetoothDiscoveryFilter::AddDeviceFilter( + const BluetoothDiscoveryFilter::DeviceInfoFilter& device_filter) { + device_filters_.insert(device_filter); } void BluetoothDiscoveryFilter::CopyFrom( const BluetoothDiscoveryFilter& filter) { transport_ = filter.transport_; - uuids_.clear(); - if (filter.uuids_.size()) { - for (const auto& uuid : filter.uuids_) - AddUUID(*uuid); - } + device_filters_.clear(); + for (const auto& device_filter : filter.device_filters_) + AddDeviceFilter(device_filter); rssi_ = filter.rssi_; pathloss_ = filter.pathloss_; @@ -113,15 +125,13 @@ BluetoothDiscoveryFilter::Merge( // if both filters have uuids, them merge them. Otherwise uuids filter should // be left empty - if (filter_a->uuids_.size() && filter_b->uuids_.size()) { - std::set<device::BluetoothUUID> uuids; - filter_a->GetUUIDs(uuids); - for (auto& uuid : uuids) - result->AddUUID(uuid); - - filter_b->GetUUIDs(uuids); - for (auto& uuid : uuids) - result->AddUUID(uuid); + if (!filter_a->device_filters_.empty() && + !filter_b->device_filters_.empty()) { + for (const auto& device_filter : filter_a->device_filters_) + result->AddDeviceFilter(device_filter); + + for (const auto& device_filter : filter_b->device_filters_) + result->AddDeviceFilter(device_filter); } if ((filter_a->rssi_ && filter_b->pathloss_) || @@ -155,17 +165,14 @@ bool BluetoothDiscoveryFilter::Equals( if (transport_ != other.transport_) return false; - std::set<device::BluetoothUUID> uuids_a, uuids_b; - GetUUIDs(uuids_a); - other.GetUUIDs(uuids_b); - if (uuids_a != uuids_b) + if (device_filters_ != other.device_filters_) return false; return true; } bool BluetoothDiscoveryFilter::IsDefault() const { - return !(rssi_ || pathloss_ || uuids_.size() || + return !(rssi_ || pathloss_ || !device_filters_.empty() || transport_ != BLUETOOTH_TRANSPORT_DUAL); } diff --git a/chromium/device/bluetooth/bluetooth_discovery_filter.h b/chromium/device/bluetooth/bluetooth_discovery_filter.h index 77496359ec3..b76a37232d7 100644 --- a/chromium/device/bluetooth/bluetooth_discovery_filter.h +++ b/chromium/device/bluetooth/bluetooth_discovery_filter.h @@ -9,8 +9,10 @@ #include <memory> #include <set> +#include <string> #include <vector> +#include "base/containers/flat_set.h" #include "base/macros.h" #include "base/optional.h" #include "device/bluetooth/bluetooth_common.h" @@ -19,13 +21,48 @@ namespace device { -// Used to keep a discovery filter that can be used to limit reported devices. +// ***************************************************************************** +// BluetoothDiscoveryFilter is a class which stores information used to filter +// out Bluetooth devices at the operating system level while doing discovery. +// If you want to filter by RSSI or path loss set them directly in the class +// with the SetRSSI() and SetPathloss() functions. However, if you are looking +// for a device with a particular name and/or set of services you must add a +// DeviceInfoFilter. +// Here is an example usage for DeviceInfoFilters: +// +// BluetoothDiscoveryFilter discovery_filter(BLUETOOTH_TRANSPORT_LE); +// BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; +// device_filter.uuids.insert(BluetoothUUID("1019")); +// device_filter.uuids.insert(BluetoothUUID("1020")); +// discovery_filter.AddDeviceFilter(device_filter); +// +// BluetoothDiscoveryFilter::DeviceInfoFilter device_filter2; +// device_filter2.uuids.insert(BluetoothUUID("1021")); +// device_filter2.name = "this device"; +// discovery_filter.AddDeviceFilter(device_filter2); +// +// When we add |device_filter| to |discovery_filter| our filter will only return +// devices that have both the uuid 1019 AND 1020. When we add |device_filter2| +// we will then allow devices though that have either (uuid 1019 AND 1020) OR +// (uuid 1021 and a device name of "this device"). +// ***************************************************************************** + class DEVICE_BLUETOOTH_EXPORT BluetoothDiscoveryFilter { public: BluetoothDiscoveryFilter(); BluetoothDiscoveryFilter(BluetoothTransport transport); ~BluetoothDiscoveryFilter(); + struct DEVICE_BLUETOOTH_EXPORT DeviceInfoFilter { + DeviceInfoFilter(); + DeviceInfoFilter(const DeviceInfoFilter& other); + ~DeviceInfoFilter(); + bool operator==(const DeviceInfoFilter& other) const; + bool operator<(const DeviceInfoFilter& other) const; + base::flat_set<device::BluetoothUUID> uuids; + std::string name; + }; + // These getters return true when given field is set in filter, and copy this // value to |out_*| parameter. If value is not set, returns false. // These setters assign given value to proper filter field. @@ -38,12 +75,14 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDiscoveryFilter { BluetoothTransport GetTransport() const; void SetTransport(BluetoothTransport transport); - // Make |out_uuids| represent all uuids assigned to this filter. + // Make |out_uuids| represent all uuids in the |device_filters_| set. void GetUUIDs(std::set<device::BluetoothUUID>& out_uuids) const; - // Add UUID to internal UUIDs filter. If UUIDs filter doesn't exist, it will - // be created. - void AddUUID(const device::BluetoothUUID& uuid); + // Add new DeviceInfoFilter to our array of DeviceInfoFilters, + void AddDeviceFilter(const DeviceInfoFilter& device_filter); + + // Returns a const pointer of our list of DeviceInfoFilters, device_filters_. + const base::flat_set<DeviceInfoFilter>* GetDeviceFilters(); // Copy content of |filter| and assigns it to this filter. void CopyFrom(const BluetoothDiscoveryFilter& filter); @@ -64,7 +103,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDiscoveryFilter { base::Optional<int16_t> rssi_; base::Optional<uint16_t> pathloss_; BluetoothTransport transport_; - std::vector<std::unique_ptr<device::BluetoothUUID>> uuids_; + base::flat_set<DeviceInfoFilter> device_filters_; DISALLOW_COPY_AND_ASSIGN(BluetoothDiscoveryFilter); }; diff --git a/chromium/device/bluetooth/bluetooth_discovery_filter_unittest.cc b/chromium/device/bluetooth/bluetooth_discovery_filter_unittest.cc index 53c19acebf9..1b24f0df217 100644 --- a/chromium/device/bluetooth/bluetooth_discovery_filter_unittest.cc +++ b/chromium/device/bluetooth/bluetooth_discovery_filter_unittest.cc @@ -9,6 +9,7 @@ #include "base/macros.h" #include "device/bluetooth/bluetooth_common.h" #include "device/bluetooth/bluetooth_discovery_session.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace { @@ -16,6 +17,8 @@ namespace { const device::BluetoothUUID uuid1003("1003"); const device::BluetoothUUID uuid1004("1004"); const device::BluetoothUUID uuid1020("1020"); +const device::BluetoothUUID uuid1057("1027"); +const device::BluetoothUUID uuid1019("1019"); } // namespace @@ -24,20 +27,46 @@ namespace device { TEST(BluetoothDiscoveryFilterTest, Equal) { BluetoothDiscoveryFilter df1(BLUETOOTH_TRANSPORT_CLASSIC); df1.SetRSSI(-65); - df1.AddUUID(uuid1020); - df1.AddUUID(uuid1003); + { + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(uuid1020); + device_filter.uuids.insert(uuid1057); + df1.AddDeviceFilter(device_filter); + } + { + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(uuid1003); + df1.AddDeviceFilter(device_filter); + } BluetoothDiscoveryFilter df2(BLUETOOTH_TRANSPORT_CLASSIC); df2.SetRSSI(-65); - df2.AddUUID(uuid1020); - df2.AddUUID(uuid1004); + { + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(uuid1020); + device_filter.uuids.insert(uuid1057); + df2.AddDeviceFilter(device_filter); + } + { + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(uuid1004); + df2.AddDeviceFilter(device_filter); + } // uuids are not same, so should fail ASSERT_FALSE(df1.Equals(df2)); // make filters equal - df1.AddUUID(uuid1004); - df2.AddUUID(uuid1003); + { + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(uuid1004); + df1.AddDeviceFilter(device_filter); + } + { + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(uuid1003); + df2.AddDeviceFilter(device_filter); + } ASSERT_TRUE(df1.Equals(df2)); // now transport don't match @@ -52,11 +81,41 @@ TEST(BluetoothDiscoveryFilterTest, Equal) { df1.SetRSSI(-30); ASSERT_FALSE(df1.Equals(df2)); + // set RSSIs to be the same and confirm that + // the filters match to prepare for next test + df2.SetRSSI(-30); + ASSERT_TRUE(df1.Equals(df2)); + + // add filters with the same uuid but different names + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter_no_name; + device_filter_no_name.uuids.insert(uuid1019); + df1.AddDeviceFilter(device_filter_no_name); + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter_name; + device_filter_name.uuids.insert(uuid1019); + device_filter_name.name = "device 1019"; + df2.AddDeviceFilter(device_filter_name); + + // with different names the filters should not be the same + ASSERT_FALSE(df1.Equals(df2)); + BluetoothDiscoveryFilter df3(BLUETOOTH_TRANSPORT_CLASSIC); df3.SetPathloss(45); - df3.AddUUID(uuid1020); - df3.AddUUID(uuid1003); - df3.AddUUID(uuid1004); + { + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(uuid1020); + device_filter.uuids.insert(uuid1057); + df3.AddDeviceFilter(device_filter); + } + { + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(uuid1003); + df3.AddDeviceFilter(device_filter); + } + { + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(uuid1004); + df3.AddDeviceFilter(device_filter); + } // Having Pathloss and RSSI set in two different filter makes them unequal. ASSERT_FALSE(df1.Equals(df3)); @@ -65,8 +124,17 @@ TEST(BluetoothDiscoveryFilterTest, Equal) { TEST(BluetoothDiscoveryFilterTest, CopyFrom) { BluetoothDiscoveryFilter df1(BLUETOOTH_TRANSPORT_CLASSIC); df1.SetRSSI(-65); - df1.AddUUID(uuid1020); - df1.AddUUID(uuid1003); + { + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(uuid1020); + device_filter.uuids.insert(uuid1057); + df1.AddDeviceFilter(device_filter); + } + { + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(uuid1003); + df1.AddDeviceFilter(device_filter); + } BluetoothDiscoveryFilter df2(BLUETOOTH_TRANSPORT_CLASSIC); @@ -82,18 +150,37 @@ TEST(BluetoothDiscoveryFilterTest, CopyFrom) { EXPECT_EQ(BLUETOOTH_TRANSPORT_CLASSIC, df2.GetTransport()); df2.GetUUIDs(out_uuids); - EXPECT_TRUE(out_uuids.find(uuid1020) != out_uuids.end()); - EXPECT_TRUE(out_uuids.find(uuid1003) != out_uuids.end()); + EXPECT_THAT(out_uuids, testing::Contains(uuid1020)); + EXPECT_THAT(out_uuids, testing::Contains(uuid1057)); + EXPECT_THAT(out_uuids, testing::Contains(uuid1003)); } TEST(BluetoothDiscoveryFilterTest, MergeUUIDs) { BluetoothDiscoveryFilter df1(BLUETOOTH_TRANSPORT_LE); - df1.AddUUID(uuid1020); - df1.AddUUID(uuid1003); + { + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(uuid1020); + device_filter.uuids.insert(uuid1057); + df1.AddDeviceFilter(device_filter); + } + { + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(uuid1003); + df1.AddDeviceFilter(device_filter); + } BluetoothDiscoveryFilter df2(BLUETOOTH_TRANSPORT_LE); - df2.AddUUID(uuid1020); - df2.AddUUID(uuid1004); + { + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(uuid1020); + device_filter.uuids.insert(uuid1057); + df2.AddDeviceFilter(device_filter); + } + { + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(uuid1004); + df2.AddDeviceFilter(device_filter); + } std::unique_ptr<BluetoothDiscoveryFilter> df3 = BluetoothDiscoveryFilter::Merge(&df1, &df2); diff --git a/chromium/device/bluetooth/bluetooth_task_manager_win.cc b/chromium/device/bluetooth/bluetooth_task_manager_win.cc index 26599ed71dd..91b9f7229a2 100644 --- a/chromium/device/bluetooth/bluetooth_task_manager_win.cc +++ b/chromium/device/bluetooth/bluetooth_task_manager_win.cc @@ -455,12 +455,8 @@ void BluetoothTaskManagerWin::StopDiscovery() { void BluetoothTaskManagerWin::DiscoverDevices(int timeout_multiplier) { DCHECK(bluetooth_task_runner_->RunsTasksInCurrentSequence()); - if (!discovering_ || !classic_wrapper_->HasHandle()) { - ui_task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&BluetoothTaskManagerWin::OnDiscoveryStopped, this)); + if (!discovering_ || !classic_wrapper_->HasHandle()) return; - } std::vector<std::unique_ptr<DeviceState>> device_list; if (SearchDevices(timeout_multiplier, false, &device_list)) { diff --git a/chromium/device/bluetooth/bluez/bluetooth_adapter_bluez.cc b/chromium/device/bluetooth/bluez/bluetooth_adapter_bluez.cc index 1b687557e72..e216180adc0 100644 --- a/chromium/device/bluetooth/bluez/bluetooth_adapter_bluez.cc +++ b/chromium/device/bluetooth/bluez/bluetooth_adapter_bluez.cc @@ -1476,6 +1476,10 @@ void BluetoothAdapterBlueZ::OnPropertyChangeCompleted( } } +base::WeakPtr<BluetoothAdapter> BluetoothAdapterBlueZ::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + // BluetoothAdapterBlueZ should override SetPowered() instead. bool BluetoothAdapterBlueZ::SetPoweredImpl(bool powered) { NOTREACHED(); diff --git a/chromium/device/bluetooth/bluez/bluetooth_adapter_bluez.h b/chromium/device/bluetooth/bluez/bluetooth_adapter_bluez.h index 0734e5f0e27..8e8d5f24ea1 100644 --- a/chromium/device/bluetooth/bluez/bluetooth_adapter_bluez.h +++ b/chromium/device/bluetooth/bluez/bluetooth_adapter_bluez.h @@ -74,7 +74,7 @@ class BluetoothPairingBlueZ; // // When adding methods to this class verify shutdown behavior in // BluetoothBlueZTest, Shutdown. -class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterBlueZ +class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterBlueZ final : public device::BluetoothAdapter, public bluez::BluetoothAdapterClient::Observer, public bluez::BluetoothDeviceClient::Observer, @@ -372,6 +372,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterBlueZ bool success); // BluetoothAdapter: + base::WeakPtr<BluetoothAdapter> GetWeakPtr() override; bool SetPoweredImpl(bool powered) override; void StartScanWithFilter( std::unique_ptr<device::BluetoothDiscoveryFilter> discovery_filter, diff --git a/chromium/device/bluetooth/bluez/bluetooth_advertisement_bluez_unittest.cc b/chromium/device/bluetooth/bluez/bluetooth_advertisement_bluez_unittest.cc index 292b9e2e527..d84b5afed1d 100644 --- a/chromium/device/bluetooth/bluez/bluetooth_advertisement_bluez_unittest.cc +++ b/chromium/device/bluetooth/bluez/bluetooth_advertisement_bluez_unittest.cc @@ -12,8 +12,8 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" +#include "base/test/task_environment.h" #include "device/bluetooth/bluetooth_adapter.h" #include "device/bluetooth/bluetooth_adapter_factory.h" #include "device/bluetooth/bluetooth_advertisement.h" @@ -166,7 +166,8 @@ class BluetoothAdvertisementBlueZTest : public testing::Test { BluetoothAdvertisement::ErrorCode last_error_code_; - base::MessageLoopForIO message_loop_; + base::test::SingleThreadTaskEnvironment task_environment_{ + base::test::SingleThreadTaskEnvironment::MainThreadType::IO}; std::unique_ptr<TestBluetoothAdvertisementObserver> observer_; scoped_refptr<BluetoothAdapter> adapter_; diff --git a/chromium/device/bluetooth/bluez/bluetooth_bluez_unittest.cc b/chromium/device/bluetooth/bluez/bluetooth_bluez_unittest.cc index 19585df3874..e7f8620c018 100644 --- a/chromium/device/bluetooth/bluez/bluetooth_bluez_unittest.cc +++ b/chromium/device/bluetooth/bluez/bluetooth_bluez_unittest.cc @@ -313,7 +313,7 @@ class BluetoothBlueZTest : public testing::Test { } protected: - base::test::TaskEnvironment task_environment_; + base::test::SingleThreadTaskEnvironment task_environment_; bluez::FakeBluetoothAdapterClient* fake_bluetooth_adapter_client_; bluez::FakeBluetoothDeviceClient* fake_bluetooth_device_client_; scoped_refptr<BluetoothAdapter> adapter_; @@ -1334,15 +1334,18 @@ TEST_F(BluetoothBlueZTest, SetDiscoveryFilterBeforeStartDiscovery) { TestBluetoothAdapterObserver observer(adapter_); - BluetoothDiscoveryFilter* df = - new BluetoothDiscoveryFilter(device::BLUETOOTH_TRANSPORT_LE); - df->SetRSSI(-60); - df->AddUUID(BluetoothUUID("1000")); - std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter(df); + auto discovery_filter = std::make_unique<BluetoothDiscoveryFilter>( + device::BLUETOOTH_TRANSPORT_LE); + discovery_filter->SetRSSI(-60); + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(BluetoothUUID("1000")); + discovery_filter->AddDeviceFilter(std::move(device_filter)); adapter_->SetPowered( true, base::Bind(&BluetoothBlueZTest::Callback, base::Unretained(this)), base::Bind(&BluetoothBlueZTest::ErrorCallback, base::Unretained(this))); + + auto* comparison_filter_holder = discovery_filter.get(); adapter_->StartDiscoverySessionWithFilter( std::move(discovery_filter), base::Bind(&BluetoothBlueZTest::DiscoverySessionCallback, @@ -1357,7 +1360,8 @@ TEST_F(BluetoothBlueZTest, SetDiscoveryFilterBeforeStartDiscovery) { ASSERT_TRUE(adapter_->IsDiscovering()); ASSERT_EQ((size_t)1, discovery_sessions_.size()); ASSERT_TRUE(discovery_sessions_[0]->IsActive()); - ASSERT_TRUE(df->Equals(*discovery_sessions_[0]->GetDiscoveryFilter())); + ASSERT_TRUE(comparison_filter_holder->Equals( + *discovery_sessions_[0]->GetDiscoveryFilter())); auto* filter = fake_bluetooth_adapter_client_->GetDiscoveryFilter(); EXPECT_NE(nullptr, filter); @@ -1394,11 +1398,12 @@ TEST_F(BluetoothBlueZTest, SetDiscoveryFilterBeforeStartDiscoveryFail) { TestBluetoothAdapterObserver observer(adapter_); - BluetoothDiscoveryFilter* df = - new BluetoothDiscoveryFilter(device::BLUETOOTH_TRANSPORT_LE); - df->SetRSSI(-60); - df->AddUUID(BluetoothUUID("1000")); - std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter(df); + auto discovery_filter = std::make_unique<BluetoothDiscoveryFilter>( + device::BLUETOOTH_TRANSPORT_LE); + discovery_filter->SetRSSI(-60); + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(BluetoothUUID("1000")); + discovery_filter->AddDeviceFilter(std::move(device_filter)); adapter_->SetPowered( true, base::Bind(&BluetoothBlueZTest::Callback, base::Unretained(this)), @@ -1445,25 +1450,30 @@ TEST_F(BluetoothBlueZTest, SetDiscoveryFilterBeforeStartDiscoveryMultiple) { for (int i = 0; i < 3; i++) { std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter; if (i == 0) { - BluetoothDiscoveryFilter* df = - new BluetoothDiscoveryFilter(device::BLUETOOTH_TRANSPORT_LE); - df->SetRSSI(-85); - df->AddUUID(BluetoothUUID("1000")); - discovery_filter.reset(df); + discovery_filter = std::make_unique<BluetoothDiscoveryFilter>( + device::BLUETOOTH_TRANSPORT_LE); + discovery_filter->SetRSSI(-85); + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(BluetoothUUID("1000")); + discovery_filter->AddDeviceFilter(std::move(device_filter)); } else if (i == 1) { - BluetoothDiscoveryFilter* df = - new BluetoothDiscoveryFilter(device::BLUETOOTH_TRANSPORT_LE); - df->SetRSSI(-60); - df->AddUUID(BluetoothUUID("1020")); - df->AddUUID(BluetoothUUID("1001")); - discovery_filter.reset(df); + discovery_filter = std::make_unique<BluetoothDiscoveryFilter>( + device::BLUETOOTH_TRANSPORT_LE); + discovery_filter->SetRSSI(-60); + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(BluetoothUUID("1020")); + device_filter.uuids.insert(BluetoothUUID("1001")); + discovery_filter->AddDeviceFilter(std::move(device_filter)); } else if (i == 2) { - BluetoothDiscoveryFilter* df = - new BluetoothDiscoveryFilter(device::BLUETOOTH_TRANSPORT_LE); - df->SetRSSI(-65); - df->AddUUID(BluetoothUUID("1020")); - df->AddUUID(BluetoothUUID("1003")); - discovery_filter.reset(df); + discovery_filter = std::make_unique<BluetoothDiscoveryFilter>( + device::BLUETOOTH_TRANSPORT_LE); + discovery_filter->SetRSSI(-65); + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(BluetoothUUID("1020")); + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter2; + device_filter2.uuids.insert(BluetoothUUID("1003")); + discovery_filter->AddDeviceFilter(std::move(device_filter)); + discovery_filter->AddDeviceFilter(std::move(device_filter2)); } adapter_->StartDiscoverySessionWithFilter( @@ -1569,25 +1579,30 @@ TEST_F(BluetoothBlueZTest, SetDiscoveryFilterBeforeStartDiscoveryMultiple) { std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter; if (i == 0) { - BluetoothDiscoveryFilter* df = - new BluetoothDiscoveryFilter(device::BLUETOOTH_TRANSPORT_LE); - df->SetRSSI(-85); - df->AddUUID(BluetoothUUID("1000")); - discovery_filter.reset(df); + discovery_filter = std::make_unique<BluetoothDiscoveryFilter>( + device::BLUETOOTH_TRANSPORT_LE); + discovery_filter->SetRSSI(-85); + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(BluetoothUUID("1000")); + discovery_filter->AddDeviceFilter(std::move(device_filter)); } else if (i == 1) { - BluetoothDiscoveryFilter* df = - new BluetoothDiscoveryFilter(device::BLUETOOTH_TRANSPORT_LE); - df->SetRSSI(-60); - df->AddUUID(BluetoothUUID("1020")); - df->AddUUID(BluetoothUUID("1001")); - discovery_filter.reset(df); + discovery_filter = std::make_unique<BluetoothDiscoveryFilter>( + device::BLUETOOTH_TRANSPORT_LE); + discovery_filter->SetRSSI(-60); + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(BluetoothUUID("1020")); + device_filter.uuids.insert(BluetoothUUID("1001")); + discovery_filter->AddDeviceFilter(std::move(device_filter)); } else if (i == 2) { - BluetoothDiscoveryFilter* df = - new BluetoothDiscoveryFilter(device::BLUETOOTH_TRANSPORT_LE); - df->SetRSSI(-65); - df->AddUUID(BluetoothUUID("1020")); - df->AddUUID(BluetoothUUID("1003")); - discovery_filter.reset(df); + discovery_filter = std::make_unique<BluetoothDiscoveryFilter>( + device::BLUETOOTH_TRANSPORT_LE); + discovery_filter->SetRSSI(-65); + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(BluetoothUUID("1020")); + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter2; + device_filter2.uuids.insert(BluetoothUUID("1003")); + discovery_filter->AddDeviceFilter(std::move(device_filter)); + discovery_filter->AddDeviceFilter(std::move(device_filter2)); } adapter_->StartDiscoverySessionWithFilter( @@ -1668,7 +1683,9 @@ TEST_F(BluetoothBlueZTest, SetDiscoveryFilterMergingTest) { BluetoothDiscoveryFilter* df = new BluetoothDiscoveryFilter(device::BLUETOOTH_TRANSPORT_LE); df->SetRSSI(-15); - df->AddUUID(BluetoothUUID("1000")); + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(BluetoothUUID("1000")); + df->AddDeviceFilter(std::move(device_filter)); std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter(df); adapter_->StartDiscoverySessionWithFilter( @@ -1688,8 +1705,10 @@ TEST_F(BluetoothBlueZTest, SetDiscoveryFilterMergingTest) { df = new BluetoothDiscoveryFilter(device::BLUETOOTH_TRANSPORT_LE); df->SetRSSI(-60); - df->AddUUID(BluetoothUUID("1020")); - df->AddUUID(BluetoothUUID("1001")); + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter2; + device_filter2.uuids.insert(BluetoothUUID("1020")); + device_filter2.uuids.insert(BluetoothUUID("1001")); + df->AddDeviceFilter(device_filter2); discovery_filter = std::unique_ptr<BluetoothDiscoveryFilter>(df); adapter_->StartDiscoverySessionWithFilter( @@ -1712,8 +1731,12 @@ TEST_F(BluetoothBlueZTest, SetDiscoveryFilterMergingTest) { BluetoothDiscoveryFilter* df3 = new BluetoothDiscoveryFilter(device::BLUETOOTH_TRANSPORT_CLASSIC); df3->SetRSSI(-65); - df3->AddUUID(BluetoothUUID("1020")); - df3->AddUUID(BluetoothUUID("1003")); + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter3; + device_filter3.uuids.insert(BluetoothUUID("1020")); + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter4; + device_filter4.uuids.insert(BluetoothUUID("1003")); + df3->AddDeviceFilter(device_filter3); + df3->AddDeviceFilter(device_filter4); std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter3(df3); adapter_->StartDiscoverySessionWithFilter( diff --git a/chromium/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc b/chromium/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc index 21ccaa1578e..fc0bc6b40a5 100644 --- a/chromium/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc +++ b/chromium/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc @@ -84,6 +84,13 @@ bool ValuesEqual(const std::vector<uint8_t>& value0, return true; } +void AddDeviceFilterWithUUID(BluetoothDiscoveryFilter* filter, + BluetoothUUID uuid) { + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(uuid); + filter->AddDeviceFilter(device_filter); +} + } // namespace class BluetoothGattBlueZTest : public testing::Test { @@ -287,7 +294,7 @@ class BluetoothGattBlueZTest : public testing::Test { base::RunLoop::QuitCurrentWhenIdleDeprecated(); } - base::test::TaskEnvironment task_environment_; + base::test::SingleThreadTaskEnvironment task_environment_; bluez::FakeBluetoothDeviceClient* fake_bluetooth_device_client_; bluez::FakeBluetoothGattServiceClient* fake_bluetooth_gatt_service_client_; @@ -324,7 +331,7 @@ TEST_F(BluetoothGattBlueZTest, AddDualDevice(); BluetoothDiscoveryFilter discovery_filter(device::BLUETOOTH_TRANSPORT_LE); - discovery_filter.AddUUID(kBatteryServiceUUID); + AddDeviceFilterWithUUID(&discovery_filter, kBatteryServiceUUID); DeviceToUUIDs result = adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter( @@ -340,7 +347,9 @@ TEST_F( BluetoothDevice* dual = AddDualDevice(); BluetoothDiscoveryFilter discovery_filter(device::BLUETOOTH_TRANSPORT_LE); - discovery_filter.AddUUID(kGenericAccessServiceUUID); + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(kGenericAccessServiceUUID); + discovery_filter.AddDeviceFilter(device_filter); DeviceToUUIDs result = adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter( @@ -357,7 +366,7 @@ TEST_F( BluetoothDevice* dual = AddDualDevice(); BluetoothDiscoveryFilter discovery_filter(device::BLUETOOTH_TRANSPORT_LE); - discovery_filter.AddUUID(kHeartRateServiceUUID); + AddDeviceFilterWithUUID(&discovery_filter, kHeartRateServiceUUID); DeviceToUUIDs result = adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter( @@ -374,8 +383,8 @@ TEST_F(BluetoothGattBlueZTest, BluetoothDevice* dual = AddDualDevice(); BluetoothDiscoveryFilter discovery_filter(device::BLUETOOTH_TRANSPORT_LE); - discovery_filter.AddUUID(kGenericAccessServiceUUID); - discovery_filter.AddUUID(kHeartRateServiceUUID); + AddDeviceFilterWithUUID(&discovery_filter, kGenericAccessServiceUUID); + AddDeviceFilterWithUUID(&discovery_filter, kHeartRateServiceUUID); DeviceToUUIDs result = adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter( @@ -393,8 +402,12 @@ TEST_F( AddLeDevice(); BluetoothDevice* dual = AddDualDevice(); BluetoothDiscoveryFilter discovery_filter(device::BLUETOOTH_TRANSPORT_LE); - discovery_filter.AddUUID(kGenericAccessServiceUUID); - discovery_filter.AddUUID(kBatteryServiceUUID); + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(kGenericAccessServiceUUID); + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter2; + device_filter2.uuids.insert(kBatteryServiceUUID); + discovery_filter.AddDeviceFilter(device_filter); + discovery_filter.AddDeviceFilter(device_filter2); DeviceToUUIDs result = adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter( diff --git a/chromium/device/bluetooth/cast/bluetooth_adapter_cast.cc b/chromium/device/bluetooth/cast/bluetooth_adapter_cast.cc index 343ee9ff143..71028ce2b53 100644 --- a/chromium/device/bluetooth/cast/bluetooth_adapter_cast.cc +++ b/chromium/device/bluetooth/cast/bluetooth_adapter_cast.cc @@ -180,6 +180,11 @@ BluetoothLocalGattService* BluetoothAdapterCast::GetGattService( return nullptr; } +base::WeakPtr<BluetoothAdapter> BluetoothAdapterCast::GetWeakPtr() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return weak_factory_.GetWeakPtr(); +} + bool BluetoothAdapterCast::SetPoweredImpl(bool powered) { NOTREACHED() << "This method is not invoked when SetPowered() is overridden."; return true; @@ -247,11 +252,6 @@ void BluetoothAdapterCast::RemovePairingDelegateInternal( NOTIMPLEMENTED(); } -base::WeakPtr<BluetoothAdapterCast> BluetoothAdapterCast::GetWeakPtr() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return weak_factory_.GetWeakPtr(); -} - void BluetoothAdapterCast::OnConnectChanged( scoped_refptr<chromecast::bluetooth::RemoteDevice> device, bool connected) { diff --git a/chromium/device/bluetooth/cast/bluetooth_adapter_cast.h b/chromium/device/bluetooth/cast/bluetooth_adapter_cast.h index 7be88e9982c..5f3b9fd60ce 100644 --- a/chromium/device/bluetooth/cast/bluetooth_adapter_cast.h +++ b/chromium/device/bluetooth/cast/bluetooth_adapter_cast.h @@ -89,6 +89,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterCast const AdvertisementErrorCallback& error_callback) override; BluetoothLocalGattService* GetGattService( const std::string& identifier) const override; + base::WeakPtr<BluetoothAdapter> GetWeakPtr() override; bool SetPoweredImpl(bool powered) override; void StartScanWithFilter( std::unique_ptr<device::BluetoothDiscoveryFilter> discovery_filter, @@ -100,12 +101,6 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterCast void RemovePairingDelegateInternal( BluetoothDevice::PairingDelegate* pairing_delegate) override; - // Return a WeakPtr for this class. Must be called on the sequence on which - // this class was created. - // TODO(slan): Remove this once this class talks to a dedicated Bluetooth - // service (b/76155468) - base::WeakPtr<BluetoothAdapterCast> GetWeakPtr(); - // |factory_cb| is used to inject a factory method from ChromecastService into // this class. It will be invoked when Create() is called. // TODO(slan): Remove this once this class talks to a dedicated Bluetooth diff --git a/chromium/device/bluetooth/chromeos/bluetooth_utils.cc b/chromium/device/bluetooth/chromeos/bluetooth_utils.cc index 3a5a84df481..dd5075c721e 100644 --- a/chromium/device/bluetooth/chromeos/bluetooth_utils.cc +++ b/chromium/device/bluetooth/chromeos/bluetooth_utils.cc @@ -84,6 +84,14 @@ BluetoothAdapter::DeviceList FilterUnknownDevices( continue; } + // Always filter out phones. There is no intended use case or Bluetooth + // profile in this context. + if (base::FeatureList::IsEnabled( + chromeos::features::kBluetoothPhoneFilter) && + device->GetDeviceType() == BluetoothDeviceType::PHONE) { + continue; + } + switch (device->GetType()) { // Device with invalid bluetooth transport is filtered out. case BLUETOOTH_TRANSPORT_INVALID: diff --git a/chromium/device/bluetooth/chromeos/bluetooth_utils_unittest.cc b/chromium/device/bluetooth/chromeos/bluetooth_utils_unittest.cc index 468193532f2..b9c3d3dfd36 100644 --- a/chromium/device/bluetooth/chromeos/bluetooth_utils_unittest.cc +++ b/chromium/device/bluetooth/chromeos/bluetooth_utils_unittest.cc @@ -245,6 +245,17 @@ TEST_F( 0u /* num_expected_remaining_devices */); } +TEST_F(BluetoothUtilsTest, + TestFilterBluetoothDeviceList_FilterKnown_RemoveAppearancePhone) { + auto* mock_bluetooth_device = + AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_DUAL); + ON_CALL(*mock_bluetooth_device, GetDeviceType) + .WillByDefault(testing::Return(BluetoothDeviceType::PHONE)); + + VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN, + 0u /* num_expected_remaining_devices */); +} + TEST_F(BluetoothUtilsTest, TestGetBlockedLongTermKeys_ListIncludesBadLtks) { // One nibble too long, one nibble too short, and one nibble just right. std::string hex_key_1 = "000000000000000000000000000012345"; diff --git a/chromium/device/bluetooth/dbus/bluez_dbus_manager.cc b/chromium/device/bluetooth/dbus/bluez_dbus_manager.cc index 853fe8a5106..4c3b949ebac 100644 --- a/chromium/device/bluetooth/dbus/bluez_dbus_manager.cc +++ b/chromium/device/bluetooth/dbus/bluez_dbus_manager.cc @@ -235,14 +235,9 @@ void BluezDBusManager::InitializeClients() { } std::string BluezDBusManager::GetBluetoothServiceName() { - 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; + // TODO(b/145163508): Remove the NewBlue feature flag as Bluetooth service is + // now always BlueZ. + return bluez_object_manager::kBluezObjectManagerServiceName; } // static diff --git a/chromium/device/bluetooth/device.cc b/chromium/device/bluetooth/device.cc index 75c276ee759..41ef648d677 100644 --- a/chromium/device/bluetooth/device.cc +++ b/chromium/device/bluetooth/device.cc @@ -10,7 +10,7 @@ #include "base/strings/utf_string_conversions.h" #include "device/bluetooth/device.h" #include "device/bluetooth/public/mojom/gatt_result_type_converter.h" -#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/cpp/bindings/self_owned_receiver.h" namespace bluetooth { Device::~Device() { @@ -20,12 +20,12 @@ Device::~Device() { // static void Device::Create(scoped_refptr<device::BluetoothAdapter> adapter, std::unique_ptr<device::BluetoothGattConnection> connection, - mojom::DeviceRequest request) { + mojo::PendingReceiver<mojom::Device> receiver) { auto device_impl = base::WrapUnique(new Device(adapter, std::move(connection))); auto* device_ptr = device_impl.get(); - device_ptr->binding_ = - mojo::MakeStrongBinding(std::move(device_impl), std::move(request)); + device_ptr->receiver_ = + mojo::MakeSelfOwnedReceiver(std::move(device_impl), std::move(receiver)); } // static @@ -54,7 +54,7 @@ void Device::DeviceChanged(device::BluetoothAdapter* adapter, } if (!device->IsGattConnected()) { - binding_->Close(); + receiver_->Close(); } } @@ -72,7 +72,7 @@ void Device::GattServicesDiscovered(device::BluetoothAdapter* adapter, } void Device::Disconnect() { - binding_->Close(); + receiver_->Close(); } void Device::GetInfo(GetInfoCallback callback) { diff --git a/chromium/device/bluetooth/device.h b/chromium/device/bluetooth/device.h index b74276b5644..953fb9bf097 100644 --- a/chromium/device/bluetooth/device.h +++ b/chromium/device/bluetooth/device.h @@ -19,7 +19,7 @@ #include "device/bluetooth/bluetooth_remote_gatt_descriptor.h" #include "device/bluetooth/bluetooth_remote_gatt_service.h" #include "device/bluetooth/public/mojom/device.mojom.h" -#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/cpp/bindings/self_owned_receiver.h" namespace bluetooth { @@ -37,7 +37,7 @@ class Device : public mojom::Device, public device::BluetoothAdapter::Observer { static void Create( scoped_refptr<device::BluetoothAdapter> adapter, std::unique_ptr<device::BluetoothGattConnection> connection, - mojom::DeviceRequest request); + mojo::PendingReceiver<mojom::Device> receiver); // Creates a mojom::DeviceInfo using info from the given |device|. static mojom::DeviceInfoPtr ConstructDeviceInfoStruct( @@ -122,7 +122,7 @@ class Device : public mojom::Device, public device::BluetoothAdapter::Observer { // The GATT connection to this device. std::unique_ptr<device::BluetoothGattConnection> connection_; - mojo::StrongBindingPtr<mojom::Device> binding_; + mojo::SelfOwnedReceiverRef<mojom::Device> receiver_; // The services request queue which holds callbacks that are waiting for // services to be discovered for this device. diff --git a/chromium/device/bluetooth/device_unittest.cc b/chromium/device/bluetooth/device_unittest.cc index b87b5df49b3..18336a406ee 100644 --- a/chromium/device/bluetooth/device_unittest.cc +++ b/chromium/device/bluetooth/device_unittest.cc @@ -19,6 +19,7 @@ #include "device/bluetooth/test/mock_bluetooth_gatt_characteristic.h" #include "device/bluetooth/test/mock_bluetooth_gatt_connection.h" #include "device/bluetooth/test/mock_bluetooth_gatt_service.h" +#include "mojo/public/cpp/bindings/remote.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::Return; @@ -121,9 +122,10 @@ class BluetoothInterfaceDeviceTest : public testing::Test { auto connection = std::make_unique<NiceMockBluetoothGattConnection>( adapter_, device_.GetAddress()); - Device::Create(adapter_, std::move(connection), mojo::MakeRequest(&proxy_)); + Device::Create(adapter_, std::move(connection), + proxy_.BindNewPipeAndPassReceiver()); - proxy_.set_connection_error_handler( + proxy_.set_disconnect_handler( base::BindOnce(&BluetoothInterfaceDeviceTest::OnConnectionError, weak_factory_.GetWeakPtr())); } @@ -174,8 +176,7 @@ class BluetoothInterfaceDeviceTest : public testing::Test { scoped_refptr<NiceMockBluetoothAdapter> adapter_; NiceMockBluetoothDevice device_; base::test::SingleThreadTaskEnvironment task_environment_; - mojom::DevicePtr proxy_; - mojo::StrongBindingPtr<mojom::Device> binding_ptr_; + mojo::Remote<mojom::Device> proxy_; bool message_pipe_closed_ = false; bool expect_device_service_deleted_ = false; diff --git a/chromium/device/bluetooth/public/mojom/adapter.mojom b/chromium/device/bluetooth/public/mojom/adapter.mojom index 2225cf58e97..cd79f593b48 100644 --- a/chromium/device/bluetooth/public/mojom/adapter.mojom +++ b/chromium/device/bluetooth/public/mojom/adapter.mojom @@ -55,7 +55,8 @@ interface Adapter { // Creates a GATT connection to the device with |address| and returns a // Device if the connection was succesful. The GATT connection is tied to the // the lifetime of the Device message pipe. - ConnectToDevice(string address) => (ConnectResult result, Device? device); + ConnectToDevice(string address) => + (ConnectResult result, pending_remote<Device>? device); // Retrieves the list of the devices known by the adapter including Connected // Devices, GATT Connected Devices, Paired Devices and Devices discovered @@ -66,11 +67,11 @@ interface Adapter { GetInfo() => (AdapterInfo info); // Sets the client that listens for the adapter's events. - SetClient(AdapterClient client); + SetClient(pending_remote<AdapterClient> client); // Requests the adapter to start a new discovery session. Returns null if // session not created successfully. - StartDiscoverySession() => (DiscoverySession? session); + StartDiscoverySession() => (pending_remote<DiscoverySession>? session); }; interface AdapterClient { diff --git a/chromium/device/bluetooth/public/mojom/test/fake_bluetooth.mojom b/chromium/device/bluetooth/public/mojom/test/fake_bluetooth.mojom index 687425164c6..710be64b77d 100644 --- a/chromium/device/bluetooth/public/mojom/test/fake_bluetooth.mojom +++ b/chromium/device/bluetooth/public/mojom/test/fake_bluetooth.mojom @@ -81,7 +81,8 @@ interface FakeBluetooth { SetLESupported(bool available) => (); // Initializes a fake Central with |state| as the initial state. - SimulateCentral(CentralState state) => (FakeCentral fake_central); + SimulateCentral(CentralState state) => + (pending_remote<FakeCentral> fake_central); // Evaluates whether all responses set by this API have been consumed by this // central or otherwise. This includes responses set by: diff --git a/chromium/device/fido/BUILD.gn b/chromium/device/fido/BUILD.gn index d71340bd9f4..798345eb8ca 100644 --- a/chromium/device/fido/BUILD.gn +++ b/chromium/device/fido/BUILD.gn @@ -224,6 +224,8 @@ component("fido") { "win/authenticator.h", "win/discovery.cc", "win/discovery.h", + "win/fake_webauthn_api.cc", + "win/fake_webauthn_api.h", "win/logging.cc", "win/logging.h", "win/type_conversions.cc", @@ -362,11 +364,4 @@ source_set("test_support") { "mac/scoped_touch_id_test_environment.mm", ] } - - if (is_win) { - sources += [ - "win/fake_webauthn_api.cc", - "win/fake_webauthn_api.h", - ] - } } diff --git a/chromium/device/fido/bio/enrollment_handler.cc b/chromium/device/fido/bio/enrollment_handler.cc index a15d97b1e70..9ce4850b9d8 100644 --- a/chromium/device/fido/bio/enrollment_handler.cc +++ b/chromium/device/fido/bio/enrollment_handler.cc @@ -30,42 +30,32 @@ BioEnrollmentHandler::~BioEnrollmentHandler() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } -void BioEnrollmentHandler::GetModality(ResponseCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(authenticator_); - authenticator_->GetModality(std::move(callback)); -} - -void BioEnrollmentHandler::GetSensorInfo(ResponseCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(authenticator_); - authenticator_->GetSensorInfo(std::move(callback)); -} - -void BioEnrollmentHandler::EnrollTemplate(SampleCallback sample_callback, - StatusCallback completion_callback) { +void BioEnrollmentHandler::EnrollTemplate( + SampleCallback sample_callback, + CompletionCallback completion_callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_EQ(state_, State::kReady); + state_ = State::kEnrolling; authenticator_->BioEnrollFingerprint( - *pin_token_response_, std::move(sample_callback), - base::BindOnce(&BioEnrollmentHandler::OnEnrollTemplateFinished, - weak_factory_.GetWeakPtr(), - std::move(completion_callback))); + *pin_token_response_, /*template_id=*/base::nullopt, + base::BindOnce(&BioEnrollmentHandler::OnEnrollResponse, + weak_factory_.GetWeakPtr(), std::move(sample_callback), + std::move(completion_callback), + /*current_template_id=*/base::nullopt)); } -void BioEnrollmentHandler::Cancel(StatusCallback callback) { +void BioEnrollmentHandler::CancelEnrollment() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // Must CTAPHID_CANCEL before cancelCurrentEnrollment so the - // authenticator doesn't queue the enrollment cancel behind - // an ongoing enrollment. + DCHECK_EQ(state_, State::kEnrolling); + state_ = State::kEnrollingPendingCancel; authenticator_->Cancel(); - authenticator_->BioEnrollCancel( - base::BindOnce(&BioEnrollmentHandler::OnCancel, - weak_factory_.GetWeakPtr(), std::move(callback))); } void BioEnrollmentHandler::EnumerateTemplates(EnumerationCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(pin_token_response_); + DCHECK_EQ(state_, State::kReady); + state_ = State::kEnumerating; authenticator_->BioEnrollEnumerate( *pin_token_response_, base::BindOnce(&BioEnrollmentHandler::OnEnumerateTemplates, @@ -76,23 +66,30 @@ void BioEnrollmentHandler::RenameTemplate(std::vector<uint8_t> template_id, std::string name, StatusCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_EQ(state_, State::kReady); + state_ = State::kRenaming; authenticator_->BioEnrollRename( *pin_token_response_, std::move(template_id), std::move(name), - base::BindOnce(&BioEnrollmentHandler::OnStatusCallback, + base::BindOnce(&BioEnrollmentHandler::OnRenameTemplate, weak_factory_.GetWeakPtr(), std::move(callback))); } void BioEnrollmentHandler::DeleteTemplate(std::vector<uint8_t> template_id, StatusCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_EQ(state_, State::kReady); + state_ = State::kDeleting; authenticator_->BioEnrollDelete( *pin_token_response_, std::move(template_id), - base::BindOnce(&BioEnrollmentHandler::OnStatusCallback, + base::BindOnce(&BioEnrollmentHandler::OnDeleteTemplate, weak_factory_.GetWeakPtr(), std::move(callback))); } void BioEnrollmentHandler::DispatchRequest(FidoAuthenticator* authenticator) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (state_ != State::kWaitingForTouch) { + return; + } authenticator->GetTouch(base::BindOnce(&BioEnrollmentHandler::OnTouch, weak_factory_.GetWeakPtr(), authenticator)); @@ -103,15 +100,20 @@ void BioEnrollmentHandler::AuthenticatorRemoved( FidoAuthenticator* authenticator) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); FidoRequestHandlerBase::AuthenticatorRemoved(discovery, authenticator); - if (authenticator_ != authenticator) { + if (authenticator_ != authenticator || state_ == State::kFinished) { return; } authenticator_ = nullptr; - std::move(error_callback_).Run(BioEnrollmentStatus::kSuccess); + Finish(BioEnrollmentStatus::kSuccess); } void BioEnrollmentHandler::OnTouch(FidoAuthenticator* authenticator) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (state_ != State::kWaitingForTouch) { + return; + } + CancelActiveAuthenticators(authenticator->GetId()); if (!authenticator->Options() || @@ -121,40 +123,39 @@ void BioEnrollmentHandler::OnTouch(FidoAuthenticator* authenticator) { authenticator->Options()->bio_enrollment_availability_preview == AuthenticatorSupportedOptions::BioEnrollmentAvailability:: kNotSupported)) { - std::move(error_callback_) - .Run(BioEnrollmentStatus::kAuthenticatorMissingBioEnrollment); + Finish(BioEnrollmentStatus::kAuthenticatorMissingBioEnrollment); return; } if (authenticator->Options()->client_pin_availability != AuthenticatorSupportedOptions::ClientPinAvailability:: kSupportedAndPinSet) { - std::move(error_callback_).Run(BioEnrollmentStatus::kNoPINSet); + Finish(BioEnrollmentStatus::kNoPINSet); return; } authenticator_ = authenticator; + state_ = State::kGettingRetries; authenticator_->GetRetries(base::BindOnce( &BioEnrollmentHandler::OnRetriesResponse, weak_factory_.GetWeakPtr())); } void BioEnrollmentHandler::OnRetriesResponse( - CtapDeviceResponseCode code, + CtapDeviceResponseCode status, base::Optional<pin::RetriesResponse> response) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!response || code != CtapDeviceResponseCode::kSuccess) { - FIDO_LOG(DEBUG) << "OnRetriesResponse failed with response code " - << static_cast<int>(code); - std::move(error_callback_) - .Run(BioEnrollmentStatus::kAuthenticatorResponseInvalid); + DCHECK_EQ(state_, State::kGettingRetries); + if (!response || status != CtapDeviceResponseCode::kSuccess) { + Finish(BioEnrollmentStatus::kAuthenticatorResponseInvalid); return; } if (response->retries == 0) { - std::move(error_callback_).Run(BioEnrollmentStatus::kHardPINBlock); + Finish(BioEnrollmentStatus::kHardPINBlock); return; } + state_ = State::kWaitingForPIN; get_pin_callback_.Run(response->retries, base::BindOnce(&BioEnrollmentHandler::OnHavePIN, weak_factory_.GetWeakPtr())); @@ -162,6 +163,8 @@ void BioEnrollmentHandler::OnRetriesResponse( void BioEnrollmentHandler::OnHavePIN(std::string pin) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_EQ(state_, State::kWaitingForPIN); + state_ = State::kGettingEphemeralKey; authenticator_->GetEphemeralKey( base::BindOnce(&BioEnrollmentHandler::OnHaveEphemeralKey, weak_factory_.GetWeakPtr(), std::move(pin))); @@ -169,16 +172,17 @@ void BioEnrollmentHandler::OnHavePIN(std::string pin) { void BioEnrollmentHandler::OnHaveEphemeralKey( std::string pin, - CtapDeviceResponseCode code, + CtapDeviceResponseCode status, base::Optional<pin::KeyAgreementResponse> response) { - if (code != CtapDeviceResponseCode::kSuccess) { - FIDO_LOG(DEBUG) << "OnHaveEphemeralKey failed with response code " - << static_cast<int>(code); - std::move(error_callback_) - .Run(BioEnrollmentStatus::kAuthenticatorResponseInvalid); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_EQ(State::kGettingEphemeralKey, state_); + + if (status != CtapDeviceResponseCode::kSuccess) { + Finish(BioEnrollmentStatus::kAuthenticatorResponseInvalid); return; } + state_ = State::kGettingPINToken; authenticator_->GetPINToken( std::move(pin), *response, base::BindOnce(&BioEnrollmentHandler::OnHavePINToken, @@ -186,78 +190,148 @@ void BioEnrollmentHandler::OnHaveEphemeralKey( } void BioEnrollmentHandler::OnHavePINToken( - CtapDeviceResponseCode code, + CtapDeviceResponseCode status, base::Optional<pin::TokenResponse> response) { - switch (code) { - case CtapDeviceResponseCode::kCtap2ErrPinInvalid: - authenticator_->GetRetries( - base::BindOnce(&BioEnrollmentHandler::OnRetriesResponse, - weak_factory_.GetWeakPtr())); - return; - case CtapDeviceResponseCode::kCtap2ErrPinAuthBlocked: - std::move(error_callback_).Run(BioEnrollmentStatus::kSoftPINBlock); - return; - case CtapDeviceResponseCode::kCtap2ErrPinBlocked: - std::move(error_callback_).Run(BioEnrollmentStatus::kHardPINBlock); - return; - default: - std::move(error_callback_) - .Run(BioEnrollmentStatus::kAuthenticatorResponseInvalid); - return; - case CtapDeviceResponseCode::kSuccess: - // fall through on success - break; + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_EQ(state_, State::kGettingPINToken); + + if (status != CtapDeviceResponseCode::kSuccess) { + switch (status) { + case CtapDeviceResponseCode::kCtap2ErrPinInvalid: + state_ = State::kGettingRetries; + authenticator_->GetRetries( + base::BindOnce(&BioEnrollmentHandler::OnRetriesResponse, + weak_factory_.GetWeakPtr())); + return; + case CtapDeviceResponseCode::kCtap2ErrPinAuthBlocked: + Finish(BioEnrollmentStatus::kSoftPINBlock); + return; + case CtapDeviceResponseCode::kCtap2ErrPinBlocked: + Finish(BioEnrollmentStatus::kHardPINBlock); + return; + default: + Finish(BioEnrollmentStatus::kAuthenticatorResponseInvalid); + return; + } } + state_ = State::kReady; pin_token_response_ = std::move(response); std::move(ready_callback_).Run(); } -void BioEnrollmentHandler::OnEnrollTemplateFinished( - StatusCallback callback, - CtapDeviceResponseCode code, +void BioEnrollmentHandler::OnEnrollResponse( + SampleCallback sample_callback, + CompletionCallback completion_callback, + base::Optional<std::vector<uint8_t>> current_template_id, + CtapDeviceResponseCode status, base::Optional<BioEnrollmentResponse> response) { - if (code == CtapDeviceResponseCode::kSuccess && - (!response || !response->last_status || !response->remaining_samples)) { - // Response is incomplete or invalid. - std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrOther); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(state_ == State::kEnrolling || + state_ == State::kEnrollingPendingCancel); + + if (state_ == State::kEnrollingPendingCancel) { + state_ = State::kCancellingEnrollment; + authenticator_->BioEnrollCancel(base::BindOnce( + &BioEnrollmentHandler::OnCancel, weak_factory_.GetWeakPtr(), + std::move(completion_callback))); + return; + } + + if (status != CtapDeviceResponseCode::kSuccess) { + state_ = State::kReady; + std::move(completion_callback).Run(status, {}); return; } - FIDO_LOG(DEBUG) << "Finished bio enrollment with response code " - << static_cast<int>(code); - std::move(callback).Run(code); + + if (!response || !response->last_status || !response->remaining_samples) { + Finish(BioEnrollmentStatus::kAuthenticatorResponseInvalid); + return; + } + + if (!current_template_id) { + if (!response->template_id) { + // The templateId response field is required in the first response of each + // enrollment. + Finish(BioEnrollmentStatus::kAuthenticatorResponseInvalid); + return; + } + current_template_id = *response->template_id; + } + + if (*response->remaining_samples > 0) { + // FidoAuthenticator automatically requests the next sample and + // will invoke this method again on response. + sample_callback.Run(*response->last_status, *response->remaining_samples); + authenticator_->BioEnrollFingerprint( + *pin_token_response_, current_template_id, + base::BindOnce(&BioEnrollmentHandler::OnEnrollResponse, + weak_factory_.GetWeakPtr(), std::move(sample_callback), + std::move(completion_callback), current_template_id)); + return; + } + + state_ = State::kReady; + std::move(completion_callback) + .Run(CtapDeviceResponseCode::kSuccess, std::move(*current_template_id)); } -void BioEnrollmentHandler::OnCancel(StatusCallback callback, - CtapDeviceResponseCode code, +void BioEnrollmentHandler::OnCancel(CompletionCallback callback, + CtapDeviceResponseCode status, base::Optional<BioEnrollmentResponse>) { - std::move(callback).Run(code); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_EQ(state_, State::kCancellingEnrollment); + state_ = State::kReady; + std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrKeepAliveCancel, {}); } void BioEnrollmentHandler::OnEnumerateTemplates( EnumerationCallback callback, - CtapDeviceResponseCode code, + CtapDeviceResponseCode status, base::Optional<BioEnrollmentResponse> response) { - if (code != CtapDeviceResponseCode::kSuccess) { - // Response is not valid if operation was not successful. - // Note that an empty enumeration returns kCtap2ErrInvalidOption. - std::move(callback).Run(code, base::nullopt); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_EQ(state_, State::kEnumerating); + + state_ = State::kReady; + + if (status != CtapDeviceResponseCode::kSuccess) { + std::move(callback).Run(status, base::nullopt); return; } + if (!response || !response->template_infos) { - // Response must have template_infos. - std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrOther, - base::nullopt); + Finish(BioEnrollmentStatus::kAuthenticatorResponseInvalid); return; } - std::move(callback).Run(code, std::move(*response->template_infos)); + + std::move(callback).Run(status, std::move(*response->template_infos)); +} + +void BioEnrollmentHandler::OnRenameTemplate( + StatusCallback callback, + CtapDeviceResponseCode status, + base::Optional<BioEnrollmentResponse> response) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_EQ(state_, State::kRenaming); + state_ = State::kReady; + std::move(callback).Run(status); } -void BioEnrollmentHandler::OnStatusCallback( +void BioEnrollmentHandler::OnDeleteTemplate( StatusCallback callback, - CtapDeviceResponseCode code, + CtapDeviceResponseCode status, base::Optional<BioEnrollmentResponse> response) { - std::move(callback).Run(code); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_EQ(state_, State::kDeleting); + state_ = State::kReady; + std::move(callback).Run(status); +} + +void BioEnrollmentHandler::Finish(BioEnrollmentStatus status) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_NE(state_, State::kFinished); + state_ = State::kFinished; + std::move(error_callback_).Run(status); } } // namespace device diff --git a/chromium/device/fido/bio/enrollment_handler.h b/chromium/device/fido/bio/enrollment_handler.h index 00024a6241d..6118710b78d 100644 --- a/chromium/device/fido/bio/enrollment_handler.h +++ b/chromium/device/fido/bio/enrollment_handler.h @@ -28,22 +28,26 @@ enum class BioEnrollmentStatus { kAuthenticatorMissingBioEnrollment, }; +// BioEnrollmentHandler exercises the CTAP2.1 authenticatorBioEnrollment +// sub-protocol for enrolling biometric templates on external authenticators +// supporting internal UV. class COMPONENT_EXPORT(DEVICE_FIDO) BioEnrollmentHandler : public FidoRequestHandlerBase { public: + using TemplateId = std::vector<uint8_t>; + using ErrorCallback = base::OnceCallback<void(BioEnrollmentStatus)>; using GetPINCallback = base::RepeatingCallback<void(int64_t retries, base::OnceCallback<void(std::string)>)>; - using ResponseCallback = - base::OnceCallback<void(CtapDeviceResponseCode, - base::Optional<BioEnrollmentResponse>)>; using StatusCallback = base::OnceCallback<void(CtapDeviceResponseCode)>; using EnumerationCallback = base::OnceCallback<void( CtapDeviceResponseCode, - base::Optional<std::map<std::vector<uint8_t>, std::string>>)>; + base::Optional<std::map<TemplateId, std::string>>)>; using SampleCallback = base::RepeatingCallback<void(BioEnrollmentSampleStatus, uint8_t)>; + using CompletionCallback = + base::OnceCallback<void(CtapDeviceResponseCode, TemplateId)>; BioEnrollmentHandler( service_manager::Connector* connector, @@ -54,14 +58,6 @@ class COMPONENT_EXPORT(DEVICE_FIDO) BioEnrollmentHandler FidoDiscoveryFactory* factory); ~BioEnrollmentHandler() override; - // Returns the modality of the authenticator's user verification. - // Currently, the only valid modality is fingerprint. - void GetModality(ResponseCallback); - - // Returns fingerprint sensor info for the authenticator. - // This includes number of required samples to enroll and sensor type. - void GetSensorInfo(ResponseCallback); - // Enrolls a new fingerprint template. The user must provide the required // number of samples by touching the authenticator's sensor repeatedly. // After each sample, or a timeout, |sample_callback| is invoked with the @@ -69,11 +65,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) BioEnrollmentHandler // the operation has been cancelled, |completion_callback| is invoked // with the operation status. void EnrollTemplate(SampleCallback sample_callback, - StatusCallback completion_callback); + CompletionCallback completion_callback); - // Cancels an ongoing enrollment, if any, and invokes the callback with - // |CtapDeviceResponseCode::kSuccess|. - void Cancel(StatusCallback); + // Cancels an ongoing enrollment, if any, and invokes the + // |completion_callback| passed to EnrollTemplate() with + // |CtapDeviceResponseCode::kCtap2ErrKeepAliveCancel|. + void CancelEnrollment(); // Requests a map of current enrollments from the authenticator. On success, // the callback is invoked with a map from template IDs to human-readable @@ -89,6 +86,22 @@ class COMPONENT_EXPORT(DEVICE_FIDO) BioEnrollmentHandler void DeleteTemplate(std::vector<uint8_t> template_id, StatusCallback); private: + enum class State { + kWaitingForTouch, + kGettingRetries, + kWaitingForPIN, + kGettingEphemeralKey, + kGettingPINToken, + kReady, + kEnrolling, + kEnrollingPendingCancel, + kCancellingEnrollment, + kEnumerating, + kRenaming, + kDeleting, + kFinished, + }; + // FidoRequestHandlerBase: void DispatchRequest(FidoAuthenticator*) override; void AuthenticatorRemoved(FidoDiscoveryBase*, FidoAuthenticator*) override; @@ -102,21 +115,30 @@ class COMPONENT_EXPORT(DEVICE_FIDO) BioEnrollmentHandler base::Optional<pin::KeyAgreementResponse>); void OnHavePINToken(CtapDeviceResponseCode, base::Optional<pin::TokenResponse>); - void OnEnrollTemplateFinished(StatusCallback, - CtapDeviceResponseCode, - base::Optional<BioEnrollmentResponse>); - void OnCancel(StatusCallback, + void OnEnrollResponse(SampleCallback, + CompletionCallback, + base::Optional<TemplateId> current_template_id, + CtapDeviceResponseCode, + base::Optional<BioEnrollmentResponse>); + void OnCancel(CompletionCallback, CtapDeviceResponseCode, base::Optional<BioEnrollmentResponse>); void OnEnumerateTemplates(EnumerationCallback, CtapDeviceResponseCode, base::Optional<BioEnrollmentResponse>); - void OnStatusCallback(StatusCallback, + void OnRenameTemplate(StatusCallback, + CtapDeviceResponseCode, + base::Optional<BioEnrollmentResponse>); + void OnDeleteTemplate(StatusCallback, CtapDeviceResponseCode, base::Optional<BioEnrollmentResponse>); + void Finish(BioEnrollmentStatus status); + SEQUENCE_CHECKER(sequence_checker_); + State state_ = State::kWaitingForTouch; + FidoAuthenticator* authenticator_ = nullptr; base::OnceClosure ready_callback_; ErrorCallback error_callback_; diff --git a/chromium/device/fido/bio/enrollment_handler_unittest.cc b/chromium/device/fido/bio/enrollment_handler_unittest.cc index c1837b4f7f2..8d76e0e288c 100644 --- a/chromium/device/fido/bio/enrollment_handler_unittest.cc +++ b/chromium/device/fido/bio/enrollment_handler_unittest.cc @@ -10,16 +10,11 @@ #include "base/bind.h" #include "base/callback.h" #include "base/test/task_environment.h" -#include "build/build_config.h" #include "device/fido/bio/enrollment_handler.h" #include "device/fido/fido_transport_protocol.h" #include "device/fido/test_callback_receiver.h" #include "device/fido/virtual_fido_device_factory.h" -#if defined(OS_WIN) -#include "device/fido/win/fake_webauthn_api.h" -#endif // defined(OS_WIN) - namespace device { namespace { @@ -55,12 +50,23 @@ class BioEnrollmentHandlerTest : public ::testing::Test { &virtual_device_factory_); } + std::pair<CtapDeviceResponseCode, BioEnrollmentHandler::TemplateId> + EnrollTemplate(BioEnrollmentHandler* handler) { + test::StatusAndValueCallbackReceiver<CtapDeviceResponseCode, + BioEnrollmentHandler::TemplateId> + cb; + handler->EnrollTemplate(MakeSampleCallback(), cb.callback()); + + cb.WaitForCallback(); + return {cb.status(), cb.value()}; + } + void GetPIN(int64_t attempts, base::OnceCallback<void(std::string)> provide_pin) { std::move(provide_pin).Run(kPIN); } - auto MakeStatusCallback() { + BioEnrollmentHandler::SampleCallback MakeSampleCallback() { remaining_samples_ = 0; sampling_ = false; return base::BindRepeating(&BioEnrollmentHandlerTest::OnEnroll, @@ -73,11 +79,6 @@ class BioEnrollmentHandlerTest : public ::testing::Test { test::TestCallbackReceiver<> ready_callback_; test::ValueCallbackReceiver<BioEnrollmentStatus> error_callback_; test::VirtualFidoDeviceFactory virtual_device_factory_; - -#if defined(OS_WIN) - device::ScopedFakeWinWebAuthnApi win_webauthn_api_ = - device::ScopedFakeWinWebAuthnApi::MakeUnavailable(); -#endif // defined(OS_WIN) }; // Tests bio enrollment handler against device without PIN support. @@ -94,58 +95,6 @@ TEST_F(BioEnrollmentHandlerTest, NoPINSupport) { EXPECT_EQ(error_callback_.value(), BioEnrollmentStatus::kNoPINSet); } -// Tests getting authenticator modality without pin auth. -TEST_F(BioEnrollmentHandlerTest, Modality) { - VirtualCtap2Device::Config config; - config.pin_support = true; - config.bio_enrollment_preview_support = true; - - virtual_device_factory_.SetCtap2Config(config); - - auto handler = MakeHandler(); - ready_callback_.WaitForCallback(); - - test::StatusAndValueCallbackReceiver<CtapDeviceResponseCode, - base::Optional<BioEnrollmentResponse>> - cb; - handler->GetModality(cb.callback()); - cb.WaitForCallback(); - - // Operation was successful. - EXPECT_EQ(cb.status(), CtapDeviceResponseCode::kSuccess); - - // Result is correct. - BioEnrollmentResponse expected; - expected.modality = BioEnrollmentModality::kFingerprint; - EXPECT_EQ(cb.value(), expected); -} - -// Tests getting authenticator modality without pin auth. -TEST_F(BioEnrollmentHandlerTest, FingerprintSensorInfo) { - VirtualCtap2Device::Config config; - config.pin_support = true; - config.bio_enrollment_preview_support = true; - - virtual_device_factory_.SetCtap2Config(config); - - auto handler = MakeHandler(); - ready_callback_.WaitForCallback(); - - test::StatusAndValueCallbackReceiver<CtapDeviceResponseCode, - base::Optional<BioEnrollmentResponse>> - cb; - handler->GetSensorInfo(cb.callback()); - cb.WaitForCallback(); - - EXPECT_EQ(cb.status(), CtapDeviceResponseCode::kSuccess); - - BioEnrollmentResponse expected; - expected.modality = BioEnrollmentModality::kFingerprint; - expected.fingerprint_kind = BioEnrollmentFingerprintKind::kTouch; - expected.max_samples_for_enroll = 4; - EXPECT_EQ(cb.value(), expected); -} - // Tests enrollment handler PIN soft block. TEST_F(BioEnrollmentHandlerTest, SoftPINBlock) { VirtualCtap2Device::Config config; @@ -185,11 +134,11 @@ TEST_F(BioEnrollmentHandlerTest, Enroll) { auto handler = MakeHandler(); ready_callback_.WaitForCallback(); - test::ValueCallbackReceiver<CtapDeviceResponseCode> cb; - handler->EnrollTemplate(MakeStatusCallback(), cb.callback()); - - cb.WaitForCallback(); - EXPECT_EQ(cb.value(), CtapDeviceResponseCode::kSuccess); + CtapDeviceResponseCode status; + BioEnrollmentHandler::TemplateId template_id; + std::tie(status, template_id) = EnrollTemplate(handler.get()); + EXPECT_EQ(status, CtapDeviceResponseCode::kSuccess); + EXPECT_FALSE(template_id.empty()); } // Tests enrolling multiple fingerprints. @@ -205,10 +154,11 @@ TEST_F(BioEnrollmentHandlerTest, EnrollMultiple) { // Multiple enrollments for (auto i = 0; i < 4; i++) { - test::ValueCallbackReceiver<CtapDeviceResponseCode> cb; - handler->EnrollTemplate(MakeStatusCallback(), cb.callback()); - cb.WaitForCallback(); - EXPECT_EQ(cb.value(), CtapDeviceResponseCode::kSuccess); + CtapDeviceResponseCode status; + BioEnrollmentHandler::TemplateId template_id; + std::tie(status, template_id) = EnrollTemplate(handler.get()); + EXPECT_EQ(status, CtapDeviceResponseCode::kSuccess); + EXPECT_FALSE(template_id.empty()); } // Enumerate to check enrollments. @@ -238,33 +188,16 @@ TEST_F(BioEnrollmentHandlerTest, EnrollMax) { ready_callback_.WaitForCallback(); // Enroll until full. - auto status = CtapDeviceResponseCode::kSuccess; - while (status == CtapDeviceResponseCode::kSuccess) { - test::ValueCallbackReceiver<CtapDeviceResponseCode> cb; - handler->EnrollTemplate(MakeStatusCallback(), cb.callback()); - cb.WaitForCallback(); - status = cb.value(); + CtapDeviceResponseCode status; + BioEnrollmentHandler::TemplateId template_id; + for (;;) { + std::tie(status, template_id) = EnrollTemplate(handler.get()); + if (status != CtapDeviceResponseCode::kSuccess) + break; } EXPECT_EQ(status, CtapDeviceResponseCode::kCtap2ErrKeyStoreFull); -} - -// Tests cancelling fingerprint without an ongoing enrollment. -TEST_F(BioEnrollmentHandlerTest, CancelNoEnroll) { - VirtualCtap2Device::Config config; - config.pin_support = true; - config.bio_enrollment_preview_support = true; - - virtual_device_factory_.SetCtap2Config(config); - - auto handler = MakeHandler(); - ready_callback_.WaitForCallback(); - - test::ValueCallbackReceiver<CtapDeviceResponseCode> cb; - handler->Cancel(cb.callback()); - cb.WaitForCallback(); - - EXPECT_EQ(cb.value(), CtapDeviceResponseCode::kSuccess); + EXPECT_TRUE(template_id.empty()); } // Tests enumerating with no enrollments. @@ -300,10 +233,11 @@ TEST_F(BioEnrollmentHandlerTest, EnumerateOne) { ready_callback_.WaitForCallback(); // Enroll - skip response validation - test::ValueCallbackReceiver<CtapDeviceResponseCode> cb0; - handler->EnrollTemplate(MakeStatusCallback(), cb0.callback()); - cb0.WaitForCallback(); - EXPECT_EQ(cb0.value(), CtapDeviceResponseCode::kSuccess); + CtapDeviceResponseCode status; + BioEnrollmentHandler::TemplateId template_id; + std::tie(status, template_id) = EnrollTemplate(handler.get()); + EXPECT_EQ(status, CtapDeviceResponseCode::kSuccess); + EXPECT_FALSE(template_id.empty()); // Enumerate test::StatusAndValueCallbackReceiver< @@ -335,14 +269,15 @@ TEST_F(BioEnrollmentHandlerTest, Rename) { EXPECT_EQ(cb0.value(), CtapDeviceResponseCode::kCtap2ErrInvalidOption); // Enroll - skip response validation. - test::ValueCallbackReceiver<CtapDeviceResponseCode> cb1; - handler->EnrollTemplate(MakeStatusCallback(), cb1.callback()); - cb1.WaitForCallback(); - EXPECT_EQ(cb1.value(), CtapDeviceResponseCode::kSuccess); + CtapDeviceResponseCode status; + BioEnrollmentHandler::TemplateId template_id; + std::tie(status, template_id) = EnrollTemplate(handler.get()); + EXPECT_EQ(status, CtapDeviceResponseCode::kSuccess); + EXPECT_FALSE(template_id.empty()); // Rename non-existent enrollment. test::ValueCallbackReceiver<CtapDeviceResponseCode> cb2; - handler->RenameTemplate({1}, "OtherFingerprint1", cb2.callback()); + handler->RenameTemplate(template_id, "OtherFingerprint1", cb2.callback()); cb2.WaitForCallback(); EXPECT_EQ(cb2.value(), CtapDeviceResponseCode::kSuccess); @@ -355,7 +290,7 @@ TEST_F(BioEnrollmentHandlerTest, Rename) { cb3.WaitForCallback(); EXPECT_EQ(cb3.status(), CtapDeviceResponseCode::kSuccess); EXPECT_EQ(cb3.value(), (std::map<std::vector<uint8_t>, std::string>{ - {{1}, "OtherFingerprint1"}})); + {template_id, "OtherFingerprint1"}})); } // Tests deleting an enrollment (success and failure). @@ -376,10 +311,11 @@ TEST_F(BioEnrollmentHandlerTest, Delete) { EXPECT_EQ(cb0.value(), CtapDeviceResponseCode::kCtap2ErrInvalidOption); // Enroll - skip response validation. - test::ValueCallbackReceiver<CtapDeviceResponseCode> cb1; - handler->EnrollTemplate(MakeStatusCallback(), cb1.callback()); - cb1.WaitForCallback(); - EXPECT_EQ(cb1.value(), CtapDeviceResponseCode::kSuccess); + CtapDeviceResponseCode status; + BioEnrollmentHandler::TemplateId template_id; + std::tie(status, template_id) = EnrollTemplate(handler.get()); + EXPECT_EQ(status, CtapDeviceResponseCode::kSuccess); + EXPECT_FALSE(template_id.empty()); // Delete existing enrollment. test::ValueCallbackReceiver<CtapDeviceResponseCode> cb2; @@ -394,24 +330,5 @@ TEST_F(BioEnrollmentHandlerTest, Delete) { EXPECT_EQ(cb3.value(), CtapDeviceResponseCode::kCtap2ErrInvalidOption); } -// Test cancelling using the non-preview command. -TEST_F(BioEnrollmentHandlerTest, NonPreviewCancel) { - VirtualCtap2Device::Config config; - config.pin_support = true; - config.bio_enrollment_support = true; - - virtual_device_factory_.SetCtap2Config(config); - - auto handler = MakeHandler(); - ready_callback_.WaitForCallback(); - - // Cancel enrollment. - test::ValueCallbackReceiver<CtapDeviceResponseCode> cb; - handler->Cancel(cb.callback()); - - cb.WaitForCallback(); - EXPECT_EQ(cb.value(), CtapDeviceResponseCode::kSuccess); -} - } // namespace } // namespace device diff --git a/chromium/device/fido/ble/fido_ble_connection.cc b/chromium/device/fido/ble/fido_ble_connection.cc index 235d8985c2d..95dadff5d75 100644 --- a/chromium/device/fido/ble/fido_ble_connection.cc +++ b/chromium/device/fido/ble/fido_ble_connection.cc @@ -501,8 +501,6 @@ const BluetoothRemoteGattService* FidoBleConnection::GetFidoService() { // and a caBLE device. if (service->GetUUID() == BluetoothUUID(kFidoServiceUUID) || service->GetUUID() == BluetoothUUID(kCableAdvertisementUUID128)) { - FIDO_LOG(EVENT) << "Found caBLE service UUID: " - << service->GetUUID().value(); return service; } } diff --git a/chromium/device/fido/ble/fido_ble_discovery.cc b/chromium/device/fido/ble/fido_ble_discovery.cc index 6a2e73d6287..f42980ed6f4 100644 --- a/chromium/device/fido/ble/fido_ble_discovery.cc +++ b/chromium/device/fido/ble/fido_ble_discovery.cc @@ -48,12 +48,14 @@ void FidoBleDiscovery::OnSetPowered() { } } - auto filter = std::make_unique<BluetoothDiscoveryFilter>( + auto discovery_filter = std::make_unique<BluetoothDiscoveryFilter>( BluetoothTransport::BLUETOOTH_TRANSPORT_LE); - filter->AddUUID(FidoServiceUUID()); + device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter; + device_filter.uuids.insert(FidoServiceUUID()); + discovery_filter->AddDeviceFilter(device_filter); adapter()->StartDiscoverySessionWithFilter( - std::move(filter), + std::move(discovery_filter), base::AdaptCallbackForRepeating( base::BindOnce(&FidoBleDiscovery::OnStartDiscoverySessionWithFilter, weak_factory_.GetWeakPtr())), diff --git a/chromium/device/fido/ble/fido_ble_discovery_base.cc b/chromium/device/fido/ble/fido_ble_discovery_base.cc index 5b0945316cc..22c41154304 100644 --- a/chromium/device/fido/ble/fido_ble_discovery_base.cc +++ b/chromium/device/fido/ble/fido_ble_discovery_base.cc @@ -39,18 +39,15 @@ const BluetoothUUID& FidoBleDiscoveryBase::CableAdvertisementUUID() { void FidoBleDiscoveryBase::OnStartDiscoverySessionWithFilter( std::unique_ptr<BluetoothDiscoverySession> session) { SetDiscoverySession(std::move(session)); - FIDO_LOG(DEBUG) << "Discovery session started."; - NotifyDiscoveryStarted(true); + FIDO_LOG(DEBUG) << "BLE discovery session started"; } void FidoBleDiscoveryBase::OnSetPoweredError() { - FIDO_LOG(ERROR) << "Failed to power on the adapter."; - NotifyDiscoveryStarted(false); + FIDO_LOG(ERROR) << "Failed to power on BLE adapter"; } void FidoBleDiscoveryBase::OnStartDiscoverySessionError() { - FIDO_LOG(ERROR) << "Discovery session not started."; - NotifyDiscoveryStarted(false); + FIDO_LOG(ERROR) << "Failed to start BLE discovery"; } void FidoBleDiscoveryBase::SetDiscoverySession( @@ -67,7 +64,7 @@ bool FidoBleDiscoveryBase::IsCableDevice(const BluetoothDevice* device) const { void FidoBleDiscoveryBase::OnGetAdapter( scoped_refptr<BluetoothAdapter> adapter) { if (!adapter->IsPresent()) { - FIDO_LOG(DEBUG) << "bluetooth adapter is not available in current system."; + FIDO_LOG(DEBUG) << "No BLE adapter present"; NotifyDiscoveryStarted(false); return; } @@ -75,11 +72,19 @@ void FidoBleDiscoveryBase::OnGetAdapter( DCHECK(!adapter_); adapter_ = std::move(adapter); DCHECK(adapter_); - FIDO_LOG(DEBUG) << "Got adapter " << adapter_->GetAddress(); + FIDO_LOG(DEBUG) << "BLE adapter address " << adapter_->GetAddress(); adapter_->AddObserver(this); - if (adapter_->IsPowered()) + if (adapter_->IsPowered()) { OnSetPowered(); + } + + // FidoRequestHandlerBase blocks its transport availability callback on the + // DiscoveryStarted() calls of all instantiated discoveries. Hence, this call + // must not be put behind the BLE adapter getting powered on (which is + // dependent on the UI), or else the UI and this discovery will wait on each + // other indefinitely (see crbug.com/1018416). + NotifyDiscoveryStarted(true); } void FidoBleDiscoveryBase::StartInternal() { diff --git a/chromium/device/fido/ble/fido_ble_discovery_unittest.cc b/chromium/device/fido/ble/fido_ble_discovery_unittest.cc index 977ede1eb84..b9f69ccbecb 100644 --- a/chromium/device/fido/ble/fido_ble_discovery_unittest.cc +++ b/chromium/device/fido/ble/fido_ble_discovery_unittest.cc @@ -114,6 +114,18 @@ class FidoBleDiscoveryTest : public ::testing::Test { BluetoothAdapterFactory::SetAdapterForTesting(adapter_); } + void ExpectSuccessfulStartScan() { + EXPECT_CALL(*adapter(), StartScanWithFilter_) + .WillOnce(testing::Invoke( + [](const device::BluetoothDiscoveryFilter* discovery_filter, + device::BluetoothAdapter::DiscoverySessionResultCallback& + callback) { + std::move(callback).Run( + /*is_error=*/false, + device::UMABluetoothDiscoverySessionOutcome::SUCCESS); + })); + } + FidoBleDiscovery* discovery() { return &discovery_; } MockFidoDiscoveryObserver* observer() { return &observer_; } MockBluetoothAdapter* adapter() { @@ -136,7 +148,8 @@ TEST_F(FidoBleDiscoveryTest, SetMockBluetoothAdapter(); EXPECT_CALL(*adapter(), IsPresent()).WillOnce(Return(false)); EXPECT_CALL(*adapter(), SetPowered).Times(0); - EXPECT_CALL(*observer(), DiscoveryStarted(discovery(), false)); + EXPECT_CALL(*observer(), DiscoveryStarted(discovery(), false, + std::vector<FidoAuthenticator*>())); discovery()->Start(); task_environment_.FastForwardUntilNoTasksRemain(); } @@ -149,15 +162,9 @@ TEST_F(FidoBleDiscoveryTest, FidoBleDiscoveryResumeScanningAfterPoweredOn) { // After BluetoothAdapter is powered on, we expect that discovery session // starts again. Immediately calling the callback so that it does not hold a // reference to the adapter. - EXPECT_CALL(*adapter(), StartScanWithFilter_) - .WillOnce(testing::Invoke( - [](const device::BluetoothDiscoveryFilter* discovery_filter, - device::BluetoothAdapter::DiscoverySessionResultCallback& - callback) { - std::move(callback).Run( - /*is_error=*/false, - device::UMABluetoothDiscoverySessionOutcome::SUCCESS); - })); + ExpectSuccessfulStartScan(); + EXPECT_CALL(*observer(), DiscoveryStarted(discovery(), true, + std::vector<FidoAuthenticator*>())); discovery()->Start(); task_environment_.FastForwardUntilNoTasksRemain(); adapter()->NotifyAdapterPoweredChanged(true); @@ -168,7 +175,7 @@ TEST_F(FidoBleDiscoveryTest, FidoBleDiscoveryNoAdapter) { // simulating cases where the discovery is destroyed before obtaining a handle // to an adapter. This should be handled gracefully and not result in a crash. // We don't expect any calls to the notification methods. - EXPECT_CALL(*observer(), DiscoveryStarted(discovery(), _)).Times(0); + EXPECT_CALL(*observer(), DiscoveryStarted(discovery(), _, _)).Times(0); EXPECT_CALL(*observer(), AuthenticatorAdded(discovery(), _)).Times(0); EXPECT_CALL(*observer(), AuthenticatorRemoved(discovery(), _)).Times(0); } @@ -191,11 +198,10 @@ TEST_F(BluetoothTest, FidoBleDiscoveryFindsKnownDevice) { { base::RunLoop run_loop; auto quit = run_loop.QuitClosure(); - EXPECT_CALL( - observer, - AuthenticatorAdded(&discovery, - IdMatches(BluetoothTestBase::kTestDeviceAddress1))); - EXPECT_CALL(observer, DiscoveryStarted(&discovery, true)) + EXPECT_CALL(observer, + DiscoveryStarted(&discovery, true, + testing::ElementsAre(IdMatches( + BluetoothTestBase::kTestDeviceAddress1)))) .WillOnce(ReturnFromAsyncCall(quit)); discovery.Start(); @@ -217,7 +223,8 @@ TEST_F(BluetoothTest, FidoBleDiscoveryFindsNewDevice) { { base::RunLoop run_loop; auto quit = run_loop.QuitClosure(); - EXPECT_CALL(observer, DiscoveryStarted(&discovery, true)) + EXPECT_CALL(observer, DiscoveryStarted(&discovery, true, + std::vector<FidoAuthenticator*>())) .WillOnce(ReturnFromAsyncCall(quit)); discovery.Start(); @@ -261,7 +268,8 @@ TEST_F(BluetoothTest, FidoBleDiscoveryFindsUpdatedDevice) { { base::RunLoop run_loop; auto quit = run_loop.QuitClosure(); - EXPECT_CALL(observer, DiscoveryStarted(&discovery, true)) + EXPECT_CALL(observer, DiscoveryStarted(&discovery, true, + std::vector<FidoAuthenticator*>())) .WillOnce(ReturnFromAsyncCall(quit)); discovery.Start(); @@ -306,7 +314,8 @@ TEST_F(BluetoothTest, FidoBleDiscoveryRejectsCableDevice) { { base::RunLoop run_loop; auto quit = run_loop.QuitClosure(); - EXPECT_CALL(observer, DiscoveryStarted(&discovery, true)) + EXPECT_CALL(observer, DiscoveryStarted(&discovery, true, + std::vector<FidoAuthenticator*>())) .WillOnce(ReturnFromAsyncCall(quit)); discovery.Start(); @@ -355,13 +364,16 @@ TEST_F(FidoBleDiscoveryTest, TEST_F(FidoBleDiscoveryTest, DiscoveryNotifiesObserverWhenDeviceInPairingMode) { SetMockBluetoothAdapter(); EXPECT_CALL(*adapter(), IsPresent()).WillOnce(Return(true)); + EXPECT_CALL(*adapter(), IsPowered()).WillOnce(Return(true)); auto mock_device = CreateMockFidoDevice(); + ::testing::InSequence sequence; + ExpectSuccessfulStartScan(); + EXPECT_CALL(*observer(), DiscoveryStarted(discovery(), true, _)); const auto device_id = FidoBleDevice::GetIdForAddress(kDeviceAddress); discovery()->Start(); task_environment_.FastForwardUntilNoTasksRemain(); - ::testing::InSequence sequence; EXPECT_CALL(*observer(), AuthenticatorAdded(discovery(), IdMatches(kDeviceAddress))); adapter()->NotifyDeviceChanged(mock_device.get()); @@ -378,13 +390,16 @@ TEST_F(FidoBleDiscoveryTest, DiscoveryNotifiesObserverWhenDeviceInNonPairingMode) { SetMockBluetoothAdapter(); EXPECT_CALL(*adapter(), IsPresent()).WillOnce(Return(true)); + EXPECT_CALL(*adapter(), IsPowered()).WillOnce(Return(true)); auto mock_device = CreateMockFidoDevice(); + ::testing::InSequence sequence; + ExpectSuccessfulStartScan(); + EXPECT_CALL(*observer(), DiscoveryStarted(discovery(), true, _)); const auto device_id = FidoBleDevice::GetIdForAddress(kDeviceAddress); discovery()->Start(); task_environment_.FastForwardUntilNoTasksRemain(); - ::testing::InSequence sequence; EXPECT_CALL(*observer(), AuthenticatorAdded(discovery(), IdMatches(kDeviceAddress))); adapter()->NotifyDeviceChanged(mock_device.get()); @@ -429,9 +444,13 @@ TEST_F(FidoBleDiscoveryTest, TEST_F(FidoBleDiscoveryTest, DiscoveryDoesNotDeleteDeviceOnAddressCollision) { SetMockBluetoothAdapter(); EXPECT_CALL(*adapter(), IsPresent()).WillOnce(Return(true)); + EXPECT_CALL(*adapter(), IsPowered()).WillOnce(Return(true)); auto mock_device = CreateMockFidoDevice(); auto changed_mock_device = CreateChangedMockFidoDevice(); + ExpectSuccessfulStartScan(); + EXPECT_CALL(*observer(), DiscoveryStarted(discovery(), true, _)); + EXPECT_CALL(*observer(), AuthenticatorAdded(discovery(), IdMatches(kDeviceAddress))); diff --git a/chromium/device/fido/ble_adapter_manager_unittest.cc b/chromium/device/fido/ble_adapter_manager_unittest.cc index db0f0fff598..1617f86c0a4 100644 --- a/chromium/device/fido/ble_adapter_manager_unittest.cc +++ b/chromium/device/fido/ble_adapter_manager_unittest.cc @@ -11,22 +11,17 @@ #include "base/bind_helpers.h" #include "base/run_loop.h" #include "base/test/task_environment.h" -#include "build/build_config.h" #include "device/bluetooth/bluetooth_adapter_factory.h" #include "device/bluetooth/test/mock_bluetooth_adapter.h" #include "device/bluetooth/test/mock_bluetooth_device.h" +#include "device/fido/fake_fido_discovery.h" #include "device/fido/fido_authenticator.h" -#include "device/fido/fido_discovery_factory.h" #include "device/fido/fido_request_handler_base.h" #include "device/fido/fido_transport_protocol.h" #include "device/fido/test_callback_receiver.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#if defined(OS_WIN) -#include "device/fido/win/fake_webauthn_api.h" -#endif // defined(OS_WIN) - namespace device { namespace { @@ -75,6 +70,7 @@ class FakeFidoRequestHandlerBase : public FidoRequestHandlerBase { fido_discovery_factory, {FidoTransportProtocol::kBluetoothLowEnergy}) { set_observer(observer); + Start(); } void SimulateFidoRequestHandlerHasAuthenticator(bool simulate_authenticator) { @@ -100,6 +96,11 @@ class FidoBleAdapterManagerTest : public ::testing::Test { public: FidoBleAdapterManagerTest() { BluetoothAdapterFactory::SetAdapterForTesting(adapter_); + fido_discovery_factory_->ForgeNextBleDiscovery( + test::FakeFidoDiscovery::StartMode::kAutomatic); + + fake_request_handler_ = std::make_unique<FakeFidoRequestHandlerBase>( + mock_observer_.get(), fido_discovery_factory_.get()); } MockBluetoothDevice* AddMockBluetoothDeviceToAdapter() { @@ -139,18 +140,10 @@ class FidoBleAdapterManagerTest : public ::testing::Test { base::MakeRefCounted<::testing::NiceMock<MockBluetoothAdapter>>(); std::unique_ptr<MockObserver> mock_observer_ = std::make_unique<MockObserver>(); - std::unique_ptr<FidoDiscoveryFactory> fido_discovery_factory_ = - std::make_unique<FidoDiscoveryFactory>(); - -#if defined(OS_WIN) - device::ScopedFakeWinWebAuthnApi win_webauthn_api_ = - device::ScopedFakeWinWebAuthnApi::MakeUnavailable(); -#endif // defined(OS_WIN) - - std::unique_ptr<FakeFidoRequestHandlerBase> fake_request_handler_ = - std::make_unique<FakeFidoRequestHandlerBase>( - mock_observer_.get(), - fido_discovery_factory_.get()); + std::unique_ptr<test::FakeFidoDiscoveryFactory> fido_discovery_factory_ = + std::make_unique<test::FakeFidoDiscoveryFactory>(); + + std::unique_ptr<FakeFidoRequestHandlerBase> fake_request_handler_; }; TEST_F(FidoBleAdapterManagerTest, AdapterNotPresent) { diff --git a/chromium/device/fido/cable/cable_discovery_data.h b/chromium/device/fido/cable/cable_discovery_data.h index 0ba7dc4f0e2..81a90674135 100644 --- a/chromium/device/fido/cable/cable_discovery_data.h +++ b/chromium/device/fido/cable/cable_discovery_data.h @@ -23,6 +23,17 @@ using CableSessionPreKeyArray = std::array<uint8_t, kCableSessionPreKeySize>; // |CableDiscoveryData::DeriveQRKeyMaterial| to encrypt a coarse timestamp and // generate QR secrets, EIDs, etc. using QRGeneratorKey = std::array<uint8_t, 32>; +// CableNonce is a nonce used in BLE handshaking. +using CableNonce = std::array<uint8_t, 8>; +// CableEidGeneratorKey is an AES-256 key that is used to encrypt a 64-bit nonce +// and 64-bits of zeros, resulting in a BLE-advertised EID. +using CableEidGeneratorKey = std::array<uint8_t, 32>; +// CablePskGeneratorKey is HKDF input keying material that is used to +// generate a Noise PSK given the nonce decrypted from an EID. +using CablePskGeneratorKey = std::array<uint8_t, 32>; +// CableAuthenticatorIdentityKey is a P-256 public value used to authenticate a +// paired phone. +using CableAuthenticatorIdentityKey = std::array<uint8_t, 65>; // Encapsulates information required to discover Cable device per single // credential. When multiple credentials are enrolled to a single account @@ -32,10 +43,20 @@ using QRGeneratorKey = std::array<uint8_t, 32>; // TODO(hongjunchoi): Add discovery data required for MakeCredential request. // See: https://crbug.com/837088 struct COMPONENT_EXPORT(DEVICE_FIDO) CableDiscoveryData { - CableDiscoveryData(uint8_t version, + enum class Version { + INVALID, + V1, + V2, + }; + + CableDiscoveryData(Version version, const CableEidArray& client_eid, const CableEidArray& authenticator_eid, const CableSessionPreKeyArray& session_pre_key); + // Creates discovery data given a specific QR secret. See |DeriveQRSecret| for + // how to generate such secrets. + explicit CableDiscoveryData( + base::span<const uint8_t, kCableQRSecretSize> qr_secret); CableDiscoveryData(); CableDiscoveryData(const CableDiscoveryData& data); ~CableDiscoveryData(); @@ -43,6 +64,10 @@ struct COMPONENT_EXPORT(DEVICE_FIDO) CableDiscoveryData { CableDiscoveryData& operator=(const CableDiscoveryData& other); bool operator==(const CableDiscoveryData& other) const; + // Match attempts to recognise the given EID. If it matches this discovery + // data, the nonce is returned. + base::Optional<CableNonce> Match(const CableEidArray& candidate_eid) const; + // NewQRKey returns a random key for QR generation. static QRGeneratorKey NewQRKey(); @@ -50,21 +75,39 @@ struct COMPONENT_EXPORT(DEVICE_FIDO) CableDiscoveryData { // of these ticks is a purely local matter for Chromium. static int64_t CurrentTimeTick(); - // DeriveQRKeyMaterial writes |kCableQRSecretSize| bytes to |out_qr_secret|, - // |kCableEphemeralIdSize| bytes to |out_authenticator_eid| and - // |kCableSessionPreKeySize| bytes of |out_session_key|, based on the given - // generator key and current time. - static void DeriveQRKeyMaterial( - base::span<uint8_t, kCableQRSecretSize> out_qr_secret, - base::span<uint8_t, kCableEphemeralIdSize> out_authenticator_eid, - base::span<uint8_t, kCableSessionPreKeySize> out_session_key, + // DeriveQRKeyMaterial returns a QR-secret given a generating key and a + // timestamp. + static std::array<uint8_t, kCableQRSecretSize> DeriveQRSecret( base::span<const uint8_t, 32> qr_generator_key, const int64_t tick); - uint8_t version; - CableEidArray client_eid; - CableEidArray authenticator_eid; - CableSessionPreKeyArray session_pre_key; + // version indicates whether v1 or v2 data is contained in this object. + // |INVALID| is not a valid version but is set as the default to catch any + // cases where the version hasn't been set explicitly. + Version version = Version::INVALID; + + struct V1Data { + CableEidArray client_eid; + CableEidArray authenticator_eid; + CableSessionPreKeyArray session_pre_key; + }; + base::Optional<V1Data> v1; + + struct COMPONENT_EXPORT(DEVICE_FIDO) V2Data { + V2Data(); + V2Data(const V2Data&); + ~V2Data(); + + CableEidGeneratorKey eid_gen_key; + CablePskGeneratorKey psk_gen_key; + base::Optional<CableAuthenticatorIdentityKey> peer_identity; + // peer_name is an authenticator-controlled, UTF8-valid string containing + // the self-reported, human-friendly name of a v2 authenticator. This need + // not be filled in when handshaking but an authenticator may provide it + // when offering long-term pairing data. + base::Optional<std::string> peer_name; + }; + base::Optional<V2Data> v2; }; } // namespace device diff --git a/chromium/device/fido/cable/fido_cable_device.cc b/chromium/device/fido/cable/fido_cable_device.cc index df77590463e..2c3767ccd01 100644 --- a/chromium/device/fido/cable/fido_cable_device.cc +++ b/chromium/device/fido/cable/fido_cable_device.cc @@ -7,6 +7,7 @@ #include <utility> #include "base/bind.h" +#include "base/numerics/safe_math.h" #include "base/strings/string_piece.h" #include "base/threading/thread_task_runner_handle.h" #include "components/device_event_log/device_event_log.h" @@ -22,9 +23,9 @@ namespace { // Maximum size of EncryptionData::read_sequence_num or // EncryptionData::write_sequence_num allowed. If we encounter // counter larger than |kMaxCounter| FidoCableDevice should error out. -constexpr size_t kMaxCounter = (1 << 24) - 1; +constexpr uint32_t kMaxCounter = (1 << 24) - 1; -base::Optional<std::vector<uint8_t>> ConstructEncryptionNonce( +base::Optional<std::vector<uint8_t>> ConstructV1Nonce( base::span<const uint8_t> nonce, bool is_sender_client, uint32_t counter) { @@ -39,6 +40,20 @@ base::Optional<std::vector<uint8_t>> ConstructEncryptionNonce( return constructed_nonce; } +bool ConstructV2Nonce(base::span<uint8_t, 12> out_nonce, uint32_t counter) { + if (counter > kMaxCounter) { + return false; + } + + // Nonce is just a little-endian counter. + std::array<uint8_t, sizeof(counter)> counter_bytes; + memcpy(counter_bytes.data(), &counter, sizeof(counter)); + auto remaining = + std::copy(counter_bytes.begin(), counter_bytes.end(), out_nonce.begin()); + std::fill(remaining, out_nonce.end(), 0); + return true; +} + } // namespace FidoCableDevice::EncryptionData::EncryptionData() = default; @@ -105,16 +120,28 @@ void FidoCableDevice::SendHandshakeMessage( std::move(handshake_message), std::move(callback)); } -void FidoCableDevice::SetEncryptionData( +void FidoCableDevice::SetV1EncryptionData( base::span<const uint8_t, 32> 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(); - encryption_data_->session_key = fido_parsing_utils::Materialize(session_key); + encryption_data_->read_key = fido_parsing_utils::Materialize(session_key); + encryption_data_->write_key = fido_parsing_utils::Materialize(session_key); encryption_data_->nonce = fido_parsing_utils::Materialize(nonce); } +void FidoCableDevice::SetV2EncryptionData( + base::span<const uint8_t, 32> read_key, + base::span<const uint8_t, 32> write_key) { + DCHECK(!encryption_data_); + encryption_data_.emplace(); + encryption_data_->read_key = fido_parsing_utils::Materialize(read_key); + encryption_data_->write_key = fido_parsing_utils::Materialize(write_key); + memset(encryption_data_->nonce.data(), 0, encryption_data_->nonce.size()); + encryption_data_->is_version_two = true; +} + FidoTransportProtocol FidoCableDevice::DeviceTransport() const { return FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy; } @@ -129,14 +156,32 @@ void FidoCableDevice::SetSequenceNumbersForTesting(uint32_t read_seq, bool FidoCableDevice::EncryptOutgoingMessage( const EncryptionData& encryption_data, std::vector<uint8_t>* message_to_encrypt) { - const auto nonce = ConstructEncryptionNonce( - encryption_data.nonce, true /* is_sender_client */, - encryption_data.write_sequence_num); + return encryption_data.is_version_two + ? EncryptV2OutgoingMessage(encryption_data, message_to_encrypt) + : EncryptV1OutgoingMessage(encryption_data, message_to_encrypt); +} + +// static +bool FidoCableDevice::DecryptIncomingMessage( + const EncryptionData& encryption_data, + FidoBleFrame* incoming_frame) { + return encryption_data.is_version_two + ? DecryptV2IncomingMessage(encryption_data, incoming_frame) + : DecryptV1IncomingMessage(encryption_data, incoming_frame); +} + +// static +bool FidoCableDevice::EncryptV1OutgoingMessage( + const EncryptionData& encryption_data, + std::vector<uint8_t>* message_to_encrypt) { + const auto nonce = + ConstructV1Nonce(encryption_data.nonce, /*is_sender_client=*/true, + encryption_data.write_sequence_num); if (!nonce) return false; crypto::Aead aes_key(crypto::Aead::AES_256_GCM); - aes_key.Init(encryption_data.session_key); + aes_key.Init(encryption_data.write_key); DCHECK_EQ(nonce->size(), aes_key.NonceLength()); const uint8_t additional_data[1] = { @@ -148,17 +193,17 @@ bool FidoCableDevice::EncryptOutgoingMessage( } // static -bool FidoCableDevice::DecryptIncomingMessage( +bool FidoCableDevice::DecryptV1IncomingMessage( const EncryptionData& encryption_data, FidoBleFrame* incoming_frame) { - const auto nonce = ConstructEncryptionNonce( - encryption_data.nonce, false /* is_sender_client */, - encryption_data.read_sequence_num); + const auto nonce = + ConstructV1Nonce(encryption_data.nonce, /*is_sender_client=*/false, + encryption_data.read_sequence_num); if (!nonce) return false; crypto::Aead aes_key(crypto::Aead::AES_256_GCM); - aes_key.Init(encryption_data.session_key); + aes_key.Init(encryption_data.read_key); DCHECK_EQ(nonce->size(), aes_key.NonceLength()); const uint8_t additional_data[1] = { @@ -174,4 +219,91 @@ bool FidoCableDevice::DecryptIncomingMessage( return true; } +// static +bool FidoCableDevice::EncryptV2OutgoingMessage( + const EncryptionData& encryption_data, + std::vector<uint8_t>* message_to_encrypt) { + // Messages will be padded in order to round their length up to a multiple of + // kPaddingGranularity. + constexpr size_t kPaddingGranularity = 32; + static_assert(kPaddingGranularity > 0, "padding too small"); + static_assert(kPaddingGranularity < 256, "padding too large"); + static_assert((kPaddingGranularity & (kPaddingGranularity - 1)) == 0, + "padding must be a power of two"); + + // Padding consists of a some number of zero bytes appended to the message and + // the final byte in the message is the number of zeros. + base::CheckedNumeric<size_t> padded_size_checked = message_to_encrypt->size(); + padded_size_checked += 1; // padding-length byte. + padded_size_checked = (padded_size_checked + kPaddingGranularity - 1) & + ~(kPaddingGranularity - 1); + if (!padded_size_checked.IsValid()) { + return false; + } + + const size_t padded_size = padded_size_checked.ValueOrDie(); + DCHECK_GT(padded_size, message_to_encrypt->size()); + const size_t num_zeros = padded_size - message_to_encrypt->size() - 1; + + std::vector<uint8_t> padded_message(padded_size, 0); + memcpy(padded_message.data(), message_to_encrypt->data(), + message_to_encrypt->size()); + // The number of added zeros has to fit in a single byte so it has to be less + // than 256. + DCHECK_LT(num_zeros, 256u); + padded_message[padded_message.size() - 1] = static_cast<uint8_t>(num_zeros); + + std::array<uint8_t, 12> nonce; + if (!ConstructV2Nonce(nonce, encryption_data.write_sequence_num)) { + return false; + } + + crypto::Aead aes_key(crypto::Aead::AES_256_GCM); + aes_key.Init(encryption_data.write_key); + DCHECK_EQ(nonce.size(), aes_key.NonceLength()); + + const uint8_t additional_data[2] = { + base::strict_cast<uint8_t>(FidoBleDeviceCommand::kMsg), /*version=*/2}; + std::vector<uint8_t> ciphertext = + aes_key.Seal(padded_message, nonce, additional_data); + message_to_encrypt->swap(ciphertext); + return true; +} + +// static +bool FidoCableDevice::DecryptV2IncomingMessage( + const EncryptionData& encryption_data, + FidoBleFrame* incoming_frame) { + std::array<uint8_t, 12> nonce; + if (!ConstructV2Nonce(nonce, encryption_data.read_sequence_num)) { + return false; + } + + crypto::Aead aes_key(crypto::Aead::AES_256_GCM); + aes_key.Init(encryption_data.read_key); + DCHECK_EQ(nonce.size(), aes_key.NonceLength()); + + const uint8_t additional_data[2] = { + base::strict_cast<uint8_t>(incoming_frame->command()), /*version=*/2}; + base::Optional<std::vector<uint8_t>> plaintext = + aes_key.Open(incoming_frame->data(), nonce, additional_data); + if (!plaintext) { + FIDO_LOG(ERROR) << "Failed to decrypt caBLE message."; + return false; + } + + if (plaintext->empty()) { + return false; + } + + const size_t padding_length = (*plaintext)[plaintext->size() - 1]; + if (padding_length + 1 > plaintext->size()) { + return false; + } + plaintext->resize(plaintext->size() - padding_length - 1); + + incoming_frame->data().swap(*plaintext); + return true; +} + } // namespace device diff --git a/chromium/device/fido/cable/fido_cable_device.h b/chromium/device/fido/cable/fido_cable_device.h index 504a7c0ad64..4e6d3620990 100644 --- a/chromium/device/fido/cable/fido_cable_device.h +++ b/chromium/device/fido/cable/fido_cable_device.h @@ -43,8 +43,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDevice : public FidoBleDevice { void SendHandshakeMessage(std::vector<uint8_t> handshake_message, DeviceCallback callback); - void SetEncryptionData(base::span<const uint8_t, 32> session_key, - base::span<const uint8_t, 8> nonce); + // Configure caBLE v1 keys. + void SetV1EncryptionData(base::span<const uint8_t, 32> session_key, + base::span<const uint8_t, 8> nonce); + // Configure caBLE v2 keys. + void SetV2EncryptionData(base::span<const uint8_t, 32> read_key, + base::span<const uint8_t, 32> write_key); FidoTransportProtocol DeviceTransport() const override; // SetCountersForTesting allows tests to set the message counters. Non-test @@ -58,10 +62,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDevice : public FidoBleDevice { struct EncryptionData { EncryptionData(); - std::array<uint8_t, 32> session_key; + std::array<uint8_t, 32> read_key; + std::array<uint8_t, 32> write_key; std::array<uint8_t, 8> nonce; uint32_t write_sequence_num = 0; uint32_t read_sequence_num = 0; + bool is_version_two = false; }; static bool EncryptOutgoingMessage(const EncryptionData& encryption_data, @@ -69,6 +75,18 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDevice : public FidoBleDevice { static bool DecryptIncomingMessage(const EncryptionData& encryption_data, FidoBleFrame* incoming_frame); + static bool EncryptV1OutgoingMessage( + const EncryptionData& encryption_data, + std::vector<uint8_t>* message_to_encrypt); + static bool DecryptV1IncomingMessage(const EncryptionData& encryption_data, + FidoBleFrame* incoming_frame); + + static bool EncryptV2OutgoingMessage( + const EncryptionData& encryption_data, + std::vector<uint8_t>* message_to_encrypt); + static bool DecryptV2IncomingMessage(const EncryptionData& encryption_data, + FidoBleFrame* incoming_frame); + base::Optional<EncryptionData> encryption_data_; base::WeakPtrFactory<FidoCableDevice> weak_factory_{this}; diff --git a/chromium/device/fido/cable/fido_cable_device_unittest.cc b/chromium/device/fido/cable/fido_cable_device_unittest.cc index 2c0a3887581..9f37dfb9001 100644 --- a/chromium/device/fido/cable/fido_cable_device_unittest.cc +++ b/chromium/device/fido/cable/fido_cable_device_unittest.cc @@ -140,7 +140,7 @@ class FidoCableDeviceTest : public Test { adapter_.get(), BluetoothTestBase::kTestDeviceAddress1); connection_ = connection.get(); device_ = std::make_unique<FidoCableDevice>(std::move(connection)); - device_->SetEncryptionData(kTestSessionKey, kTestEncryptionNonce); + device_->SetV1EncryptionData(kTestSessionKey, kTestEncryptionNonce); connection_->read_callback() = device_->GetReadCallbackForTesting(); } diff --git a/chromium/device/fido/cable/fido_cable_discovery.cc b/chromium/device/fido/cable/fido_cable_discovery.cc index 3d6611588a9..dbfc006053c 100644 --- a/chromium/device/fido/cable/fido_cable_discovery.cc +++ b/chromium/device/fido/cable/fido_cable_discovery.cc @@ -27,8 +27,10 @@ #include "device/fido/cable/fido_cable_handshake_handler.h" #include "device/fido/features.h" #include "device/fido/fido_parsing_utils.h" +#include "third_party/boringssl/src/include/openssl/aes.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/mem.h" namespace device { @@ -40,7 +42,6 @@ namespace { // instead, and on Mac our only option is to advertise an additional service // with the EID as its UUID. std::unique_ptr<BluetoothAdvertisement::Data> ConstructAdvertisementData( - uint8_t version_number, base::span<const uint8_t, kCableEphemeralIdSize> client_eid) { auto advertisement_data = std::make_unique<BluetoothAdvertisement::Data>( BluetoothAdvertisement::AdvertisementType::ADVERTISEMENT_TYPE_BROADCAST); @@ -66,7 +67,7 @@ std::unique_ptr<BluetoothAdvertisement::Data> ConstructAdvertisementData( 3u + kCableEphemeralIdSize; std::array<uint8_t, 4> kCableGoogleManufacturerDataHeader = { kCableGoogleManufacturerDataLength, kCableGoogleManufacturerDataType, - kCableFlags, version_number}; + kCableFlags, /*version=*/1}; auto manufacturer_data = std::make_unique<BluetoothAdvertisement::ManufacturerData>(); @@ -91,7 +92,7 @@ std::unique_ptr<BluetoothAdvertisement::Data> ConstructAdvertisementData( // Since the remainder of this service data field is a Cable EID, set the 5th // bit of the flag byte. service_data_value[0] = kCableFlags; - service_data_value[1] = version_number; + service_data_value[1] = 1 /* version */; std::copy(client_eid.begin(), client_eid.end(), service_data_value.begin() + 2); service_data->emplace(kCableAdvertisementUUID128, @@ -109,14 +110,36 @@ std::unique_ptr<BluetoothAdvertisement::Data> ConstructAdvertisementData( CableDiscoveryData::CableDiscoveryData() = default; CableDiscoveryData::CableDiscoveryData( - uint8_t version, + CableDiscoveryData::Version version, const CableEidArray& client_eid, const CableEidArray& authenticator_eid, const CableSessionPreKeyArray& session_pre_key) - : version(version), - client_eid(client_eid), - authenticator_eid(authenticator_eid), - session_pre_key(session_pre_key) {} + : version(version) { + CHECK_EQ(Version::V1, version); + v1.emplace(); + v1->client_eid = client_eid; + v1->authenticator_eid = authenticator_eid; + v1->session_pre_key = session_pre_key; +} + +CableDiscoveryData::CableDiscoveryData( + base::span<const uint8_t, kCableQRSecretSize> qr_secret) { + version = Version::V2; + v2.emplace(); + + static const char kEIDGen[] = "caBLE QR to EID generator key"; + bool ok = + HKDF(v2->eid_gen_key.data(), v2->eid_gen_key.size(), EVP_sha256(), + qr_secret.data(), qr_secret.size(), /*salt=*/nullptr, 0, + reinterpret_cast<const uint8_t*>(kEIDGen), sizeof(kEIDGen) - 1); + DCHECK(ok); + + static const char kPSKGen[] = "caBLE QR to PSK generator key"; + ok = HKDF(v2->psk_gen_key.data(), v2->psk_gen_key.size(), EVP_sha256(), + qr_secret.data(), qr_secret.size(), /*salt=*/nullptr, 0, + reinterpret_cast<const uint8_t*>(kPSKGen), sizeof(kPSKGen) - 1); + DCHECK(ok); +} CableDiscoveryData::CableDiscoveryData(const CableDiscoveryData& data) = default; @@ -127,9 +150,76 @@ CableDiscoveryData& CableDiscoveryData::operator=( CableDiscoveryData::~CableDiscoveryData() = default; bool CableDiscoveryData::operator==(const CableDiscoveryData& other) const { - return version == other.version && client_eid == other.client_eid && - authenticator_eid == other.authenticator_eid && - session_pre_key == other.session_pre_key; + if (version != other.version) { + return false; + } + + switch (version) { + case CableDiscoveryData::Version::V1: + return v1->client_eid == other.v1->client_eid && + v1->authenticator_eid == other.v1->authenticator_eid && + v1->session_pre_key == other.v1->session_pre_key; + + case CableDiscoveryData::Version::V2: + return v2->eid_gen_key == other.v2->eid_gen_key && + v2->psk_gen_key == other.v2->psk_gen_key && + v2->peer_identity == other.v2->peer_identity && + v2->peer_name == other.v2->peer_name; + + case CableDiscoveryData::Version::INVALID: + CHECK(false); + return false; + } +} + +base::Optional<CableNonce> CableDiscoveryData::Match( + const CableEidArray& eid) const { + switch (version) { + case Version::V1: { + if (eid != v1->authenticator_eid) { + return base::nullopt; + } + + // The nonce is the first eight bytes of the EID. + CableNonce nonce; + const bool ok = + fido_parsing_utils::ExtractArray(v1->client_eid, 0, &nonce); + DCHECK(ok); + return nonce; + } + + case Version::V2: { + // Attempt to decrypt the EID with the EID generator key and check whether + // it has a valid structure. + AES_KEY key; + CHECK(AES_set_decrypt_key(v2->eid_gen_key.data(), + /*bits=*/8 * v2->eid_gen_key.size(), + &key) == 0); + static_assert(kCableEphemeralIdSize == AES_BLOCK_SIZE, + "EIDs are not AES blocks"); + CableEidArray decrypted; + AES_decrypt(/*in=*/eid.data(), /*out=*/decrypted.data(), &key); + const uint8_t kZeroTrailer[8] = {0}; + static_assert(8 + sizeof(kZeroTrailer) == + std::tuple_size<decltype(decrypted)>::value, + "Trailer is wrong size"); + if (CRYPTO_memcmp(kZeroTrailer, decrypted.data() + 8, + sizeof(kZeroTrailer)) != 0) { + return base::nullopt; + } + + CableNonce nonce; + static_assert( + sizeof(nonce) <= std::tuple_size<decltype(decrypted)>::value, + "nonce too large"); + memcpy(nonce.data(), decrypted.data(), sizeof(nonce)); + return nonce; + } + + case Version::INVALID: + DCHECK(false); + return base::nullopt; + } } // static @@ -146,10 +236,7 @@ int64_t CableDiscoveryData::CurrentTimeTick() { } // static -void CableDiscoveryData::DeriveQRKeyMaterial( - base::span<uint8_t, kCableQRSecretSize> out_qr_secret, - base::span<uint8_t, kCableEphemeralIdSize> out_authenticator_eid, - base::span<uint8_t, kCableSessionPreKeySize> out_session_key, +std::array<uint8_t, kCableQRSecretSize> CableDiscoveryData::DeriveQRSecret( base::span<const uint8_t, 32> qr_generator_key, const int64_t tick) { union { @@ -158,34 +245,53 @@ void CableDiscoveryData::DeriveQRKeyMaterial( } current_tick; current_tick.i = tick; - bool ok = HKDF(out_qr_secret.data(), out_qr_secret.size(), EVP_sha256(), - qr_generator_key.data(), qr_generator_key.size(), + std::array<uint8_t, kCableQRSecretSize> ret; + bool ok = HKDF(ret.data(), ret.size(), EVP_sha256(), qr_generator_key.data(), + qr_generator_key.size(), /*salt=*/nullptr, 0, current_tick.bytes, sizeof(current_tick)); DCHECK(ok); - static const char kAuthenticatorEIDInfo[] = "caBLE QR to EID"; - ok = HKDF(out_authenticator_eid.data(), out_authenticator_eid.size(), - EVP_sha256(), out_qr_secret.data(), out_qr_secret.size(), - /*salt=*/nullptr, 0, - reinterpret_cast<const uint8_t*>(kAuthenticatorEIDInfo), - sizeof(kAuthenticatorEIDInfo) - 1); - DCHECK(ok); - static const char kSessionKeyInfo[] = "caBLE QR to session pre-key"; - ok = HKDF(out_session_key.data(), out_session_key.size(), EVP_sha256(), - out_qr_secret.data(), out_qr_secret.size(), /*salt=*/nullptr, 0, - reinterpret_cast<const uint8_t*>(kSessionKeyInfo), - sizeof(kSessionKeyInfo) - 1); - DCHECK(ok); + return ret; } +CableDiscoveryData::V2Data::V2Data() = default; +CableDiscoveryData::V2Data::V2Data(const V2Data&) = default; +CableDiscoveryData::V2Data::~V2Data() = default; + +// FidoCableDiscovery::Result ------------------------------------------------- + +FidoCableDiscovery::Result::Result() = default; + +FidoCableDiscovery::Result::Result(const CableDiscoveryData& in_discovery_data, + const CableNonce& in_nonce, + const CableEidArray& in_eid, + base::Optional<int> in_ticks_back) + : discovery_data(in_discovery_data), + nonce(in_nonce), + eid(in_eid), + ticks_back(in_ticks_back) {} + +FidoCableDiscovery::Result::Result(const Result& other) = default; + +FidoCableDiscovery::Result::~Result() = default; + +// FidoCableDiscovery::ObservedDeviceData ------------------------------------- + +FidoCableDiscovery::ObservedDeviceData::ObservedDeviceData() = default; +FidoCableDiscovery::ObservedDeviceData::~ObservedDeviceData() = default; + // FidoCableDiscovery --------------------------------------------------------- FidoCableDiscovery::FidoCableDiscovery( std::vector<CableDiscoveryData> discovery_data, - base::Optional<QRGeneratorKey> qr_generator_key) + base::Optional<QRGeneratorKey> qr_generator_key, + base::Optional< + base::RepeatingCallback<void(std::unique_ptr<CableDiscoveryData>)>> + pairing_callback) : FidoBleDiscoveryBase( FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy), discovery_data_(std::move(discovery_data)), - qr_generator_key_(std::move(qr_generator_key)) { + qr_generator_key_(std::move(qr_generator_key)), + pairing_callback_(std::move(pairing_callback)) { // Windows currently does not support multiple EIDs, thus we ignore any extra // discovery data. // TODO(https://crbug.com/837088): Add support for multiple EIDs on Windows. @@ -204,32 +310,41 @@ FidoCableDiscovery::~FidoCableDiscovery() { base::Optional<std::unique_ptr<FidoCableHandshakeHandler>> FidoCableDiscovery::CreateHandshakeHandler( FidoCableDevice* device, - const CableDiscoveryData* discovery_data) { + const CableDiscoveryData& discovery_data, + const CableNonce& nonce, + const CableEidArray& eid) { std::unique_ptr<FidoCableHandshakeHandler> handler; - switch (discovery_data->version) { - case 1: { + switch (discovery_data.version) { + case CableDiscoveryData::Version::V1: { // Nonce is embedded as first 8 bytes of client EID. std::array<uint8_t, 8> nonce; const bool ok = fido_parsing_utils::ExtractArray( - discovery_data->client_eid, 0, &nonce); + discovery_data.v1->client_eid, 0, &nonce); DCHECK(ok); handler.reset(new FidoCableV1HandshakeHandler( - device, nonce, discovery_data->session_pre_key)); + device, nonce, discovery_data.v1->session_pre_key)); break; } - case 2: + case CableDiscoveryData::Version::V2: { if (!base::FeatureList::IsEnabled(device::kWebAuthPhoneSupport)) { return base::nullopt; } + if (!pairing_callback_) { + FIDO_LOG(DEBUG) << "Discarding caBLE v2 handshake because of missing " + "pairing callback"; + return base::nullopt; + } + handler.reset(new FidoCableV2HandshakeHandler( - device, discovery_data->session_pre_key)); + device, discovery_data.v2->psk_gen_key, nonce, eid, + discovery_data.v2->peer_identity, *pairing_callback_)); break; + } - default: - FIDO_LOG(DEBUG) << "Dropping caBLE handshake request for unknown version " - << discovery_data->version; + case CableDiscoveryData::Version::INVALID: + CHECK(false); return base::nullopt; } @@ -241,7 +356,6 @@ void FidoCableDiscovery::DeviceAdded(BluetoothAdapter* adapter, if (!IsCableDevice(device)) return; - FIDO_LOG(DEBUG) << "Discovered caBLE device: " << device->GetAddress(); CableDeviceFound(adapter, device); } @@ -250,8 +364,6 @@ void FidoCableDiscovery::DeviceChanged(BluetoothAdapter* adapter, if (!IsCableDevice(device)) return; - FIDO_LOG(DEBUG) << "Device changed for caBLE device: " - << device->GetAddress(); CableDeviceFound(adapter, device); } @@ -304,26 +416,33 @@ void FidoCableDiscovery::OnStartDiscoverySessionWithFilter( std::unique_ptr<BluetoothDiscoverySession> session) { SetDiscoverySession(std::move(session)); FIDO_LOG(DEBUG) << "Discovery session started."; - StartAdvertisement(); + // Advertising is delayed by 500ms to ensure that any UI has a chance to + // appear as we don't want to start broadcasting without the user being + // aware. + base::SequencedTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::BindOnce(&FidoCableDiscovery::StartAdvertisement, + weak_factory_.GetWeakPtr()), + base::TimeDelta::FromMilliseconds(500)); } void FidoCableDiscovery::StartAdvertisement() { DCHECK(adapter()); - if (discovery_data_.empty() && qr_generator_key_.has_value()) { - // If no caBLE extension was provided then there are no BLE advertisements - // and discovery starts immediately on the assumption that the user will - // scan a QR-code with their phone. - NotifyDiscoveryStarted(true); - return; - } - - FIDO_LOG(DEBUG) << "Starting to advertise clientEID."; + bool advertisements_pending = false; for (const auto& data : discovery_data_) { + if (data.version != CableDiscoveryData::Version::V1) { + continue; + } + + if (!advertisements_pending) { + FIDO_LOG(DEBUG) << "Starting to advertise clientEIDs."; + advertisements_pending = true; + } adapter()->RegisterAdvertisement( - ConstructAdvertisementData(data.version, data.client_eid), + ConstructAdvertisementData(data.v1->client_eid), base::AdaptCallbackForRepeating( base::BindOnce(&FidoCableDiscovery::OnAdvertisementRegistered, - weak_factory_.GetWeakPtr(), data.client_eid)), + weak_factory_.GetWeakPtr(), data.v1->client_eid)), base::AdaptCallbackForRepeating( base::BindOnce(&FidoCableDiscovery::OnAdvertisementRegisterError, weak_factory_.GetWeakPtr()))); @@ -363,45 +482,43 @@ void FidoCableDiscovery::OnAdvertisementRegisterError( void FidoCableDiscovery::RecordAdvertisementResult(bool is_success) { // If at least one advertisement succeeds, then notify discovery start. if (is_success) { - if (!advertisement_success_counter_++) - NotifyDiscoveryStarted(true); - return; + advertisement_success_counter_++; + } else { + advertisement_failure_counter_++; } - - // No advertisements succeeded, no point in continuing with Cable discovery. - if (++advertisement_failure_counter_ == discovery_data_.size()) - NotifyDiscoveryStarted(false); } void FidoCableDiscovery::CableDeviceFound(BluetoothAdapter* adapter, BluetoothDevice* device) { - base::Optional<CableDiscoveryData> found_cable_device_data = - GetCableDiscoveryData(device); const std::string device_address = device->GetAddress(); - if (!found_cable_device_data || - base::Contains(active_authenticator_eids_, - found_cable_device_data->authenticator_eid) || - base::Contains(active_devices_, device_address)) { + if (base::Contains(active_devices_, device_address)) { + return; + } + + base::Optional<Result> maybe_result = GetCableDiscoveryData(device); + if (!maybe_result || + base::Contains(active_authenticator_eids_, maybe_result->eid)) { return; } FIDO_LOG(EVENT) << "Found new caBLE device."; - active_authenticator_eids_.insert(found_cable_device_data->authenticator_eid); active_devices_.insert(device_address); + active_authenticator_eids_.insert(maybe_result->eid); auto cable_device = std::make_unique<FidoCableDevice>(adapter, device->GetAddress()); StopAdvertisements( base::BindOnce(&FidoCableDiscovery::ConductEncryptionHandshake, weak_factory_.GetWeakPtr(), std::move(cable_device), - std::move(*found_cable_device_data))); + std::move(*maybe_result))); } void FidoCableDiscovery::ConductEncryptionHandshake( std::unique_ptr<FidoCableDevice> cable_device, - CableDiscoveryData discovery_data) { + FidoCableDiscovery::Result result) { base::Optional<std::unique_ptr<FidoCableHandshakeHandler>> handshake_handler = - CreateHandshakeHandler(cable_device.get(), &discovery_data); + CreateHandshakeHandler(cable_device.get(), result.discovery_data, + result.nonce, result.eid); if (!handshake_handler) { return; } @@ -430,24 +547,65 @@ void FidoCableDiscovery::ValidateAuthenticatorHandshakeMessage( } } -base::Optional<CableDiscoveryData> FidoCableDiscovery::GetCableDiscoveryData( - const BluetoothDevice* device) const { - auto maybe_discovery_data = GetCableDiscoveryDataFromServiceData(device); - if (maybe_discovery_data) { - FIDO_LOG(DEBUG) << "Found caBLE service data."; - return maybe_discovery_data; +base::Optional<FidoCableDiscovery::Result> +FidoCableDiscovery::GetCableDiscoveryData(const BluetoothDevice* device) const { + base::Optional<CableEidArray> maybe_eid_from_service_data = + MaybeGetEidFromServiceData(device); + std::vector<CableEidArray> uuids = GetUUIDs(device); + + const std::string address = device->GetAddress(); + const auto it = observed_devices_.find(address); + const bool known = it != observed_devices_.end(); + if (known) { + std::unique_ptr<ObservedDeviceData>& data = it->second; + if (maybe_eid_from_service_data == data->service_data && + uuids == data->uuids) { + // Duplicate data. Ignore. + return base::nullopt; + } + } + + auto data = std::make_unique<ObservedDeviceData>(); + data->service_data = maybe_eid_from_service_data; + data->uuids = uuids; + observed_devices_.insert_or_assign(address, std::move(data)); + + // New or updated device information. + if (known) { + FIDO_LOG(DEBUG) << "Updated information for caBLE device " << address + << ":"; + } else { + FIDO_LOG(DEBUG) << "New caBLE device " << address << ":"; } - FIDO_LOG(DEBUG) - << "caBLE service data not found. Searching for caBLE UUIDs instead."; - // iOS devices cannot advertise service data. These devices instead put the - // authenticator EID as a second UUID in addition to the caBLE UUID. - return GetCableDiscoveryDataFromServiceUUIDs(device); + base::Optional<FidoCableDiscovery::Result> ret; + if (maybe_eid_from_service_data.has_value()) { + ret = + GetCableDiscoveryDataFromAuthenticatorEid(*maybe_eid_from_service_data); + FIDO_LOG(DEBUG) << " Service data: " + << ResultDebugString(*maybe_eid_from_service_data, ret); + + } else { + FIDO_LOG(DEBUG) << " Service data: <none>"; + } + + if (!uuids.empty()) { + FIDO_LOG(DEBUG) << " UUIDs:"; + for (const auto& uuid : uuids) { + auto result = GetCableDiscoveryDataFromAuthenticatorEid(uuid); + FIDO_LOG(DEBUG) << " " << ResultDebugString(uuid, result); + if (!ret.has_value() && result.has_value()) { + ret = result; + } + } + } + + return ret; } -base::Optional<CableDiscoveryData> -FidoCableDiscovery::GetCableDiscoveryDataFromServiceData( - const BluetoothDevice* device) const { +// static +base::Optional<CableEidArray> FidoCableDiscovery::MaybeGetEidFromServiceData( + const BluetoothDevice* device) { const auto* service_data = device->GetServiceDataForUUID(CableAdvertisementUUID()); if (!service_data) { @@ -464,19 +622,16 @@ FidoCableDiscovery::GetCableDiscoveryDataFromServiceData( *service_data, 2, &received_authenticator_eid); if (!extract_success) return base::nullopt; - - return GetCableDiscoveryDataFromAuthenticatorEid( - std::move(received_authenticator_eid)); + return received_authenticator_eid; } -base::Optional<CableDiscoveryData> -FidoCableDiscovery::GetCableDiscoveryDataFromServiceUUIDs( - const BluetoothDevice* device) const { +// static +std::vector<CableEidArray> FidoCableDiscovery::GetUUIDs( + const BluetoothDevice* device) { + std::vector<CableEidArray> ret; + const auto service_uuids = device->GetUUIDs(); for (const auto& uuid : service_uuids) { - if (uuid == CableAdvertisementUUID()) - continue; - // |uuid_hex| is a hex string with the format: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx const std::string& uuid_hex = uuid.canonical_value(); @@ -501,47 +656,52 @@ FidoCableDiscovery::GetCableDiscoveryDataFromServiceUUIDs( memcpy(authenticator_eid.data(), uuid_binary.data(), authenticator_eid.size()); - auto match = GetCableDiscoveryDataFromAuthenticatorEid(authenticator_eid); - if (match.has_value()) { - return match; - } + ret.emplace_back(std::move(authenticator_eid)); } - return base::nullopt; + return ret; } -base::Optional<CableDiscoveryData> +base::Optional<FidoCableDiscovery::Result> FidoCableDiscovery::GetCableDiscoveryDataFromAuthenticatorEid( CableEidArray authenticator_eid) const { - auto discovery_data_iterator = - std::find_if(discovery_data_.begin(), discovery_data_.end(), - [&authenticator_eid](const auto& data) { - return authenticator_eid == data.authenticator_eid; - }); - - if (discovery_data_iterator != discovery_data_.end()) { - return *discovery_data_iterator; + for (const auto& candidate : discovery_data_) { + auto maybe_nonce = candidate.Match(authenticator_eid); + if (maybe_nonce) { + return Result(candidate, *maybe_nonce, authenticator_eid, base::nullopt); + } } if (qr_generator_key_) { // Attempt to match |authenticator_eid| as the result of scanning a QR code. const int64_t current_tick = CableDiscoveryData::CurrentTimeTick(); // kNumPreviousTicks is the number of previous ticks that will be accepted - // as valid. Ticks are currently 256ms so the value of eight translates to a - // couple of seconds. - constexpr int kNumPreviousTicks = 8; + // as valid. Ticks are currently 256ms so the value of sixteen translates to + // about four seconds. + constexpr int kNumPreviousTicks = 16; for (int i = 0; i < kNumPreviousTicks; i++) { - uint8_t qr_secret[device::kCableQRSecretSize]; - CableEidArray expected_authenticator_eid; - CableSessionPreKeyArray session_pre_key; - CableDiscoveryData::DeriveQRKeyMaterial( - qr_secret, expected_authenticator_eid, session_pre_key, - *qr_generator_key_, current_tick - i); - if (expected_authenticator_eid == authenticator_eid) { - CableEidArray zero_eid{}; - return CableDiscoveryData(/*version=*/2, zero_eid, authenticator_eid, - session_pre_key); + auto qr_secret = CableDiscoveryData::DeriveQRSecret(*qr_generator_key_, + current_tick - i); + CableDiscoveryData candidate(qr_secret); + auto maybe_nonce = candidate.Match(authenticator_eid); + if (maybe_nonce) { + return Result(candidate, *maybe_nonce, authenticator_eid, i); + } + } + + if (base::Contains(noted_obsolete_eids_, authenticator_eid)) { + for (int i = kNumPreviousTicks; i < 2 * kNumPreviousTicks; i++) { + auto qr_secret = CableDiscoveryData::DeriveQRSecret(*qr_generator_key_, + current_tick - i); + CableDiscoveryData candidate(qr_secret); + if (candidate.Match(authenticator_eid)) { + noted_obsolete_eids_.insert(authenticator_eid); + FIDO_LOG(DEBUG) + << "(EID " << base::HexEncode(authenticator_eid) << " is " << i + << " ticks old and would be valid but for the cutoff)"; + break; + } } } } @@ -549,4 +709,69 @@ FidoCableDiscovery::GetCableDiscoveryDataFromAuthenticatorEid( return base::nullopt; } +// static +std::string FidoCableDiscovery::ResultDebugString( + const CableEidArray& eid, + const base::Optional<FidoCableDiscovery::Result>& result) { + static const uint8_t kAppleContinuity[16] = { + 0xd0, 0x61, 0x1e, 0x78, 0xbb, 0xb4, 0x45, 0x91, + 0xa5, 0xf8, 0x48, 0x79, 0x10, 0xae, 0x43, 0x66, + }; + static const uint8_t kAppleUnknown[16] = { + 0x9f, 0xa4, 0x80, 0xe0, 0x49, 0x67, 0x45, 0x42, + 0x93, 0x90, 0xd3, 0x43, 0xdc, 0x5d, 0x04, 0xae, + }; + static const uint8_t kAppleMedia[16] = { + 0x89, 0xd3, 0x50, 0x2b, 0x0f, 0x36, 0x43, 0x3a, + 0x8e, 0xf4, 0xc5, 0x02, 0xad, 0x55, 0xf8, 0xdc, + }; + static const uint8_t kAppleNotificationCenter[16] = { + 0x79, 0x05, 0xf4, 0x31, 0xb5, 0xce, 0x4e, 0x99, + 0xa4, 0x0f, 0x4b, 0x1e, 0x12, 0x2d, 0x00, 0xd0, + }; + static const uint8_t kCable[16] = { + 0x00, 0x00, 0xfd, 0xe2, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb, + }; + + std::string ret = base::HexEncode(eid) + ""; + + if (!result) { + // Try to identify some common UUIDs that are random and thus otherwise look + // like potential EIDs. + if (memcmp(eid.data(), kAppleContinuity, eid.size()) == 0) { + ret += " (Apple Continuity service)"; + } else if (memcmp(eid.data(), kAppleUnknown, eid.size()) == 0) { + ret += " (Apple service)"; + } else if (memcmp(eid.data(), kAppleMedia, eid.size()) == 0) { + ret += " (Apple Media service)"; + } else if (memcmp(eid.data(), kAppleNotificationCenter, eid.size()) == 0) { + ret += " (Apple Notification service)"; + } else if (memcmp(eid.data(), kCable, eid.size()) == 0) { + ret += " (caBLE indicator)"; + } + return ret; + } + + switch (result->discovery_data.version) { + case CableDiscoveryData::Version::V1: + ret += " (version one match"; + break; + case CableDiscoveryData::Version::V2: + ret += " (version two match"; + break; + case CableDiscoveryData::Version::INVALID: + NOTREACHED(); + } + + if (!result->ticks_back) { + ret += " against pairing data)"; + } else { + ret += " from QR, " + base::NumberToString(*result->ticks_back) + + " tick(s) ago)"; + } + + return ret; +} + } // namespace device diff --git a/chromium/device/fido/cable/fido_cable_discovery.h b/chromium/device/fido/cable/fido_cable_discovery.h index 55dd744be5d..96878e1e7da 100644 --- a/chromium/device/fido/cable/fido_cable_discovery.h +++ b/chromium/device/fido/cable/fido_cable_discovery.h @@ -31,16 +31,53 @@ class FidoCableHandshakeHandler; class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery : public FidoBleDiscoveryBase { public: - FidoCableDiscovery(std::vector<CableDiscoveryData> discovery_data, - base::Optional<QRGeneratorKey> qr_generator_key); + FidoCableDiscovery( + std::vector<CableDiscoveryData> discovery_data, + base::Optional<QRGeneratorKey> qr_generator_key, + base::Optional< + base::RepeatingCallback<void(std::unique_ptr<CableDiscoveryData>)>> + pairing_callback); ~FidoCableDiscovery() override; protected: virtual base::Optional<std::unique_ptr<FidoCableHandshakeHandler>> CreateHandshakeHandler(FidoCableDevice* device, - const CableDiscoveryData* discovery_data); + const CableDiscoveryData& discovery_data, + const CableNonce& nonce, + const CableEidArray& eid); private: + // Result represents a successful match of a received EID against a specific + // |FidoDiscoveryData|. + struct Result { + Result(); + Result(const CableDiscoveryData& in_discovery_data, + const CableNonce& in_nonce, + const CableEidArray& in_eid, + base::Optional<int> ticks_back); + Result(const Result&); + ~Result(); + + CableDiscoveryData discovery_data; + CableNonce nonce; + CableEidArray eid; + // ticks_back is either |base::nullopt|, if the Result is from established + // discovery pairings, or else contains the number of QR ticks back in time + // against which the match was found. + base::Optional<int> ticks_back; + }; + + // ObservedDeviceData contains potential EIDs observed from a BLE device. This + // information is kept in order to de-duplicate device-log entries and make + // debugging easier. + struct ObservedDeviceData { + ObservedDeviceData(); + ~ObservedDeviceData(); + + base::Optional<CableEidArray> service_data; + std::vector<CableEidArray> uuids; + }; + FRIEND_TEST_ALL_PREFIXES(FidoCableDiscoveryTest, TestDiscoveryWithAdvertisementFailures); FRIEND_TEST_ALL_PREFIXES(FidoCableDiscoveryTest, @@ -77,20 +114,23 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery void StopAdvertisements(base::OnceClosure callback); void CableDeviceFound(BluetoothAdapter* adapter, BluetoothDevice* device); void ConductEncryptionHandshake(std::unique_ptr<FidoCableDevice> cable_device, - CableDiscoveryData discovery_data); + Result discovery_data); void ValidateAuthenticatorHandshakeMessage( std::unique_ptr<FidoCableDevice> cable_device, FidoCableHandshakeHandler* handshake_handler, base::Optional<std::vector<uint8_t>> handshake_response); - base::Optional<CableDiscoveryData> GetCableDiscoveryData( - const BluetoothDevice* device) const; - base::Optional<CableDiscoveryData> GetCableDiscoveryDataFromServiceData( + base::Optional<Result> GetCableDiscoveryData( const BluetoothDevice* device) const; - base::Optional<CableDiscoveryData> GetCableDiscoveryDataFromServiceUUIDs( - const BluetoothDevice* device) const; - base::Optional<CableDiscoveryData> GetCableDiscoveryDataFromAuthenticatorEid( + static base::Optional<CableEidArray> MaybeGetEidFromServiceData( + const BluetoothDevice* device); + static std::vector<CableEidArray> GetUUIDs(const BluetoothDevice* device); + base::Optional<Result> GetCableDiscoveryDataFromAuthenticatorEid( CableEidArray authenticator_eid) const; + // ResultDebugString returns a containing a hex dump of |eid| and a + // description of |result|, if present. + static std::string ResultDebugString(const CableEidArray& eid, + const base::Optional<Result>& result); std::vector<CableDiscoveryData> discovery_data_; // active_authenticator_eids_ contains authenticator EIDs for which a @@ -109,6 +149,18 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery advertisements_; std::vector<std::unique_ptr<FidoCableHandshakeHandler>> cable_handshake_handlers_; + base::Optional< + base::RepeatingCallback<void(std::unique_ptr<CableDiscoveryData>)>> + pairing_callback_; + + // observed_devices_ caches the information from observed caBLE devices so + // that the device-log isn't spammed. + mutable base::flat_map<std::string, std::unique_ptr<ObservedDeviceData>> + observed_devices_; + // noted_obsolete_eids_ remembers QR-code EIDs that have been logged as + // valid-but-expired in order to avoid spamming the device-log. + mutable base::flat_set<CableEidArray> noted_obsolete_eids_; + base::WeakPtrFactory<FidoCableDiscovery> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(FidoCableDiscovery); diff --git a/chromium/device/fido/cable/fido_cable_discovery_unittest.cc b/chromium/device/fido/cable/fido_cable_discovery_unittest.cc index c7cac3ae0cb..24364aade0e 100644 --- a/chromium/device/fido/cable/fido_cable_discovery_unittest.cc +++ b/chromium/device/fido/cable/fido_cable_discovery_unittest.cc @@ -25,14 +25,17 @@ #include "testing/gtest/include/gtest/gtest.h" using ::testing::_; -using ::testing::Sequence; using ::testing::NiceMock; +using ::testing::Sequence; namespace device { namespace { -constexpr uint8_t kTestCableVersionNumber = 0x01; +constexpr auto kTestCableVersion = CableDiscoveryData::Version::V1; +#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS) +constexpr auto kTestCableVersionNumber = 1; +#endif // Constants required for discovering and constructing a Cable device that // are given by the relying party via an extension. @@ -291,20 +294,19 @@ class FakeFidoCableDiscovery : public FidoCableDiscovery { public: explicit FakeFidoCableDiscovery( std::vector<CableDiscoveryData> discovery_data) - : FidoCableDiscovery(std::move(discovery_data), BogusQRGeneratorKey()) {} + : FidoCableDiscovery(std::move(discovery_data), + BogusQRGeneratorKey(), + /*pairing_callback=*/base::nullopt) {} ~FakeFidoCableDiscovery() override = default; private: base::Optional<std::unique_ptr<FidoCableHandshakeHandler>> CreateHandshakeHandler(FidoCableDevice* device, - const CableDiscoveryData* discovery_data) override { - // Nonce is embedded as first 8 bytes of client EID. - std::array<uint8_t, 8> nonce; - const bool ok = - fido_parsing_utils::ExtractArray(discovery_data->client_eid, 0, &nonce); - DCHECK(ok); + const CableDiscoveryData& discovery_data, + const CableNonce& nonce, + const CableEidArray& eid) override { return std::make_unique<FakeHandshakeHandler>( - device, nonce, discovery_data->session_pre_key); + device, nonce, discovery_data.v1->session_pre_key); } static std::array<uint8_t, 32> BogusQRGeneratorKey() { @@ -320,18 +322,63 @@ class FidoCableDiscoveryTest : public ::testing::Test { public: std::unique_ptr<FidoCableDiscovery> CreateDiscovery() { std::vector<CableDiscoveryData> discovery_data; - discovery_data.emplace_back(kTestCableVersionNumber, kClientEid, + discovery_data.emplace_back(kTestCableVersion, kClientEid, kAuthenticatorEid, kTestSessionPreKey); return std::make_unique<FakeFidoCableDiscovery>(std::move(discovery_data)); } - base::test::TaskEnvironment task_environment_; + base::test::TaskEnvironment task_environment_{ + base::test::TaskEnvironment::TimeSource::MOCK_TIME}; }; +// Tests discovery without a BLE adapter. +TEST_F(FidoCableDiscoveryTest, TestDiscoveryFails) { + auto cable_discovery = CreateDiscovery(); + NiceMock<MockFidoDiscoveryObserver> mock_observer; + EXPECT_CALL(mock_observer, + DiscoveryStarted(cable_discovery.get(), false, + std::vector<FidoAuthenticator*>())); + EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _)).Times(0); + cable_discovery->set_observer(&mock_observer); + + auto mock_adapter = + base::MakeRefCounted<::testing::NiceMock<CableMockAdapter>>(); + EXPECT_CALL(*mock_adapter, IsPresent()).WillOnce(::testing::Return(false)); + + BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter); + cable_discovery->Start(); + task_environment_.FastForwardUntilNoTasksRemain(); +} + +// Tests discovery with a powered-off BLE adapter. Not calling +// DiscoveryStarted() in the case of a present-but-unpowered adapter leads to a +// deadlock between the discovery and the UI (see crbug.com/1018416). +TEST_F(FidoCableDiscoveryTest, TestDiscoveryStartedWithUnpoweredAdapter) { + auto cable_discovery = CreateDiscovery(); + NiceMock<MockFidoDiscoveryObserver> mock_observer; + EXPECT_CALL(mock_observer, + DiscoveryStarted(cable_discovery.get(), true, + std::vector<FidoAuthenticator*>())); + EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _)).Times(0); + cable_discovery->set_observer(&mock_observer); + + auto mock_adapter = + base::MakeRefCounted<::testing::NiceMock<CableMockAdapter>>(); + EXPECT_CALL(*mock_adapter, IsPresent()).WillOnce(::testing::Return(true)); + EXPECT_CALL(*mock_adapter, IsPowered()).WillOnce(::testing::Return(false)); + + BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter); + cable_discovery->Start(); + task_environment_.FastForwardUntilNoTasksRemain(); +} + // Tests regular successful discovery flow for Cable device. TEST_F(FidoCableDiscoveryTest, TestDiscoveryFindsNewDevice) { auto cable_discovery = CreateDiscovery(); NiceMock<MockFidoDiscoveryObserver> mock_observer; + EXPECT_CALL(mock_observer, + DiscoveryStarted(cable_discovery.get(), true, + std::vector<FidoAuthenticator*>())); EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _)); cable_discovery->set_observer(&mock_observer); @@ -344,13 +391,16 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryFindsNewDevice) { BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter); cable_discovery->Start(); - task_environment_.RunUntilIdle(); + task_environment_.FastForwardUntilNoTasksRemain(); } // Tests successful discovery flow for Apple Cable device. TEST_F(FidoCableDiscoveryTest, TestDiscoveryFindsNewAppleDevice) { auto cable_discovery = CreateDiscovery(); NiceMock<MockFidoDiscoveryObserver> mock_observer; + EXPECT_CALL(mock_observer, + DiscoveryStarted(cable_discovery.get(), true, + std::vector<FidoAuthenticator*>())); EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _)); cable_discovery->set_observer(&mock_observer); @@ -363,7 +413,7 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryFindsNewAppleDevice) { BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter); cable_discovery->Start(); - task_environment_.RunUntilIdle(); + task_environment_.FastForwardUntilNoTasksRemain(); } // Tests a scenario where upon broadcasting advertisement and scanning, client @@ -373,6 +423,8 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryFindsIncorrectDevice) { auto cable_discovery = CreateDiscovery(); NiceMock<MockFidoDiscoveryObserver> mock_observer; EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _)).Times(0); + EXPECT_CALL(mock_observer, DiscoveryStarted(cable_discovery.get(), true, + testing::IsEmpty())); cable_discovery->set_observer(&mock_observer); auto mock_adapter = @@ -384,7 +436,7 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryFindsIncorrectDevice) { BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter); cable_discovery->Start(); - task_environment_.RunUntilIdle(); + task_environment_.FastForwardUntilNoTasksRemain(); } // Windows currently does not support multiple EIDs, so the following tests are @@ -397,9 +449,9 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryFindsIncorrectDevice) { // BluetoothAdapter::RegisterAdvertisement(). TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithMultipleEids) { std::vector<CableDiscoveryData> discovery_data; - discovery_data.emplace_back(kTestCableVersionNumber, kClientEid, - kAuthenticatorEid, kTestSessionPreKey); - discovery_data.emplace_back(kTestCableVersionNumber, kSecondaryClientEid, + discovery_data.emplace_back(kTestCableVersion, kClientEid, kAuthenticatorEid, + kTestSessionPreKey); + discovery_data.emplace_back(kTestCableVersion, kSecondaryClientEid, kSecondaryAuthenticatorEid, kSecondarySessionPreKey); auto cable_discovery = @@ -410,6 +462,9 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithMultipleEids) { mock_adapter->ExpectDiscoveryWithScanCallback(kAuthenticatorEid); NiceMock<MockFidoDiscoveryObserver> mock_observer; + EXPECT_CALL(mock_observer, + DiscoveryStarted(cable_discovery.get(), true, + std::vector<FidoAuthenticator*>())); EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _)); cable_discovery->set_observer(&mock_observer); @@ -423,7 +478,7 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithMultipleEids) { BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter); cable_discovery->Start(); - task_environment_.RunUntilIdle(); + task_environment_.FastForwardUntilNoTasksRemain(); } // Tests a scenario where only one of the two client EID's are advertised @@ -431,14 +486,17 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithMultipleEids) { // scanning process should be invoked. TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithPartialAdvertisementSuccess) { std::vector<CableDiscoveryData> discovery_data; - discovery_data.emplace_back(kTestCableVersionNumber, kClientEid, - kAuthenticatorEid, kTestSessionPreKey); - discovery_data.emplace_back(kTestCableVersionNumber, kSecondaryClientEid, + discovery_data.emplace_back(kTestCableVersion, kClientEid, kAuthenticatorEid, + kTestSessionPreKey); + discovery_data.emplace_back(kTestCableVersion, kSecondaryClientEid, kSecondaryAuthenticatorEid, kSecondarySessionPreKey); auto cable_discovery = std::make_unique<FakeFidoCableDiscovery>(std::move(discovery_data)); NiceMock<MockFidoDiscoveryObserver> mock_observer; + EXPECT_CALL(mock_observer, + DiscoveryStarted(cable_discovery.get(), true, + std::vector<FidoAuthenticator*>())); EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _)); cable_discovery->set_observer(&mock_observer); @@ -456,15 +514,15 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithPartialAdvertisementSuccess) { BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter); cable_discovery->Start(); - task_environment_.RunUntilIdle(); + task_environment_.FastForwardUntilNoTasksRemain(); } // Test the scenario when all advertisement for client EID's fails. TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithAdvertisementFailures) { std::vector<CableDiscoveryData> discovery_data; - discovery_data.emplace_back(kTestCableVersionNumber, kClientEid, - kAuthenticatorEid, kTestSessionPreKey); - discovery_data.emplace_back(kTestCableVersionNumber, kSecondaryClientEid, + discovery_data.emplace_back(kTestCableVersion, kClientEid, kAuthenticatorEid, + kTestSessionPreKey); + discovery_data.emplace_back(kTestCableVersion, kSecondaryClientEid, kSecondaryAuthenticatorEid, kSecondarySessionPreKey); auto cable_discovery = @@ -472,6 +530,8 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithAdvertisementFailures) { NiceMock<MockFidoDiscoveryObserver> mock_observer; EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _)).Times(0); + EXPECT_CALL(mock_observer, DiscoveryStarted(cable_discovery.get(), true, + testing::IsEmpty())); cable_discovery->set_observer(&mock_observer); auto mock_adapter = @@ -488,7 +548,7 @@ TEST_F(FidoCableDiscoveryTest, TestDiscoveryWithAdvertisementFailures) { BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter); cable_discovery->Start(); - task_environment_.RunUntilIdle(); + task_environment_.FastForwardUntilNoTasksRemain(); EXPECT_TRUE(cable_discovery->advertisements_.empty()); } #endif // !defined(OS_WIN) @@ -509,7 +569,7 @@ TEST_F(FidoCableDiscoveryTest, TestUnregisterAdvertisementUponDestruction) { BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter); cable_discovery->Start(); - task_environment_.RunUntilIdle(); + task_environment_.FastForwardUntilNoTasksRemain(); EXPECT_EQ(1u, cable_discovery->advertisements_.size()); cable_discovery.reset(); @@ -519,7 +579,10 @@ TEST_F(FidoCableDiscoveryTest, TestUnregisterAdvertisementUponDestruction) { TEST_F(FidoCableDiscoveryTest, TestResumeDiscoveryAfterPoweredOn) { auto cable_discovery = CreateDiscovery(); NiceMock<MockFidoDiscoveryObserver> mock_observer; - EXPECT_CALL(mock_observer, AuthenticatorAdded); + EXPECT_CALL(mock_observer, + DiscoveryStarted(cable_discovery.get(), true, + std::vector<FidoAuthenticator*>())); + EXPECT_CALL(mock_observer, AuthenticatorAdded(_, _)); cable_discovery->set_observer(&mock_observer); auto mock_adapter = @@ -548,6 +611,7 @@ TEST_F(FidoCableDiscoveryTest, TestResumeDiscoveryAfterPoweredOn) { } mock_adapter->NotifyAdapterPoweredChanged(true); + task_environment_.FastForwardUntilNoTasksRemain(); } } // namespace device diff --git a/chromium/device/fido/cable/fido_cable_handshake_handler.cc b/chromium/device/fido/cable/fido_cable_handshake_handler.cc index 76d7f5bdb7c..c7c26ea52b9 100644 --- a/chromium/device/fido/cable/fido_cable_handshake_handler.cc +++ b/chromium/device/fido/cable/fido_cable_handshake_handler.cc @@ -155,7 +155,7 @@ bool FidoCableV1HandshakeHandler::ValidateAuthenticatorHandshakeMessage( return false; } - cable_device_->SetEncryptionData( + cable_device_->SetV1EncryptionData( base::make_span<32>( GetEncryptionKeyAfterSuccessfulHandshake(base::make_span<16>( authenticator_random_nonce->second.GetBytestring()))), @@ -176,19 +176,32 @@ FidoCableV1HandshakeHandler::GetEncryptionKeyAfterSuccessfulHandshake( /*derived_key_length=*/32); } +// kP256PointSize is the number of bytes in an X9.62 encoding of a P-256 point. +static constexpr size_t kP256PointSize = 65; + FidoCableV2HandshakeHandler::FidoCableV2HandshakeHandler( FidoCableDevice* cable_device, - base::span<const uint8_t, 32> session_pre_key) + base::span<const uint8_t, 32> psk_gen_key, + base::span<const uint8_t, 8> nonce, + base::span<const uint8_t, kCableEphemeralIdSize> eid, + base::Optional<base::span<const uint8_t, kP256PointSize>> peer_identity, + base::RepeatingCallback<void(std::unique_ptr<CableDiscoveryData>)> + pairing_callback) : cable_device_(cable_device), - session_pre_key_(fido_parsing_utils::Materialize(session_pre_key)) {} + eid_(fido_parsing_utils::Materialize(eid)), + pairing_callback_(std::move(pairing_callback)) { + HKDF(psk_.data(), psk_.size(), EVP_sha256(), psk_gen_key.data(), + psk_gen_key.size(), /*salt=*/nonce.data(), nonce.size(), + /*info=*/nullptr, 0); + if (peer_identity) { + peer_identity_ = fido_parsing_utils::Materialize(*peer_identity); + } +} FidoCableV2HandshakeHandler::~FidoCableV2HandshakeHandler() {} namespace { -// P256PointSize is the number of bytes in an X9.62 encoding of a P-256 point. -constexpr size_t P256PointSize = 65; - // HKDF2 implements the functions with the same name from Noise[1], specialized // to the case where |num_outputs| is two. // @@ -198,7 +211,7 @@ std::tuple<std::array<uint8_t, 32>, std::array<uint8_t, 32>> HKDF2( base::span<const uint8_t> ikm) { uint8_t output[32 * 2]; HKDF(output, sizeof(output), EVP_sha256(), ikm.data(), ikm.size(), ck.data(), - ck.size(), /*salt=*/nullptr, 0); + ck.size(), /*info=*/nullptr, 0); std::array<uint8_t, 32> a, b; memcpy(a.data(), &output[0], 32); @@ -217,7 +230,7 @@ std::tuple<std::array<uint8_t, 32>, HKDF3(base::span<const uint8_t, 32> ck, base::span<const uint8_t> ikm) { uint8_t output[32 * 3]; HKDF(output, sizeof(output), EVP_sha256(), ikm.data(), ikm.size(), ck.data(), - ck.size(), /*salt=*/nullptr, 0); + ck.size(), /*info=*/nullptr, 0); std::array<uint8_t, 32> a, b, c; memcpy(a.data(), &output[0], 32); @@ -227,44 +240,86 @@ HKDF3(base::span<const uint8_t, 32> ck, base::span<const uint8_t> ikm) { return std::make_tuple(a, b, c); } +template <size_t N> +bool CopyBytestring(std::array<uint8_t, N>* out, + const cbor::Value::MapValue& map, + int key) { + const auto it = map.find(cbor::Value(key)); + if (it == map.end() || !it->second.is_bytestring()) { + return false; + } + const std::vector<uint8_t> bytestring = it->second.GetBytestring(); + return fido_parsing_utils::ExtractArray(bytestring, /*pos=*/0, out); +} + } // namespace void FidoCableV2HandshakeHandler::InitiateCableHandshake( FidoDevice::DeviceCallback callback) { // See https://www.noiseprotocol.org/noise.html#the-handshakestate-object - static const char kProtocolName[] = "Noise_NNpsk0_P256_AESGCM_SHA256"; - static_assert(sizeof(kProtocolName) == crypto::kSHA256Length, + static const char kNNProtocolName[] = "Noise_NNpsk0_P256_AESGCM_SHA256"; + static const char kNKProtocolName[] = "Noise_NKpsk0_P256_AESGCM_SHA256"; + static_assert(sizeof(kNKProtocolName) == sizeof(kNNProtocolName), + "protocol names are different lengths"); + static_assert(sizeof(kNNProtocolName) == crypto::kSHA256Length, "name may need padding if not HASHLEN bytes long"); static_assert( std::tuple_size<decltype(chaining_key_)>::value == crypto::kSHA256Length, "chaining_key_ is wrong size"); static_assert(std::tuple_size<decltype(h_)>::value == crypto::kSHA256Length, "h_ is wrong size"); - memcpy(chaining_key_.data(), kProtocolName, sizeof(kProtocolName)); + if (peer_identity_) { + memcpy(chaining_key_.data(), kNKProtocolName, sizeof(kNKProtocolName)); + } else { + memcpy(chaining_key_.data(), kNNProtocolName, sizeof(kNNProtocolName)); + } h_ = chaining_key_; - static const uint8_t kPrologue[] = "caBLE QR code handshake"; - MixHash(kPrologue); + if (peer_identity_) { + static const uint8_t kPrologue[] = "caBLE handshake"; + MixHash(kPrologue); + } else { + static const uint8_t kPrologue[] = "caBLE QR code handshake"; + MixHash(kPrologue); + } - MixKeyAndHash(session_pre_key_); + MixKeyAndHash(psk_); ephemeral_key_.reset(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + const EC_GROUP* group = EC_KEY_get0_group(ephemeral_key_.get()); CHECK(EC_KEY_generate_key(ephemeral_key_.get())); - uint8_t ephemeral_key_public_bytes[P256PointSize]; + uint8_t ephemeral_key_public_bytes[kP256PointSize]; CHECK_EQ(sizeof(ephemeral_key_public_bytes), EC_POINT_point2oct( - EC_KEY_get0_group(ephemeral_key_.get()), - EC_KEY_get0_public_key(ephemeral_key_.get()), + group, EC_KEY_get0_public_key(ephemeral_key_.get()), POINT_CONVERSION_UNCOMPRESSED, ephemeral_key_public_bytes, sizeof(ephemeral_key_public_bytes), /*ctx=*/nullptr)); MixHash(ephemeral_key_public_bytes); MixKey(ephemeral_key_public_bytes); + if (peer_identity_) { + // If we know the identity of the peer from a previous interaction, NKpsk0 + // is performed to ensure that other browsers, which may also know the PSK, + // cannot impersonate the authenticator. + bssl::UniquePtr<EC_POINT> peer_identity_point(EC_POINT_new(group)); + uint8_t es_key[32]; + if (!EC_POINT_oct2point(group, peer_identity_point.get(), + peer_identity_->data(), peer_identity_->size(), + /*ctx=*/nullptr) || + !ECDH_compute_key(es_key, sizeof(es_key), peer_identity_point.get(), + ephemeral_key_.get(), /*kdf=*/nullptr)) { + FIDO_LOG(DEBUG) << "Dropping handshake because peer identity is invalid"; + return; + } + MixKey(es_key); + } + std::vector<uint8_t> ciphertext = Encrypt(base::span<const uint8_t>()); MixHash(ciphertext); std::vector<uint8_t> handshake_message; - handshake_message.reserve(sizeof(ephemeral_key_public_bytes) + + handshake_message.reserve(eid_.size() + sizeof(ephemeral_key_public_bytes) + ciphertext.size()); + handshake_message.insert(handshake_message.end(), eid_.begin(), eid_.end()); handshake_message.insert( handshake_message.end(), ephemeral_key_public_bytes, ephemeral_key_public_bytes + sizeof(ephemeral_key_public_bytes)); @@ -277,17 +332,17 @@ void FidoCableV2HandshakeHandler::InitiateCableHandshake( bool FidoCableV2HandshakeHandler::ValidateAuthenticatorHandshakeMessage( base::span<const uint8_t> response) { - if (response.size() < P256PointSize) { + if (response.size() < kP256PointSize) { return false; } - auto peer_point_bytes = response.subspan(0, P256PointSize); - auto ciphertext = response.subspan(P256PointSize); + auto peer_point_bytes = response.subspan(0, kP256PointSize); + auto ciphertext = response.subspan(kP256PointSize); bssl::UniquePtr<EC_POINT> peer_point( EC_POINT_new(EC_KEY_get0_group(ephemeral_key_.get()))); uint8_t shared_key[32]; - if (!EC_POINT_oct2point(EC_KEY_get0_group(ephemeral_key_.get()), - peer_point.get(), peer_point_bytes.data(), + const EC_GROUP* group = EC_KEY_get0_group(ephemeral_key_.get()); + if (!EC_POINT_oct2point(group, peer_point.get(), peer_point_bytes.data(), peer_point_bytes.size(), /*ctx=*/nullptr) || !ECDH_compute_key(shared_key, sizeof(shared_key), peer_point.get(), ephemeral_key_.get(), /*kdf=*/nullptr)) { @@ -298,20 +353,63 @@ bool FidoCableV2HandshakeHandler::ValidateAuthenticatorHandshakeMessage( MixKey(peer_point_bytes); MixKey(shared_key); - auto maybe_plaintext = Decrypt(ciphertext); - if (!maybe_plaintext || !maybe_plaintext->empty()) { + auto plaintext = Decrypt(ciphertext); + if (!plaintext || plaintext->empty() != peer_identity_.has_value()) { + FIDO_LOG(DEBUG) << "Invalid caBLE handshake message"; return false; } + + if (!peer_identity_) { + // Handshakes without a peer identity (i.e. NNpsk0 handshakes setup from a + // QR code) send a padded message in the reply. This message can, + // optionally, contain CBOR-encoded, long-term pairing information. + const size_t padding_length = (*plaintext)[plaintext->size() - 1]; + if (padding_length + 1 > plaintext->size()) { + FIDO_LOG(DEBUG) << "Invalid padding in caBLE handshake message"; + return false; + } + plaintext->resize(plaintext->size() - padding_length - 1); + + if (!plaintext->empty()) { + base::Optional<cbor::Value> pairing = cbor::Reader::Read(*plaintext); + if (!pairing || !pairing->is_map()) { + FIDO_LOG(DEBUG) << "CBOR parse failure in caBLE handshake message"; + return false; + } + + auto future_discovery = std::make_unique<CableDiscoveryData>(); + future_discovery->version = CableDiscoveryData::Version::V2; + future_discovery->v2.emplace(); + future_discovery->v2->peer_identity.emplace(); + + const cbor::Value::MapValue& pairing_map(pairing->GetMap()); + const auto name_it = pairing_map.find(cbor::Value(4)); + if (!CopyBytestring(&future_discovery->v2->eid_gen_key, pairing_map, 1) || + !CopyBytestring(&future_discovery->v2->psk_gen_key, pairing_map, 2) || + !CopyBytestring(&future_discovery->v2->peer_identity.value(), + pairing_map, 3) || + name_it == pairing_map.end() || !name_it->second.is_string() || + !EC_POINT_oct2point(group, peer_point.get(), + future_discovery->v2->peer_identity->data(), + future_discovery->v2->peer_identity->size(), + /*ctx=*/nullptr)) { + FIDO_LOG(DEBUG) << "CBOR structure error in caBLE handshake message"; + return false; + } + + future_discovery->v2->peer_name = name_it->second.GetString(); + pairing_callback_.Run(std::move(future_discovery)); + } + } + // Here the spec says to do MixHash(ciphertext), but there are no more // handshake messages so that's moot. // MixHash(ciphertext); - std::array<uint8_t, 32> key1, unused_key2; - std::tie(key1, unused_key2) = + std::array<uint8_t, 32> read_key, write_key; + std::tie(write_key, read_key) = HKDF2(chaining_key_, base::span<const uint8_t>()); - - uint8_t zero_nonce[8] = {0}; - cable_device_->SetEncryptionData(key1, zero_nonce); + cable_device_->SetV2EncryptionData(read_key, write_key); return true; } @@ -351,10 +449,7 @@ void FidoCableV2HandshakeHandler::InitializeKey( std::vector<uint8_t> FidoCableV2HandshakeHandler::Encrypt( base::span<const uint8_t> plaintext) { uint8_t nonce[12] = {0}; - nonce[8] = symmetric_nonce_ >> 24; - nonce[9] = symmetric_nonce_ >> 16; - nonce[10] = symmetric_nonce_ >> 8; - nonce[11] = symmetric_nonce_; + memcpy(nonce, &symmetric_nonce_, sizeof(symmetric_nonce_)); symmetric_nonce_++; crypto::Aead aead(crypto::Aead::AES_256_GCM); @@ -365,10 +460,7 @@ std::vector<uint8_t> FidoCableV2HandshakeHandler::Encrypt( base::Optional<std::vector<uint8_t>> FidoCableV2HandshakeHandler::Decrypt( base::span<const uint8_t> ciphertext) { uint8_t nonce[12] = {0}; - nonce[8] = symmetric_nonce_ >> 24; - nonce[9] = symmetric_nonce_ >> 16; - nonce[10] = symmetric_nonce_ >> 8; - nonce[11] = symmetric_nonce_; + memcpy(nonce, &symmetric_nonce_, sizeof(symmetric_nonce_)); symmetric_nonce_++; crypto::Aead aead(crypto::Aead::AES_256_GCM); diff --git a/chromium/device/fido/cable/fido_cable_handshake_handler.h b/chromium/device/fido/cable/fido_cable_handshake_handler.h index 06c4bef4f05..e05a982f9dc 100644 --- a/chromium/device/fido/cable/fido_cable_handshake_handler.h +++ b/chromium/device/fido/cable/fido_cable_handshake_handler.h @@ -17,6 +17,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/optional.h" +#include "device/fido/cable/cable_discovery_data.h" #include "device/fido/fido_device.h" #include "third_party/boringssl/src/include/openssl/base.h" @@ -78,8 +79,14 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableV1HandshakeHandler class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableV2HandshakeHandler : public FidoCableHandshakeHandler { public: - FidoCableV2HandshakeHandler(FidoCableDevice* device, - base::span<const uint8_t, 32> session_pre_key); + FidoCableV2HandshakeHandler( + FidoCableDevice* device, + base::span<const uint8_t, 32> psk_gen_key, + base::span<const uint8_t, 8> nonce, + base::span<const uint8_t, kCableEphemeralIdSize> eid, + base::Optional<base::span<const uint8_t, 65>> peer_identity, + base::RepeatingCallback<void(std::unique_ptr<CableDiscoveryData>)> + pairing_callback); ~FidoCableV2HandshakeHandler() override; // FidoCableHandshakeHandler: @@ -97,12 +104,16 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableV2HandshakeHandler base::span<const uint8_t> ciphertext); FidoCableDevice* const cable_device_; - std::array<uint8_t, 32> session_pre_key_; + std::array<uint8_t, 16> eid_; + std::array<uint8_t, 32> psk_; std::array<uint8_t, 32> chaining_key_; std::array<uint8_t, 32> h_; std::array<uint8_t, 32> symmetric_key_; uint32_t symmetric_nonce_; + base::Optional<std::array<uint8_t, 65>> peer_identity_; bssl::UniquePtr<EC_KEY> ephemeral_key_; + base::RepeatingCallback<void(std::unique_ptr<CableDiscoveryData>)> + pairing_callback_; }; } // namespace device diff --git a/chromium/device/fido/cable/fido_cable_handshake_handler_v2_fuzzer.cc b/chromium/device/fido/cable/fido_cable_handshake_handler_v2_fuzzer.cc index 279fd85ba19..286ce789db7 100644 --- a/chromium/device/fido/cable/fido_cable_handshake_handler_v2_fuzzer.cc +++ b/chromium/device/fido/cable/fido_cable_handshake_handler_v2_fuzzer.cc @@ -18,27 +18,44 @@ namespace { -constexpr std::array<uint8_t, 32> kTestSessionPreKey = {{ +constexpr std::array<uint8_t, 32> kTestPSKGeneratorKey = { 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 = {1, 2, 3, 4, 5, 6, 7, 8}; +constexpr std::array<uint8_t, 16> kTestEphemeralID = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; +constexpr std::array<uint8_t, 65> kTestPeerIdentity = { + 0x04, 0x67, 0x80, 0xc5, 0xfc, 0x70, 0x27, 0x5e, 0x2c, 0x70, 0x61, + 0xa0, 0xe7, 0x87, 0x7b, 0xb1, 0x74, 0xde, 0xad, 0xeb, 0x98, 0x87, + 0x02, 0x7f, 0x3f, 0xa8, 0x36, 0x54, 0x15, 0x8b, 0xa7, 0xf5, 0x0c, + 0x3c, 0xba, 0x8c, 0x34, 0xbc, 0x35, 0xd2, 0x0e, 0x81, 0xf7, 0x30, + 0xac, 0x1c, 0x7b, 0xd6, 0xd6, 0x61, 0xa9, 0x42, 0xf9, 0x0c, 0x6a, + 0x9c, 0xa5, 0x5c, 0x51, 0x2f, 0x9e, 0x4a, 0x00, 0x12, 0x66, +}; 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); + auto input = base::make_span(raw_data, size); auto adapter = base::MakeRefCounted<::testing::NiceMock<device::MockBluetoothAdapter>>(); device::FidoCableDevice test_cable_device(adapter.get(), kTestDeviceAddress); test_cable_device.SetStateForTesting( device::FidoCableDevice::State::kDeviceError); - device::FidoCableV2HandshakeHandler handshake_handler_v2(&test_cable_device, - kTestSessionPreKey); + base::Optional<base::span<const uint8_t, 65>> peer_identity; + if (!input.empty() && (input[0] & 1)) { + peer_identity = kTestPeerIdentity; + input = input.subspan(1); + } + + device::FidoCableV2HandshakeHandler handshake_handler_v2( + &test_cable_device, kTestPSKGeneratorKey, kTestNonce, kTestEphemeralID, + peer_identity, base::DoNothing()); handshake_handler_v2.InitiateCableHandshake(base::DoNothing()); - handshake_handler_v2.ValidateAuthenticatorHandshakeMessage(data_span); + handshake_handler_v2.ValidateAuthenticatorHandshakeMessage(input); return 0; } diff --git a/chromium/device/fido/credential_management_handler_unittest.cc b/chromium/device/fido/credential_management_handler_unittest.cc index b3701e718df..067e27c6541 100644 --- a/chromium/device/fido/credential_management_handler_unittest.cc +++ b/chromium/device/fido/credential_management_handler_unittest.cc @@ -9,7 +9,6 @@ #include "base/bind.h" #include "base/strings/strcat.h" #include "base/test/task_environment.h" -#include "build/build_config.h" #include "device/fido/credential_management.h" #include "device/fido/fido_constants.h" #include "device/fido/fido_request_handler_base.h" @@ -19,10 +18,6 @@ #include "device/fido/virtual_fido_device_factory.h" #include "testing/gtest/include/gtest/gtest.h" -#if defined(OS_WIN) -#include "device/fido/win/fake_webauthn_api.h" -#endif // defined(OS_WIN) - namespace device { namespace { @@ -66,11 +61,6 @@ class CredentialManagementHandlerTest : public ::testing::Test { test::ValueCallbackReceiver<CtapDeviceResponseCode> delete_callback_; test::ValueCallbackReceiver<CredentialManagementStatus> finished_callback_; test::VirtualFidoDeviceFactory virtual_device_factory_; - -#if defined(OS_WIN) - device::ScopedFakeWinWebAuthnApi win_webauthn_api_ = - device::ScopedFakeWinWebAuthnApi::MakeUnavailable(); -#endif // defined(OS_WIN) }; TEST_F(CredentialManagementHandlerTest, Test) { diff --git a/chromium/device/fido/fake_fido_discovery_unittest.cc b/chromium/device/fido/fake_fido_discovery_unittest.cc index 81c85a684cc..1a89940aaeb 100644 --- a/chromium/device/fido/fake_fido_discovery_unittest.cc +++ b/chromium/device/fido/fake_fido_discovery_unittest.cc @@ -67,7 +67,8 @@ TEST_F(FakeFidoDiscoveryTest, StartDiscovery) { ASSERT_TRUE(discovery.is_start_requested()); ASSERT_FALSE(discovery.is_running()); - EXPECT_CALL(observer, DiscoveryStarted(&discovery, true)); + EXPECT_CALL(observer, DiscoveryStarted(&discovery, true, + std::vector<FidoAuthenticator*>())); discovery.WaitForCallToStartAndSimulateSuccess(); ASSERT_TRUE(discovery.is_running()); ASSERT_TRUE(discovery.is_start_requested()); @@ -87,7 +88,8 @@ TEST_F(FakeFidoDiscoveryTest, WaitThenStartStopDiscovery) { ASSERT_FALSE(discovery.is_running()); ASSERT_TRUE(discovery.is_start_requested()); - EXPECT_CALL(observer, DiscoveryStarted(&discovery, true)); + EXPECT_CALL(observer, DiscoveryStarted(&discovery, true, + std::vector<FidoAuthenticator*>())); discovery.SimulateStarted(true); ASSERT_TRUE(discovery.is_running()); ASSERT_TRUE(discovery.is_start_requested()); @@ -105,7 +107,8 @@ TEST_F(FakeFidoDiscoveryTest, StartFail) { ASSERT_FALSE(discovery.is_running()); ASSERT_TRUE(discovery.is_start_requested()); - EXPECT_CALL(observer, DiscoveryStarted(&discovery, false)); + EXPECT_CALL(observer, DiscoveryStarted(&discovery, false, + std::vector<FidoAuthenticator*>())); discovery.SimulateStarted(false); ASSERT_FALSE(discovery.is_running()); ASSERT_TRUE(discovery.is_start_requested()); @@ -119,19 +122,16 @@ TEST_F(FakeFidoDiscoveryTest, AddDevice) { discovery.set_observer(&observer); discovery.Start(); - auto device0 = std::make_unique<MockFidoDevice>(); EXPECT_CALL(*device0, GetId()).WillOnce(::testing::Return("device0")); base::RunLoop device0_done; - EXPECT_CALL(observer, AuthenticatorAdded(&discovery, _)) - .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)); + EXPECT_CALL(observer, DiscoveryStarted(&discovery, true, testing::SizeIs(1))) + .WillOnce(testing::InvokeWithoutArgs( + [&device0_done]() { device0_done.Quit(); })); discovery.SimulateStarted(true); + device0_done.Run(); ::testing::Mock::VerifyAndClearExpectations(&observer); auto device1 = std::make_unique<MockFidoDevice>(); diff --git a/chromium/device/fido/features.cc b/chromium/device/fido/features.cc index d80f894e60d..f0a0b1e37ac 100644 --- a/chromium/device/fido/features.cc +++ b/chromium/device/fido/features.cc @@ -16,9 +16,6 @@ const base::Feature kWebAuthUseNativeWinApi{"WebAuthenticationUseNativeWinApi", base::FEATURE_ENABLED_BY_DEFAULT}; #endif // defined(OS_WIN) -extern const base::Feature kWebAuthResidentKeys{ - "WebAuthenticationResidentKeys", base::FEATURE_ENABLED_BY_DEFAULT}; - extern const base::Feature kWebAuthBiometricEnrollment{ "WebAuthenticationBiometricEnrollment", base::FEATURE_DISABLED_BY_DEFAULT}; diff --git a/chromium/device/fido/features.h b/chromium/device/fido/features.h index 24f04e102ef..78c83eda3aa 100644 --- a/chromium/device/fido/features.h +++ b/chromium/device/fido/features.h @@ -16,10 +16,6 @@ COMPONENT_EXPORT(DEVICE_FIDO) extern const base::Feature kWebAuthUseNativeWinApi; #endif // defined(OS_WIN) -// Enable support for resident keys. -COMPONENT_EXPORT(DEVICE_FIDO) -extern const base::Feature kWebAuthResidentKeys; - // Enable biometric enrollment in the security keys settings UI. COMPONENT_EXPORT(DEVICE_FIDO) extern const base::Feature kWebAuthBiometricEnrollment; diff --git a/chromium/device/fido/fido_authenticator.cc b/chromium/device/fido/fido_authenticator.cc index 9574e871e20..11f8d3b8794 100644 --- a/chromium/device/fido/fido_authenticator.cc +++ b/chromium/device/fido/fido_authenticator.cc @@ -91,9 +91,10 @@ void FidoAuthenticator::GetSensorInfo(BioEnrollmentCallback) { NOTREACHED(); } -void FidoAuthenticator::BioEnrollFingerprint(const pin::TokenResponse&, - BioEnrollmentSampleCallback, - BioEnrollmentCallback) { +void FidoAuthenticator::BioEnrollFingerprint( + const pin::TokenResponse&, + base::Optional<std::vector<uint8_t>> template_id, + BioEnrollmentCallback) { NOTREACHED(); } diff --git a/chromium/device/fido/fido_authenticator.h b/chromium/device/fido/fido_authenticator.h index 71747c25dae..82168747d52 100644 --- a/chromium/device/fido/fido_authenticator.h +++ b/chromium/device/fido/fido_authenticator.h @@ -73,8 +73,6 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoAuthenticator { using BioEnrollmentCallback = base::OnceCallback<void(CtapDeviceResponseCode, base::Optional<BioEnrollmentResponse>)>; - using BioEnrollmentSampleCallback = - base::RepeatingCallback<void(BioEnrollmentSampleStatus, uint8_t)>; FidoAuthenticator() = default; virtual ~FidoAuthenticator() = default; @@ -174,9 +172,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoAuthenticator { // Biometric enrollment commands. virtual void GetModality(BioEnrollmentCallback callback); virtual void GetSensorInfo(BioEnrollmentCallback callback); - virtual void BioEnrollFingerprint(const pin::TokenResponse&, - BioEnrollmentSampleCallback, - BioEnrollmentCallback); + virtual void BioEnrollFingerprint( + const pin::TokenResponse&, + base::Optional<std::vector<uint8_t>> template_id, + BioEnrollmentCallback); virtual void BioEnrollCancel(BioEnrollmentCallback); virtual void BioEnrollEnumerate(const pin::TokenResponse&, BioEnrollmentCallback); diff --git a/chromium/device/fido/fido_constants.cc b/chromium/device/fido/fido_constants.cc index c07c373b1ab..3a2ae6730e9 100644 --- a/chromium/device/fido/fido_constants.cc +++ b/chromium/device/fido/fido_constants.cc @@ -32,7 +32,7 @@ const char kCredentialManagementPreviewMapKey[] = "credentialMgmtPreview"; const char kBioEnrollmentMapKey[] = "bioEnroll"; const char kBioEnrollmentPreviewMapKey[] = "userVerificationMgmtPreview"; -const base::TimeDelta kDeviceTimeout = base::TimeDelta::FromSeconds(10); +const base::TimeDelta kDeviceTimeout = base::TimeDelta::FromSeconds(20); const base::TimeDelta kU2fRetryDelay = base::TimeDelta::FromMilliseconds(200); const char kFormatKey[] = "fmt"; diff --git a/chromium/device/fido/fido_device_authenticator.cc b/chromium/device/fido/fido_device_authenticator.cc index 3f741483732..3ca8dd6c109 100644 --- a/chromium/device/fido/fido_device_authenticator.cc +++ b/chromium/device/fido/fido_device_authenticator.cc @@ -513,80 +513,42 @@ void FidoDeviceAuthenticator::GetSensorInfo(BioEnrollmentCallback callback) { } void FidoDeviceAuthenticator::BioEnrollFingerprint( - const pin::TokenResponse& response, - BioEnrollmentSampleCallback sample_callback, - BioEnrollmentCallback completion_callback) { + const pin::TokenResponse& pin_token, + base::Optional<std::vector<uint8_t>> template_id, + BioEnrollmentCallback callback) { RunOperation<BioEnrollmentRequest, BioEnrollmentResponse>( - BioEnrollmentRequest::ForEnrollBegin( - GetBioEnrollmentRequestVersion(*Options()), response), - base::BindOnce(&FidoDeviceAuthenticator::OnBioEnroll, - weak_factory_.GetWeakPtr(), std::move(response), - std::move(sample_callback), std::move(completion_callback), - /*current_template_id=*/base::nullopt), - base::BindOnce(&BioEnrollmentResponse::Parse)); + template_id ? BioEnrollmentRequest::ForEnrollNextSample( + GetBioEnrollmentRequestVersion(*Options()), + std::move(pin_token), std::move(*template_id)) + : BioEnrollmentRequest::ForEnrollBegin( + GetBioEnrollmentRequestVersion(*Options()), + std::move(pin_token)), + std::move(callback), base::BindOnce(&BioEnrollmentResponse::Parse)); } void FidoDeviceAuthenticator::BioEnrollRename( - const pin::TokenResponse& response, + const pin::TokenResponse& pin_token, std::vector<uint8_t> id, std::string name, BioEnrollmentCallback callback) { RunOperation<BioEnrollmentRequest, BioEnrollmentResponse>( BioEnrollmentRequest::ForRename( - GetBioEnrollmentRequestVersion(*Options()), response, std::move(id), + GetBioEnrollmentRequestVersion(*Options()), pin_token, std::move(id), std::move(name)), std::move(callback), base::BindOnce(&BioEnrollmentResponse::Parse)); } void FidoDeviceAuthenticator::BioEnrollDelete( - const pin::TokenResponse& response, + const pin::TokenResponse& pin_token, std::vector<uint8_t> template_id, BioEnrollmentCallback callback) { RunOperation<BioEnrollmentRequest, BioEnrollmentResponse>( BioEnrollmentRequest::ForDelete( - GetBioEnrollmentRequestVersion(*Options()), response, + GetBioEnrollmentRequestVersion(*Options()), pin_token, std::move(template_id)), std::move(callback), base::BindOnce(&BioEnrollmentResponse::Parse)); } -void FidoDeviceAuthenticator::OnBioEnroll( - pin::TokenResponse response, - BioEnrollmentSampleCallback sample_callback, - BioEnrollmentCallback completion_callback, - base::Optional<std::vector<uint8_t>> current_template_id, - CtapDeviceResponseCode code, - base::Optional<BioEnrollmentResponse> bio) { - if (code != CtapDeviceResponseCode::kSuccess || !bio->last_status || - !bio->remaining_samples || bio->remaining_samples == 0) { - std::move(completion_callback).Run(code, std::move(bio)); - return; - } - if (!current_template_id) { - if (!bio->template_id) { - // The templateId response field is required in the first response of each - // enrollment. - std::move(completion_callback) - .Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt); - return; - } - current_template_id = *bio->template_id; - } - - sample_callback.Run(*bio->last_status, *bio->remaining_samples); - - auto request = BioEnrollmentRequest::ForEnrollNextSample( - GetBioEnrollmentRequestVersion(*Options()), response, - *current_template_id); - - RunOperation<BioEnrollmentRequest, BioEnrollmentResponse>( - std::move(request), - base::BindOnce(&FidoDeviceAuthenticator::OnBioEnroll, - weak_factory_.GetWeakPtr(), std::move(response), - std::move(sample_callback), std::move(completion_callback), - std::move(current_template_id)), - base::BindOnce(&BioEnrollmentResponse::Parse)); -} - void FidoDeviceAuthenticator::BioEnrollCancel(BioEnrollmentCallback callback) { RunOperation<BioEnrollmentRequest, BioEnrollmentResponse>( BioEnrollmentRequest::ForCancel( diff --git a/chromium/device/fido/fido_device_authenticator.h b/chromium/device/fido/fido_device_authenticator.h index 6ca4a1ae662..0f0c2b7ca3b 100644 --- a/chromium/device/fido/fido_device_authenticator.h +++ b/chromium/device/fido/fido_device_authenticator.h @@ -82,7 +82,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDeviceAuthenticator void GetModality(BioEnrollmentCallback callback) override; void GetSensorInfo(BioEnrollmentCallback callback) override; void BioEnrollFingerprint(const pin::TokenResponse&, - BioEnrollmentSampleCallback, + base::Optional<std::vector<uint8_t>> template_id, BioEnrollmentCallback) override; void BioEnrollCancel(BioEnrollmentCallback) override; void BioEnrollEnumerate(const pin::TokenResponse&, @@ -154,13 +154,6 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDeviceAuthenticator CtapDeviceResponseCode status, base::Optional<EnumerateCredentialsResponse> response); - void OnBioEnroll(pin::TokenResponse, - BioEnrollmentSampleCallback sample_callback, - BioEnrollmentCallback completion_callback, - base::Optional<std::vector<uint8_t>> current_template_id, - CtapDeviceResponseCode, - base::Optional<BioEnrollmentResponse>); - const std::unique_ptr<FidoDevice> device_; base::Optional<AuthenticatorSupportedOptions> options_; std::unique_ptr<FidoTask> task_; diff --git a/chromium/device/fido/fido_device_discovery.cc b/chromium/device/fido/fido_device_discovery.cc index ae9ab155fa5..41c25063a3d 100644 --- a/chromium/device/fido/fido_device_discovery.cc +++ b/chromium/device/fido/fido_device_discovery.cc @@ -38,13 +38,18 @@ void FidoDeviceDiscovery::NotifyDiscoveryStarted(bool success) { state_ = State::kRunning; if (!observer()) return; - observer()->DiscoveryStarted(this, success); + + std::vector<FidoAuthenticator*> authenticators; + authenticators.reserve(authenticators_.size()); + for (const auto& authenticator : authenticators_) + authenticators.push_back(authenticator.second.get()); + observer()->DiscoveryStarted(this, success, std::move(authenticators)); } void FidoDeviceDiscovery::NotifyAuthenticatorAdded( FidoAuthenticator* authenticator) { DCHECK_NE(state_, State::kIdle); - if (!observer()) + if (!observer() || state_ != State::kRunning) return; observer()->AuthenticatorAdded(this, authenticator); } @@ -52,7 +57,7 @@ void FidoDeviceDiscovery::NotifyAuthenticatorAdded( void FidoDeviceDiscovery::NotifyAuthenticatorRemoved( FidoAuthenticator* authenticator) { DCHECK_NE(state_, State::kIdle); - if (!observer()) + if (!observer() || state_ != State::kRunning) return; observer()->AuthenticatorRemoved(this, authenticator); } diff --git a/chromium/device/fido/fido_device_discovery_unittest.cc b/chromium/device/fido/fido_device_discovery_unittest.cc index 9e8a1aa8d9f..6b1dd0eb19f 100644 --- a/chromium/device/fido/fido_device_discovery_unittest.cc +++ b/chromium/device/fido/fido_device_discovery_unittest.cc @@ -76,7 +76,8 @@ TEST(FidoDiscoveryTest, TestNotificationsOnSuccessfulStart) { EXPECT_FALSE(discovery.is_running()); ::testing::Mock::VerifyAndClearExpectations(&discovery); - EXPECT_CALL(observer, DiscoveryStarted(&discovery, true)); + EXPECT_CALL(observer, DiscoveryStarted(&discovery, true, + std::vector<FidoAuthenticator*>())); discovery.NotifyDiscoveryStarted(true); EXPECT_TRUE(discovery.is_start_requested()); EXPECT_TRUE(discovery.is_running()); @@ -93,7 +94,8 @@ TEST(FidoDiscoveryTest, TestNotificationsOnFailedStart) { discovery.Start(); task_environment_.RunUntilIdle(); - EXPECT_CALL(observer, DiscoveryStarted(&discovery, false)); + EXPECT_CALL(observer, DiscoveryStarted(&discovery, false, + std::vector<FidoAuthenticator*>())); discovery.NotifyDiscoveryStarted(false); EXPECT_TRUE(discovery.is_start_requested()); EXPECT_FALSE(discovery.is_running()); @@ -112,19 +114,23 @@ TEST(FidoDiscoveryTest, TestAddRemoveDevices) { auto* device0_raw = device0.get(); FidoAuthenticator* authenticator0 = nullptr; base::RunLoop device0_done; - EXPECT_CALL(observer, AuthenticatorAdded(&discovery, _)) - .WillOnce(DoAll(SaveArg<1>(&authenticator0), - testing::InvokeWithoutArgs( - [&device0_done]() { device0_done.Quit(); }))); + EXPECT_CALL(observer, DiscoveryStarted(&discovery, true, _)) + .WillOnce(testing::Invoke( + [&](auto* discovery, bool success, auto authenticators) { + EXPECT_EQ(1u, authenticators.size()); + authenticator0 = authenticators[0]; + device0_done.Quit(); + })); EXPECT_CALL(*device0, GetId()).WillRepeatedly(Return("device0")); EXPECT_TRUE(discovery.AddDevice(std::move(device0))); + discovery.NotifyDiscoveryStarted(true); EXPECT_EQ("device0", authenticator0->GetId()); EXPECT_EQ(device0_raw, static_cast<FidoDeviceAuthenticator*>(authenticator0)->device()); device0_done.Run(); ::testing::Mock::VerifyAndClearExpectations(&observer); - // Expect successful insertion. + // Expect successful insertion after starting. base::RunLoop device1_done; auto device1 = std::make_unique<MockFidoDevice>(); auto* device1_raw = device1.get(); diff --git a/chromium/device/fido/fido_discovery_base.h b/chromium/device/fido/fido_discovery_base.h index 6fd43084880..4df9ef73b58 100644 --- a/chromium/device/fido/fido_discovery_base.h +++ b/chromium/device/fido/fido_discovery_base.h @@ -5,6 +5,8 @@ #ifndef DEVICE_FIDO_FIDO_DISCOVERY_BASE_H_ #define DEVICE_FIDO_FIDO_DISCOVERY_BASE_H_ +#include <vector> + #include "base/component_export.h" #include "base/logging.h" #include "base/macros.h" @@ -23,13 +25,14 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDiscoveryBase { virtual ~Observer(); // It is guaranteed that this is never invoked synchronously from Start(). - virtual void DiscoveryStarted(FidoDiscoveryBase* discovery, bool success) {} - - // It is guaranteed that AuthenticatorAdded/AuthenticatorRemoved() will not - // be invoked before the client of FidoDiscoveryBase calls - // FidoDiscoveryBase::Start(). However, for authenticators already known to - // the system at that point, AuthenticatorAdded() might already be called to - // reported already known devices. + // |authenticators| is the list of authenticators discovered upon start. + virtual void DiscoveryStarted( + FidoDiscoveryBase* discovery, + bool success, + std::vector<FidoAuthenticator*> authenticators = {}) {} + + // Called after DiscoveryStarted for any devices discovered after + // initialization. virtual void AuthenticatorAdded(FidoDiscoveryBase* discovery, FidoAuthenticator* authenticator) = 0; virtual void AuthenticatorRemoved(FidoDiscoveryBase* discovery, diff --git a/chromium/device/fido/fido_discovery_factory.cc b/chromium/device/fido/fido_discovery_factory.cc index 73a58b857d5..05d5f211b17 100644 --- a/chromium/device/fido/fido_discovery_factory.cc +++ b/chromium/device/fido/fido_discovery_factory.cc @@ -46,6 +46,10 @@ std::unique_ptr<FidoDiscoveryBase> CreateUsbFidoDiscovery( FidoDiscoveryFactory::FidoDiscoveryFactory() = default; FidoDiscoveryFactory::~FidoDiscoveryFactory() = default; +void FidoDiscoveryFactory::ResetRequestState() { + request_state_ = {}; +} + std::unique_ptr<FidoDiscoveryBase> FidoDiscoveryFactory::Create( FidoTransportProtocol transport, service_manager::Connector* connector) { @@ -55,10 +59,13 @@ std::unique_ptr<FidoDiscoveryBase> FidoDiscoveryFactory::Create( case FidoTransportProtocol::kBluetoothLowEnergy: return std::make_unique<FidoBleDiscovery>(); case FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy: - if (cable_data_.has_value() || qr_generator_key_.has_value()) { + if (request_state_.cable_data_.has_value() || + request_state_.qr_generator_key_.has_value()) { return std::make_unique<FidoCableDiscovery>( - cable_data_.value_or(std::vector<CableDiscoveryData>()), - qr_generator_key_); + request_state_.cable_data_.value_or( + std::vector<CableDiscoveryData>()), + request_state_.qr_generator_key_, + request_state_.cable_pairing_callback_); } return nullptr; case FidoTransportProtocol::kNearFieldCommunication: @@ -81,26 +88,40 @@ std::unique_ptr<FidoDiscoveryBase> FidoDiscoveryFactory::Create( void FidoDiscoveryFactory::set_cable_data( std::vector<CableDiscoveryData> cable_data, base::Optional<QRGeneratorKey> qr_generator_key) { - cable_data_ = std::move(cable_data); - qr_generator_key_ = std::move(qr_generator_key); + request_state_.cable_data_ = std::move(cable_data); + request_state_.qr_generator_key_ = std::move(qr_generator_key); +} + +void FidoDiscoveryFactory::set_cable_pairing_callback( + base::RepeatingCallback<void(std::unique_ptr<CableDiscoveryData>)> + pairing_callback) { + request_state_.cable_pairing_callback_.emplace(std::move(pairing_callback)); } #if defined(OS_WIN) +void FidoDiscoveryFactory::set_win_webauthn_api(WinWebAuthnApi* api) { + win_webauthn_api_ = api; +} + +WinWebAuthnApi* FidoDiscoveryFactory::win_webauthn_api() const { + return win_webauthn_api_; +} + std::unique_ptr<FidoDiscoveryBase> FidoDiscoveryFactory::MaybeCreateWinWebAuthnApiDiscovery() { - if (!base::FeatureList::IsEnabled(device::kWebAuthUseNativeWinApi) || - !WinWebAuthnApi::GetDefault()->IsAvailable()) { - return nullptr; - } - return std::make_unique<WinWebAuthnApiAuthenticatorDiscovery>( - // TODO(martinkr): Inject the window from which the request - // originated. Windows uses this parameter to center the - // dialog over the parent. The dialog should be centered - // over the originating Chrome Window; the foreground window - // may have changed to something else since the request was - // issued. - GetForegroundWindow()); + // TODO(martinkr): Inject the window from which the request originated. + // Windows uses this parameter to center the dialog over the parent. The + // dialog should be centered over the originating Chrome Window; the + // foreground window may have changed to something else since the request + // was issued. + return win_webauthn_api_ && win_webauthn_api_->IsAvailable() + ? std::make_unique<WinWebAuthnApiAuthenticatorDiscovery>( + GetForegroundWindow(), win_webauthn_api_) + : nullptr; } #endif // defined(OS_WIN) +FidoDiscoveryFactory::RequestState::RequestState() = default; +FidoDiscoveryFactory::RequestState::~RequestState() = default; + } // namespace device diff --git a/chromium/device/fido/fido_discovery_factory.h b/chromium/device/fido/fido_discovery_factory.h index 0b04b7cff04..63133e7dc2b 100644 --- a/chromium/device/fido/fido_discovery_factory.h +++ b/chromium/device/fido/fido_discovery_factory.h @@ -27,6 +27,10 @@ class Connector; namespace device { +#if defined(OS_WIN) +class WinWebAuthnApi; +#endif // defined(OS_WIN) + // FidoDiscoveryFactory offers methods to construct instances of // FidoDiscoveryBase for a given |transport| protocol. class COMPONENT_EXPORT(DEVICE_FIDO) FidoDiscoveryFactory { @@ -34,6 +38,18 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDiscoveryFactory { FidoDiscoveryFactory(); virtual ~FidoDiscoveryFactory(); + // Resets all fields that are only meaningful for the duration of a single + // request to a safe default. + // + // The "regular" FidoDiscoveryFactory is owned by the + // AuthenticatorClientRequestDelegate and lives only for a single request. + // Instances returned from + // AuthenticatorEnvironmentImpl::GetDiscoveryFactoryOverride(), which are + // used in unit tests or by the WebDriver virtual authenticators, are + // long-lived and may handle multiple WebAuthn requests. Hence, they will be + // reset at the beginning of each new request. + void ResetRequestState(); + // Instantiates a FidoDiscoveryBase for the given transport. // // FidoTransportProtocol::kUsbHumanInterfaceDevice requires specifying a @@ -46,6 +62,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDiscoveryFactory { void set_cable_data(std::vector<CableDiscoveryData> cable_data, base::Optional<QRGeneratorKey> qr_generator_key); + // set_cable_pairing_callback installs a repeating callback that will be + // called when a QR handshake results in a phone wishing to pair with this + // browser. + void set_cable_pairing_callback( + base::RepeatingCallback<void(std::unique_ptr<CableDiscoveryData>)>); + #if defined(OS_MACOSX) // Configures the Touch ID authenticator. Set to base::nullopt to disable it. void set_mac_touch_id_info( @@ -55,17 +77,37 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDiscoveryFactory { #endif // defined(OS_MACOSX) #if defined(OS_WIN) - // Instantiates a FidoDiscovery for the native Windows WebAuthn - // API where available. Returns nullptr otherwise. + // Instantiates a FidoDiscovery for the native Windows WebAuthn API where + // available. Returns nullptr otherwise. std::unique_ptr<FidoDiscoveryBase> MaybeCreateWinWebAuthnApiDiscovery(); + + // Sets the WinWebAuthnApi instance to be used for creating the discovery for + // the Windows authenticator. If none is set, + // MaybeCreateWinWebAuthnApiDiscovery() returns nullptr. + void set_win_webauthn_api(WinWebAuthnApi* api); + WinWebAuthnApi* win_webauthn_api() const; #endif // defined(OS_WIN) private: + // RequestState holds configuration data that is only meaningful for a + // single WebAuthn request. + struct RequestState { + RequestState(); + ~RequestState(); + base::Optional<std::vector<CableDiscoveryData>> cable_data_; + base::Optional<QRGeneratorKey> qr_generator_key_; + base::Optional< + base::RepeatingCallback<void(std::unique_ptr<CableDiscoveryData>)>> + cable_pairing_callback_; + }; + + RequestState request_state_; #if defined(OS_MACOSX) base::Optional<fido::mac::AuthenticatorConfig> mac_touch_id_config_; #endif // defined(OS_MACOSX) - base::Optional<std::vector<CableDiscoveryData>> cable_data_; - base::Optional<QRGeneratorKey> qr_generator_key_; +#if defined(OS_WIN) + WinWebAuthnApi* win_webauthn_api_ = nullptr; +#endif // defined(OS_WIN) }; } // namespace device diff --git a/chromium/device/fido/fido_request_handler_base.cc b/chromium/device/fido/fido_request_handler_base.cc index b3322aca79f..e6216746eb9 100644 --- a/chromium/device/fido/fido_request_handler_base.cc +++ b/chromium/device/fido/fido_request_handler_base.cc @@ -48,41 +48,22 @@ FidoRequestHandlerBase::Observer::~Observer() = default; FidoRequestHandlerBase::FidoRequestHandlerBase( service_manager::Connector* connector, FidoDiscoveryFactory* fido_discovery_factory, - const base::flat_set<FidoTransportProtocol>& available_transports) - : fido_discovery_factory_(fido_discovery_factory), connector_(connector) { + const base::flat_set<FidoTransportProtocol>& available_transports) { #if defined(OS_WIN) - InitDiscoveriesWin(available_transports); + InitDiscoveriesWin(fido_discovery_factory, connector, available_transports); #else - InitDiscoveries(available_transports); + InitDiscoveries(fido_discovery_factory, connector, available_transports); #endif // !defined(OS_WIN) } void FidoRequestHandlerBase::InitDiscoveries( + FidoDiscoveryFactory* fido_discovery_factory, + service_manager::Connector* connector, const base::flat_set<FidoTransportProtocol>& available_transports) { - // The number of times |notify_observer_callback_| needs to be invoked before - // Observer::OnTransportAvailabilityEnumerated is dispatched. Essentially this - // is used to wait until the parts |transport_availability_info_| are - // filled out; the |notify_observer_callback_| is invoked once for each part - // once that part is ready, namely: - // - // 1) [If the platform authenticator is enabled] once the platform - // authenticator is ready. - // 2) [If BLE or caBLE are enabled] if Bluetooth adapter is present. - // - // On top of that, we wait for (3) an invocation that happens when the - // |observer_| is set, so that OnTransportAvailabilityEnumerated is never - // called before the observer is set. - size_t transport_info_callback_count = 1u; - -#if defined(OS_WIN) - if (transport_availability_info_.has_win_native_api_authenticator) - ++transport_info_callback_count; -#endif // defined(OS_WIN) - transport_availability_info_.available_transports = available_transports; for (const auto transport : available_transports) { std::unique_ptr<FidoDiscoveryBase> discovery = - fido_discovery_factory_->Create(transport, connector_); + fido_discovery_factory->Create(transport, connector); if (discovery == nullptr) { // This can occur in tests when a ScopedVirtualU2fDevice is in effect and // HID transports are not configured or when caBLE discovery data isn't @@ -91,27 +72,35 @@ void FidoRequestHandlerBase::InitDiscoveries( continue; } - if (transport == FidoTransportProtocol::kInternal) { - ++transport_info_callback_count; - } - discovery->set_observer(this); discoveries_.push_back(std::move(discovery)); } + bool has_ble = false; if (base::Contains(transport_availability_info_.available_transports, FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy) || base::Contains(transport_availability_info_.available_transports, FidoTransportProtocol::kBluetoothLowEnergy)) { - ++transport_info_callback_count; + has_ble = true; base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&FidoRequestHandlerBase::ConstructBleAdapterPowerManager, weak_factory_.GetWeakPtr())); } + // Initialize |notify_observer_callback_| with the number of times it has to + // be invoked before Observer::OnTransportAvailabilityEnumerated is + // dispatched. + // Essentially this is used to wait until the parts + // |transport_availability_info_| are filled out; the + // |notify_observer_callback_| is invoked once for each discovery once it is + // ready, and additionally: + // + // 1) [If BLE or caBLE are enabled] once BLE adapters have been enumerated + // 2) When |observer_| is set, so that OnTransportAvailabilityEnumerated is + // never called before it is set. notify_observer_callback_ = base::BarrierClosure( - transport_info_callback_count, + discoveries_.size() + has_ble + 1, base::BindOnce( &FidoRequestHandlerBase::NotifyObserverTransportAvailability, weak_factory_.GetWeakPtr())); @@ -119,14 +108,15 @@ void FidoRequestHandlerBase::InitDiscoveries( #if defined(OS_WIN) void FidoRequestHandlerBase::InitDiscoveriesWin( + FidoDiscoveryFactory* fido_discovery_factory, + service_manager::Connector* connector, const base::flat_set<FidoTransportProtocol>& available_transports) { // Try to instantiate the discovery for proxying requests to the native // Windows WebAuthn API; or fall back to using the regular device transport // discoveries if the API is unavailable. - auto discovery = - fido_discovery_factory_->MaybeCreateWinWebAuthnApiDiscovery(); + auto discovery = fido_discovery_factory->MaybeCreateWinWebAuthnApiDiscovery(); if (!discovery) { - InitDiscoveries(available_transports); + InitDiscoveries(fido_discovery_factory, connector, available_transports); return; } @@ -154,7 +144,7 @@ void FidoRequestHandlerBase::InitDiscoveriesWin( FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy}; } - InitDiscoveries(other_transports); + InitDiscoveries(fido_discovery_factory, connector, other_transports); } #endif // defined(OS_WIN) @@ -243,13 +233,6 @@ void FidoRequestHandlerBase::Start() { discovery->Start(); } -void FidoRequestHandlerBase::AuthenticatorAdded( - FidoDiscoveryBase* discovery, - FidoAuthenticator* authenticator) { - DCHECK(!base::Contains(active_authenticators(), authenticator->GetId())); - AddAuthenticator(authenticator); -} - void FidoRequestHandlerBase::AuthenticatorRemoved( FidoDiscoveryBase* discovery, FidoAuthenticator* authenticator) { @@ -296,7 +279,19 @@ void FidoRequestHandlerBase::AuthenticatorPairingModeChanged( } } -void FidoRequestHandlerBase::AddAuthenticator( +void FidoRequestHandlerBase::DiscoveryStarted( + FidoDiscoveryBase* discovery, + bool success, + std::vector<FidoAuthenticator*> authenticators) { + for (auto* authenticator : authenticators) { + AuthenticatorAdded(discovery, authenticator); + } + DCHECK(notify_observer_callback_); + notify_observer_callback_.Run(); +} + +void FidoRequestHandlerBase::AuthenticatorAdded( + FidoDiscoveryBase* discovery, FidoAuthenticator* authenticator) { DCHECK(authenticator && !base::Contains(active_authenticators(), authenticator->GetId())); @@ -336,16 +331,8 @@ void FidoRequestHandlerBase::AddAuthenticator( .win_native_ui_shows_resident_credential_notice = static_cast<WinWebAuthnApiAuthenticator*>(authenticator) ->ShowsPrivacyNotice(); - DCHECK(notify_observer_callback_); - notify_observer_callback_.Run(); } #endif // defined(OS_WIN) - - if (authenticator->AuthenticatorTransport() == - FidoTransportProtocol::kInternal) { - DCHECK(notify_observer_callback_); - notify_observer_callback_.Run(); - } } bool FidoRequestHandlerBase::HasAuthenticator( diff --git a/chromium/device/fido/fido_request_handler_base.h b/chromium/device/fido/fido_request_handler_base.h index bfaddf9ef33..936ece7c9d6 100644 --- a/chromium/device/fido/fido_request_handler_base.h +++ b/chromium/device/fido/fido_request_handler_base.h @@ -235,6 +235,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase Observer* observer() const { return observer_; } // FidoDiscoveryBase::Observer + void DiscoveryStarted( + FidoDiscoveryBase* discovery, + bool success, + std::vector<FidoAuthenticator*> authenticators) override; void AuthenticatorAdded(FidoDiscoveryBase* discovery, FidoAuthenticator* authenticator) override; void AuthenticatorRemoved(FidoDiscoveryBase* discovery, @@ -246,19 +250,20 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase const std::string& device_id, bool is_in_pairing_mode) override; - FidoDiscoveryFactory* fido_discovery_factory_; - private: friend class FidoRequestHandlerTest; void InitDiscoveries( + FidoDiscoveryFactory* fido_discovery_factory, + service_manager::Connector* connector, const base::flat_set<FidoTransportProtocol>& available_transports); #if defined(OS_WIN) void InitDiscoveriesWin( + FidoDiscoveryFactory* fido_discovery_factory, + service_manager::Connector* connector, const base::flat_set<FidoTransportProtocol>& available_transports); #endif - void AddAuthenticator(FidoAuthenticator* authenticator); void NotifyObserverTransportAvailability(); // Invokes FidoAuthenticator::InitializeAuthenticator(), followed by @@ -274,7 +279,6 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase TransportAvailabilityInfo transport_availability_info_; base::RepeatingClosure notify_observer_callback_; std::unique_ptr<BleAdapterManager> bluetooth_adapter_manager_; - service_manager::Connector* const connector_; base::WeakPtrFactory<FidoRequestHandlerBase> weak_factory_{this}; 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 96f50f0f9e1..66711141096 100644 --- a/chromium/device/fido/fido_request_handler_unittest.cc +++ b/chromium/device/fido/fido_request_handler_unittest.cc @@ -367,11 +367,6 @@ class FidoRequestHandlerTest : public ::testing::Test { test::FakeFidoDiscovery* discovery_; test::FakeFidoDiscovery* ble_discovery_; FakeHandlerCallbackReceiver cb_; - -#if defined(OS_WIN) - device::ScopedFakeWinWebAuthnApi win_webauthn_api_ = - device::ScopedFakeWinWebAuthnApi::MakeUnavailable(); -#endif // defined(OS_WIN) }; TEST_F(FidoRequestHandlerTest, TestSingleDeviceSuccess) { @@ -581,6 +576,8 @@ TEST_F(FidoRequestHandlerTest, discovery()->AddDevice(std::move(device0)); platform_discovery->AddDevice(std::move(device1)); + discovery()->WaitForCallToStartAndSimulateSuccess(); + platform_discovery->WaitForCallToStartAndSimulateSuccess(); task_environment_.FastForwardUntilNoTasksRemain(); callback().WaitForCallback(); @@ -626,7 +623,8 @@ TEST_F(FidoRequestHandlerTest, TestWithPlatformAuthenticator) { device->ExpectRequestAndRespondWith(std::vector<uint8_t>(), CreateFakeSuccessDeviceResponse()); device->SetDeviceTransport(FidoTransportProtocol::kInternal); - auto* fake_discovery = fake_discovery_factory_.ForgeNextPlatformDiscovery(); + auto* fake_discovery = fake_discovery_factory_.ForgeNextPlatformDiscovery( + test::FakeFidoDiscovery::StartMode::kAutomatic); TestObserver observer; auto request_handler = std::make_unique<FakeFidoRequestHandler>( @@ -660,6 +658,8 @@ TEST_F(FidoRequestHandlerTest, BleTransportAllowedIfBluetoothAdapterPresent) { TestObserver observer; auto request_handler = CreateFakeHandler(); + ble_discovery()->WaitForCallToStartAndSimulateSuccess(); + discovery()->WaitForCallToStartAndSimulateSuccess(); request_handler->set_observer(&observer); observer.WaitForAndExpectAvailableTransportsAre( @@ -673,6 +673,8 @@ TEST_F(FidoRequestHandlerTest, TestObserver observer; auto request_handler = CreateFakeHandler(); + ble_discovery()->WaitForCallToStartAndSimulateSuccess(); + discovery()->WaitForCallToStartAndSimulateSuccess(); request_handler->set_observer(&observer); observer.WaitForAndExpectAvailableTransportsAre( @@ -685,6 +687,8 @@ TEST_F(FidoRequestHandlerTest, TestObserver observer; auto request_handler = CreateFakeHandler(); + ble_discovery()->WaitForCallToStartAndSimulateSuccess(); + discovery()->WaitForCallToStartAndSimulateSuccess(); task_environment_.FastForwardUntilNoTasksRemain(); request_handler->set_observer(&observer); @@ -698,6 +702,7 @@ TEST_F(FidoRequestHandlerTest, EmbedderNotifiedWhenAuthenticatorIdChanges) { TestObserver observer; auto request_handler = CreateFakeHandler(); request_handler->set_observer(&observer); + discovery()->WaitForCallToStartAndSimulateSuccess(); ble_discovery()->WaitForCallToStartAndSimulateSuccess(); auto device = std::make_unique<MockFidoDevice>(); @@ -711,9 +716,11 @@ TEST_F(FidoRequestHandlerTest, EmbedderNotifiedWhenAuthenticatorIdChanges) { #if defined(OS_WIN) TEST_F(FidoRequestHandlerTest, TransportAvailabilityOfWindowsAuthenticator) { + FakeWinWebAuthnApi api; + fake_discovery_factory_.set_win_webauthn_api(&api); for (const bool api_available : {false, true}) { SCOPED_TRACE(::testing::Message() << "api_available=" << api_available); - win_webauthn_api_.set_available(api_available); + api.set_available(api_available); TestObserver observer; ForgeNextHidDiscovery(); @@ -721,6 +728,12 @@ TEST_F(FidoRequestHandlerTest, TransportAvailabilityOfWindowsAuthenticator) { {FidoTransportProtocol::kUsbHumanInterfaceDevice}, &fake_discovery_factory_); request_handler.set_observer(&observer); + + // If the windows API is not enabled, the request is dispatched to the USB + // discovery. Simulate a success to fill the transport availability info. + if (!api_available) + discovery()->WaitForCallToStartAndSimulateSuccess(); + task_environment_.FastForwardUntilNoTasksRemain(); auto transport_availability_info = diff --git a/chromium/device/fido/fido_test_data.h b/chromium/device/fido/fido_test_data.h index de691a551a6..1651004e490 100644 --- a/chromium/device/fido/fido_test_data.h +++ b/chromium/device/fido/fido_test_data.h @@ -163,6 +163,27 @@ constexpr uint8_t kU2fSignCommandApduWithKeyAlpha[] = { 0x00, 0x00, }; +constexpr uint8_t kU2fSignCommandApduWithKeyAlphaAndBogusChallenge[] = { + // CLA, INS, P1, P2 APDU instruction parameters + 0x00, 0x02, 0x03, 0x00, + // Data Length (3 bytes in big endian order) + 0x00, 0x00, 0x42, + // Bogus challenge parameter + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + // Application parameter + 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, + // Dummy key handle length + 0x01, + // Key handle + 0xEA, + // Max response length + 0x00, 0x00, +}; + constexpr uint8_t kU2fSignCommandApduWithKeyBeta[] = { // CLA, INS, P1, P2 APDU instruction parameters 0x00, 0x02, 0x03, 0x00, @@ -184,6 +205,27 @@ constexpr uint8_t kU2fSignCommandApduWithKeyBeta[] = { 0x00, 0x00, }; +constexpr uint8_t kU2fSignCommandApduWithKeyBetaAndBogusChallenge[] = { + // CLA, INS, P1, P2 APDU instruction parameters + 0x00, 0x02, 0x03, 0x00, + // Data Length (3 bytes in big endian order) + 0x00, 0x00, 0x42, + // Bogus challenge parameter + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + // Application parameter + 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, + // Dummy key handle length + 0x01, + // Key handle + 0xEB, + // Max response length + 0x00, 0x00, +}; + constexpr uint8_t kU2fSignCommandApduWithKeyGamma[] = { // CLA, INS, P1, P2 APDU instruction parameters 0x00, 0x02, 0x03, 0x00, @@ -205,6 +247,27 @@ constexpr uint8_t kU2fSignCommandApduWithKeyGamma[] = { 0x00, 0x00, }; +constexpr uint8_t kU2fSignCommandApduWithKeyGammaAndBogusChallenge[] = { + // CLA, INS, P1, P2 APDU instruction parameters + 0x00, 0x02, 0x03, 0x00, + // Data Length (3 bytes in big endian order) + 0x00, 0x00, 0x42, + // Bogus challenge parameter + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + // Application parameter + 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, + // Dummy key handle length + 0x01, + // Key handle + 0xEC, + // Max response length + 0x00, 0x00, +}; + constexpr uint8_t kU2fSignCommandApdu[] = { // CLA, INS, P1, P2 APDU instruction parameters 0x00, 0x02, 0x03, 0x00, diff --git a/chromium/device/fido/get_assertion_handler_unittest.cc b/chromium/device/fido/get_assertion_handler_unittest.cc index 9b2236c8690..95af34ed342 100644 --- a/chromium/device/fido/get_assertion_handler_unittest.cc +++ b/chromium/device/fido/get_assertion_handler_unittest.cc @@ -32,7 +32,6 @@ #include "testing/gtest/include/gtest/gtest.h" #if defined(OS_WIN) -#include "device/fido/win/authenticator.h" #include "device/fido/win/fake_webauthn_api.h" #endif // defined(OS_WIN) @@ -102,7 +101,7 @@ class FidoGetAssertionHandlerTest : public ::testing::Test { auto handler = std::make_unique<GetAssertionRequestHandler>( nullptr /* connector */, fake_discovery_factory_.get(), supported_transports_, std::move(request), - get_assertion_cb_.callback()); + /*allow_skipping_pin_touch=*/true, get_assertion_cb_.callback()); return handler; } @@ -177,11 +176,6 @@ class FidoGetAssertionHandlerTest : public ::testing::Test { TestGetAssertionRequestCallback get_assertion_cb_; base::flat_set<FidoTransportProtocol> supported_transports_ = GetAllTransportProtocols(); - -#if defined(OS_WIN) - device::ScopedFakeWinWebAuthnApi win_webauthn_api_ = - device::ScopedFakeWinWebAuthnApi::MakeUnavailable(); -#endif // defined(OS_WIN) }; TEST_F(FidoGetAssertionHandlerTest, TransportAvailabilityInfo) { @@ -742,10 +736,6 @@ TEST_F(FidoGetAssertionHandlerTest, DeviceFailsImmediately) { TEST(GetAssertionRequestHandlerTest, IncorrectTransportType) { base::test::TaskEnvironment task_environment{ base::test::TaskEnvironment::TimeSource::MOCK_TIME}; -#if defined(OS_WIN) - device::ScopedFakeWinWebAuthnApi win_webauthn_api_ = - device::ScopedFakeWinWebAuthnApi::MakeUnavailable(); -#endif // defined(OS_WIN) device::test::VirtualFidoDeviceFactory virtual_device_factory; virtual_device_factory.SetSupportedProtocol(device::ProtocolVersion::kCtap2); @@ -769,7 +759,7 @@ TEST(GetAssertionRequestHandlerTest, IncorrectTransportType) { nullptr /* connector */, &virtual_device_factory, base::flat_set<FidoTransportProtocol>( {FidoTransportProtocol::kUsbHumanInterfaceDevice}), - std::move(request), cb.callback()); + std::move(request), /*allow_skipping_pin_touch=*/true, cb.callback()); task_environment.FastForwardUntilNoTasksRemain(); EXPECT_FALSE(cb.was_called()); @@ -821,10 +811,10 @@ class TestObserver : public FidoRequestHandlerBase::Observer { // on API availability. TEST(GetAssertionRequestHandlerWinTest, TestWinUsbDiscovery) { base::test::TaskEnvironment task_environment; - ScopedFakeWinWebAuthnApi scoped_fake_win_webauthn_api; for (const bool enable_api : {false, true}) { SCOPED_TRACE(::testing::Message() << "enable_api=" << enable_api); - scoped_fake_win_webauthn_api.set_available(enable_api); + FakeWinWebAuthnApi api; + api.set_available(enable_api); // Simulate a connected HID device. ScopedFakeFidoHidManager fake_hid_manager; @@ -832,13 +822,14 @@ TEST(GetAssertionRequestHandlerWinTest, TestWinUsbDiscovery) { TestGetAssertionRequestCallback cb; FidoDiscoveryFactory fido_discovery_factory; + fido_discovery_factory.set_win_webauthn_api(&api); auto handler = std::make_unique<GetAssertionRequestHandler>( fake_hid_manager.service_manager_connector(), &fido_discovery_factory, base::flat_set<FidoTransportProtocol>( {FidoTransportProtocol::kUsbHumanInterfaceDevice}), CtapGetAssertionRequest(test_data::kRelyingPartyId, test_data::kClientDataJson), - cb.callback()); + /*allow_skipping_pin_touch=*/true, cb.callback()); // Register an observer that disables automatic dispatch. Dispatch to the // (unimplemented) fake Windows API would immediately result in an invalid // response. diff --git a/chromium/device/fido/get_assertion_request_handler.cc b/chromium/device/fido/get_assertion_request_handler.cc index df16a9457f6..ba547443d48 100644 --- a/chromium/device/fido/get_assertion_request_handler.cc +++ b/chromium/device/fido/get_assertion_request_handler.cc @@ -14,6 +14,7 @@ #include "base/metrics/histogram_functions.h" #include "base/stl_util.h" #include "build/build_config.h" +#include "components/cbor/diagnostic_writer.h" #include "components/device_event_log/device_event_log.h" #include "device/fido/cable/fido_cable_discovery.h" #include "device/fido/fido_authenticator.h" @@ -151,6 +152,16 @@ bool ResponseValid(const FidoAuthenticator& authenticator, return false; } + // No extensions are supported when getting assertions therefore no extensions + // are permitted in the response. + const base::Optional<cbor::Value>& extensions = + response.auth_data().extensions(); + if (extensions) { + FIDO_LOG(ERROR) << "assertion response invalid due to extensions block: " + << cbor::DiagnosticWriter::Write(*extensions); + return false; + } + return true; } @@ -213,6 +224,7 @@ GetAssertionRequestHandler::GetAssertionRequestHandler( FidoDiscoveryFactory* fido_discovery_factory, const base::flat_set<FidoTransportProtocol>& supported_transports, CtapGetAssertionRequest request, + bool allow_skipping_pin_touch, CompletionCallback completion_callback) : FidoRequestHandlerBase( connector, @@ -221,7 +233,8 @@ GetAssertionRequestHandler::GetAssertionRequestHandler( supported_transports, GetTransportsAllowedByRP(request))), completion_callback_(std::move(completion_callback)), - request_(std::move(request)) { + request_(std::move(request)), + allow_skipping_pin_touch_(allow_skipping_pin_touch) { transport_availability_info().request_type = FidoRequestHandlerBase::RequestType::kGetAssertion; transport_availability_info().has_empty_allow_list = @@ -251,6 +264,11 @@ void GetAssertionRequestHandler::DispatchRequest( switch (authenticator->WillNeedPINToGetAssertion(request_, observer())) { case FidoAuthenticator::GetAssertionPINDisposition::kUsePIN: + // Skip asking for touch if this is the only available authenticator. + if (active_authenticators().size() == 1 && allow_skipping_pin_touch_) { + HandleTouch(authenticator); + return; + } // A PIN will be needed. Just request a touch to let the user select // this authenticator if they wish. FIDO_LOG(DEBUG) << "Asking for touch from " @@ -303,6 +321,9 @@ void GetAssertionRequestHandler::AuthenticatorAdded( DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_); #if defined(OS_MACOSX) + // Indicate to the UI whether a GetAssertion call to Touch ID would succeed + // or not. This needs to happen before the base AuthenticatorAdded() + // implementation runs |notify_observer_callback_| for this callback. if (authenticator->IsTouchIdAuthenticator()) { transport_availability_info().has_recognized_mac_touch_id_credential = static_cast<fido::mac::TouchIdAuthenticator*>(authenticator) @@ -537,7 +558,6 @@ void GetAssertionRequestHandler::OnHavePIN(std::string pin) { if (authenticator_ == nullptr) { // Authenticator was detached. The request will already have been canceled // but this callback may have been waiting in a queue. - DCHECK(!completion_callback_); return; } diff --git a/chromium/device/fido/get_assertion_request_handler.h b/chromium/device/fido/get_assertion_request_handler.h index f112431f624..d1aa1f7da20 100644 --- a/chromium/device/fido/get_assertion_request_handler.h +++ b/chromium/device/fido/get_assertion_request_handler.h @@ -64,6 +64,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) GetAssertionRequestHandler FidoDiscoveryFactory* fido_discovery_factory, const base::flat_set<FidoTransportProtocol>& supported_transports, CtapGetAssertionRequest request_parameter, + bool allow_skipping_pin_touch, CompletionCallback completion_callback); ~GetAssertionRequestHandler() override; @@ -108,6 +109,11 @@ class COMPONENT_EXPORT(DEVICE_FIDO) GetAssertionRequestHandler CompletionCallback completion_callback_; State state_ = State::kWaitingForTouch; CtapGetAssertionRequest request_; + // If true, and if at the time the request is dispatched to the first + // authenticator no other authenticators are available, the request handler + // will skip the initial touch that is usually required to select a PIN + // protected authenticator. + bool allow_skipping_pin_touch_; // authenticator_ points to the authenticator that will be used for this // operation. It's only set after the user touches an authenticator to select // it, after which point that authenticator will be used exclusively through diff --git a/chromium/device/fido/hid/fake_hid_impl_for_testing.cc b/chromium/device/fido/hid/fake_hid_impl_for_testing.cc index c51c9c2f853..a81999c5620 100644 --- a/chromium/device/fido/hid/fake_hid_impl_for_testing.cc +++ b/chromium/device/fido/hid/fake_hid_impl_for_testing.cc @@ -25,9 +25,9 @@ MATCHER_P(IsCtapHidCommand, expected_command, "") { MockFidoHidConnection::MockFidoHidConnection( device::mojom::HidDeviceInfoPtr device, - device::mojom::HidConnectionRequest request, + mojo::PendingReceiver<device::mojom::HidConnection> receiver, std::array<uint8_t, 4> connection_channel_id) - : binding_(this, std::move(request)), + : receiver_(this, std::move(receiver)), device_(std::move(device)), connection_channel_id_(connection_channel_id) {} @@ -127,13 +127,14 @@ FakeFidoHidManager::FakeFidoHidManager() = default; FakeFidoHidManager::~FakeFidoHidManager() = default; -void FakeFidoHidManager::AddBinding(mojo::ScopedMessagePipeHandle handle) { - bindings_.AddBinding(this, - device::mojom::HidManagerRequest(std::move(handle))); +void FakeFidoHidManager::AddReceiver(mojo::ScopedMessagePipeHandle handle) { + receivers_.Add(this, mojo::PendingReceiver<device::mojom::HidManager>( + std::move(handle))); } -void FakeFidoHidManager::AddBinding2(device::mojom::HidManagerRequest request) { - bindings_.AddBinding(this, std::move(request)); +void FakeFidoHidManager::AddReceiver2( + mojo::PendingReceiver<device::mojom::HidManager> receiver) { + receivers_.Add(this, std::move(receiver)); } void FakeFidoHidManager::AddFidoHidDevice(std::string guid) { @@ -155,9 +156,7 @@ void FakeFidoHidManager::GetDevicesAndSetClient( GetDevicesCallback callback) { GetDevices(std::move(callback)); - device::mojom::HidManagerClientAssociatedPtr client_ptr; - client_ptr.Bind(std::move(client)); - clients_.AddPtr(std::move(client_ptr)); + clients_.Add(std::move(client)); } void FakeFidoHidManager::GetDevices(GetDevicesCallback callback) { @@ -175,7 +174,7 @@ void FakeFidoHidManager::Connect( auto device_it = devices_.find(device_guid); auto connection_it = connections_.find(device_guid); if (device_it == devices_.end() || connection_it == connections_.end()) { - std::move(callback).Run(nullptr); + std::move(callback).Run(mojo::NullRemote()); return; } @@ -184,16 +183,15 @@ void FakeFidoHidManager::Connect( void FakeFidoHidManager::AddDevice(device::mojom::HidDeviceInfoPtr device) { device::mojom::HidDeviceInfo* device_info = device.get(); - clients_.ForAllPtrs([device_info](device::mojom::HidManagerClient* client) { + for (auto& client : clients_) client->DeviceAdded(device_info->Clone()); - }); devices_[device->guid] = std::move(device); } void FakeFidoHidManager::AddDeviceAndSetConnection( device::mojom::HidDeviceInfoPtr device, - device::mojom::HidConnectionPtr connection) { + mojo::PendingRemote<device::mojom::HidConnection> connection) { connections_[device->guid] = std::move(connection); AddDevice(std::move(device)); } @@ -204,9 +202,8 @@ void FakeFidoHidManager::RemoveDevice(const std::string device_guid) { return; device::mojom::HidDeviceInfo* device_info = it->second.get(); - clients_.ForAllPtrs([device_info](device::mojom::HidManagerClient* client) { + for (auto& client : clients_) client->DeviceRemoved(device_info->Clone()); - }); devices_.erase(it); } @@ -216,7 +213,7 @@ ScopedFakeFidoHidManager::ScopedFakeFidoHidManager() { connector_->OverrideBinderForTesting( service_manager::ServiceFilter::ByName(device::mojom::kServiceName), device::mojom::HidManager::Name_, - base::BindRepeating(&FakeFidoHidManager::AddBinding, + base::BindRepeating(&FakeFidoHidManager::AddReceiver, base::Unretained(this))); } diff --git a/chromium/device/fido/hid/fake_hid_impl_for_testing.h b/chromium/device/fido/hid/fake_hid_impl_for_testing.h index 597cd5d357e..d72f4c3e200 100644 --- a/chromium/device/fido/hid/fake_hid_impl_for_testing.h +++ b/chromium/device/fido/hid/fake_hid_impl_for_testing.h @@ -14,9 +14,11 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" #include "device/fido/fido_constants.h" -#include "mojo/public/cpp/bindings/binding_set.h" -#include "mojo/public/cpp/bindings/interface_ptr_set.h" -#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/bindings/remote_set.h" #include "services/device/public/mojom/hid.mojom.h" #include "testing/gmock/include/gmock/gmock.h" @@ -28,9 +30,10 @@ namespace device { class MockFidoHidConnection : public device::mojom::HidConnection { public: - explicit MockFidoHidConnection(device::mojom::HidDeviceInfoPtr device, - device::mojom::HidConnectionRequest request, - std::array<uint8_t, 4> connection_channel_id); + explicit MockFidoHidConnection( + device::mojom::HidDeviceInfoPtr device, + mojo::PendingReceiver<device::mojom::HidConnection> receiver, + std::array<uint8_t, 4> connection_channel_id); ~MockFidoHidConnection() override; MOCK_METHOD1(ReadPtr, void(ReadCallback* callback)); @@ -61,7 +64,7 @@ class MockFidoHidConnection : public device::mojom::HidConnection { const std::vector<uint8_t>& nonce() const { return nonce_; } private: - mojo::Binding<device::mojom::HidConnection> binding_; + mojo::Receiver<device::mojom::HidConnection> receiver_; device::mojom::HidDeviceInfoPtr device_; std::vector<uint8_t> nonce_; std::array<uint8_t, 4> connection_channel_id_; @@ -111,18 +114,20 @@ class FakeFidoHidManager : public device::mojom::HidManager { const std::string& device_guid, mojo::PendingRemote<mojom::HidConnectionClient> connection_client, ConnectCallback callback) override; - void AddBinding(mojo::ScopedMessagePipeHandle handle); - void AddBinding2(device::mojom::HidManagerRequest request); + void AddReceiver(mojo::ScopedMessagePipeHandle handle); + void AddReceiver2(mojo::PendingReceiver<device::mojom::HidManager> receiver); void AddDevice(device::mojom::HidDeviceInfoPtr device); - void AddDeviceAndSetConnection(device::mojom::HidDeviceInfoPtr device, - device::mojom::HidConnectionPtr connection); + void AddDeviceAndSetConnection( + device::mojom::HidDeviceInfoPtr device, + mojo::PendingRemote<device::mojom::HidConnection> connection); void RemoveDevice(const std::string device_guid); private: std::map<std::string, device::mojom::HidDeviceInfoPtr> devices_; - std::map<std::string, device::mojom::HidConnectionPtr> connections_; - mojo::AssociatedInterfacePtrSet<device::mojom::HidManagerClient> clients_; - mojo::BindingSet<device::mojom::HidManager> bindings_; + std::map<std::string, mojo::PendingRemote<device::mojom::HidConnection>> + connections_; + mojo::AssociatedRemoteSet<device::mojom::HidManagerClient> clients_; + mojo::ReceiverSet<device::mojom::HidManager> receivers_; DISALLOW_COPY_AND_ASSIGN(FakeFidoHidManager); }; diff --git a/chromium/device/fido/hid/fido_hid_device.cc b/chromium/device/fido/hid/fido_hid_device.cc index a90c2a79a84..d581712ef01 100644 --- a/chromium/device/fido/hid/fido_hid_device.cc +++ b/chromium/device/fido/hid/fido_hid_device.cc @@ -181,7 +181,8 @@ void FidoHidDevice::Connect( std::move(callback)); } -void FidoHidDevice::OnConnect(device::mojom::HidConnectionPtr connection) { +void FidoHidDevice::OnConnect( + mojo::PendingRemote<device::mojom::HidConnection> connection) { timeout_callback_.Cancel(); if (!connection) { @@ -189,7 +190,7 @@ void FidoHidDevice::OnConnect(device::mojom::HidConnectionPtr connection) { return; } - connection_ = std::move(connection); + connection_.Bind(std::move(connection)); // Send random nonce to device to verify received message. std::vector<uint8_t> nonce(8); crypto::RandBytes(nonce.data(), nonce.size()); diff --git a/chromium/device/fido/hid/fido_hid_device.h b/chromium/device/fido/hid/fido_hid_device.h index 8c4bc8c5b7f..c36efd70e28 100644 --- a/chromium/device/fido/hid/fido_hid_device.h +++ b/chromium/device/fido/hid/fido_hid_device.h @@ -19,6 +19,8 @@ #include "components/apdu/apdu_command.h" #include "components/apdu/apdu_response.h" #include "device/fido/fido_device.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/remote.h" #include "services/device/public/mojom/hid.mojom.h" namespace device { @@ -92,7 +94,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoHidDevice : public FidoDevice { // Open a connection to this device. void Connect(device::mojom::HidManager::ConnectCallback callback); - void OnConnect(device::mojom::HidConnectionPtr connection); + void OnConnect(mojo::PendingRemote<device::mojom::HidConnection> connection); void OnInitWriteComplete(std::vector<uint8_t> nonce, bool success); // Ask device to allocate a unique channel id for this connection. void OnAllocateChannel(std::vector<uint8_t> nonce, @@ -145,7 +147,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoHidDevice : public FidoDevice { // U2fRequest. device::mojom::HidManager* hid_manager_; device::mojom::HidDeviceInfoPtr device_info_; - device::mojom::HidConnectionPtr connection_; + mojo::Remote<device::mojom::HidConnection> connection_; base::WeakPtrFactory<FidoHidDevice> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(FidoHidDevice); diff --git a/chromium/device/fido/hid/fido_hid_device_unittest.cc b/chromium/device/fido/hid/fido_hid_device_unittest.cc index b6597970bb2..c229fc1841e 100644 --- a/chromium/device/fido/hid/fido_hid_device_unittest.cc +++ b/chromium/device/fido/hid/fido_hid_device_unittest.cc @@ -21,8 +21,8 @@ #include "device/fido/hid/fake_hid_impl_for_testing.h" #include "device/fido/hid/fido_hid_message.h" #include "device/fido/test_callback_receiver.h" -#include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/bindings/remote.h" #include "services/device/public/cpp/hid/hid_device_filter.h" #include "services/device/public/mojom/hid.mojom.h" #include "testing/gmock/include/gmock/gmock.h" @@ -136,12 +136,13 @@ CreateHidConnectionWithHidInitExpectations( FakeFidoHidManager* fake_hid_manager, ::testing::Sequence sequence) { auto hid_device = TestHidDevice(); - device::mojom::HidConnectionPtr connection_client; + mojo::PendingRemote<device::mojom::HidConnection> connection_client; // Replace device HID connection with custom client connection bound to mock // server-side mojo connection. auto mock_connection = std::make_unique<MockFidoHidConnection>( - hid_device.Clone(), mojo::MakeRequest(&connection_client), channel_id); + hid_device.Clone(), connection_client.InitWithNewPipeAndPassReceiver(), + channel_id); // Initial write for establishing channel ID. mock_connection->ExpectWriteHidInit(); @@ -223,13 +224,13 @@ class FidoHidDeviceTest : public ::testing::Test { public: void SetUp() override { fake_hid_manager_ = std::make_unique<FakeFidoHidManager>(); - fake_hid_manager_->AddBinding2(mojo::MakeRequest(&hid_manager_)); + fake_hid_manager_->AddReceiver2(hid_manager_.BindNewPipeAndPassReceiver()); } protected: base::test::TaskEnvironment task_environment_{ base::test::TaskEnvironment::TimeSource::MOCK_TIME}; - device::mojom::HidManagerPtr hid_manager_; + mojo::Remote<device::mojom::HidManager> hid_manager_; std::unique_ptr<FakeFidoHidManager> fake_hid_manager_; }; @@ -283,9 +284,10 @@ TEST_F(FidoHidDeviceTest, TestRetryChannelAllocation) { // Replace device HID connection with custom client connection bound to mock // server-side mojo connection. - device::mojom::HidConnectionPtr connection_client; + mojo::PendingRemote<device::mojom::HidConnection> connection_client; MockFidoHidConnection mock_connection( - hid_device.Clone(), mojo::MakeRequest(&connection_client), kChannelId); + hid_device.Clone(), connection_client.InitWithNewPipeAndPassReceiver(), + kChannelId); // Initial write for establishing a channel ID. mock_connection.ExpectWriteHidInit(); @@ -883,9 +885,10 @@ TEST_F(FidoHidDeviceTest, TestWinkNotSupported) { // Replace device HID connection with custom client connection bound to mock // server-side mojo connection. - device::mojom::HidConnectionPtr connection_client; + mojo::PendingRemote<device::mojom::HidConnection> connection_client; MockFidoHidConnection mock_connection( - hid_device.Clone(), mojo::MakeRequest(&connection_client), kChannelId); + hid_device.Clone(), connection_client.InitWithNewPipeAndPassReceiver(), + kChannelId); // Initial write for establishing a channel ID. mock_connection.ExpectWriteHidInit(); @@ -943,9 +946,10 @@ TEST_F(FidoHidDeviceTest, TestCtap2DeviceShouldNotBlink) { // Replace device HID connection with custom client connection bound to mock // server-side mojo connection. - device::mojom::HidConnectionPtr connection_client; + mojo::PendingRemote<device::mojom::HidConnection> connection_client; MockFidoHidConnection mock_connection( - hid_device.Clone(), mojo::MakeRequest(&connection_client), kChannelId); + hid_device.Clone(), connection_client.InitWithNewPipeAndPassReceiver(), + kChannelId); // Initial write for establishing a channel ID. mock_connection.ExpectWriteHidInit(); @@ -998,9 +1002,10 @@ TEST_F(FidoHidDeviceTest, TestSuccessfulWink) { // Replace device HID connection with custom client connection bound to mock // server-side mojo connection. - device::mojom::HidConnectionPtr connection_client; + mojo::PendingRemote<device::mojom::HidConnection> connection_client; MockFidoHidConnection mock_connection( - hid_device.Clone(), mojo::MakeRequest(&connection_client), kChannelId); + hid_device.Clone(), connection_client.InitWithNewPipeAndPassReceiver(), + kChannelId); // Initial write for establishing a channel ID. mock_connection.ExpectWriteHidInit(); diff --git a/chromium/device/fido/hid/fido_hid_discovery.cc b/chromium/device/fido/hid/fido_hid_discovery.cc index c471cd02df8..cd80cf0fca7 100644 --- a/chromium/device/fido/hid/fido_hid_discovery.cc +++ b/chromium/device/fido/hid/fido_hid_discovery.cc @@ -8,7 +8,6 @@ #include "base/bind.h" #include "device/fido/hid/fido_hid_device.h" -#include "mojo/public/cpp/bindings/interface_request.h" #include "services/device/public/mojom/constants.mojom.h" #include "services/service_manager/public/cpp/connector.h" @@ -16,8 +15,7 @@ namespace device { FidoHidDiscovery::FidoHidDiscovery(::service_manager::Connector* connector) : FidoDeviceDiscovery(FidoTransportProtocol::kUsbHumanInterfaceDevice), - connector_(connector), - binding_(this) { + connector_(connector) { // TODO(piperc@): Give this constant a name. filter_.SetUsagePage(0xf1d0); } @@ -26,14 +24,13 @@ FidoHidDiscovery::~FidoHidDiscovery() = default; void FidoHidDiscovery::StartInternal() { DCHECK(connector_); - connector_->BindInterface(device::mojom::kServiceName, - mojo::MakeRequest(&hid_manager_)); - device::mojom::HidManagerClientAssociatedPtrInfo client; - binding_.Bind(mojo::MakeRequest(&client)); + connector_->Connect(device::mojom::kServiceName, + hid_manager_.BindNewPipeAndPassReceiver()); hid_manager_->GetDevicesAndSetClient( - std::move(client), base::BindOnce(&FidoHidDiscovery::OnGetDevices, - weak_factory_.GetWeakPtr())); + receiver_.BindNewEndpointAndPassRemote(), + base::BindOnce(&FidoHidDiscovery::OnGetDevices, + weak_factory_.GetWeakPtr())); } void FidoHidDiscovery::DeviceAdded( diff --git a/chromium/device/fido/hid/fido_hid_discovery.h b/chromium/device/fido/hid/fido_hid_discovery.h index 204bb256122..25772ae2dbd 100644 --- a/chromium/device/fido/hid/fido_hid_discovery.h +++ b/chromium/device/fido/hid/fido_hid_discovery.h @@ -12,7 +12,8 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "device/fido/fido_device_discovery.h" -#include "mojo/public/cpp/bindings/associated_binding.h" +#include "mojo/public/cpp/bindings/associated_receiver.h" +#include "mojo/public/cpp/bindings/remote.h" #include "services/device/public/cpp/hid/hid_device_filter.h" #include "services/device/public/mojom/hid.mojom.h" @@ -43,8 +44,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoHidDiscovery void OnGetDevices(std::vector<device::mojom::HidDeviceInfoPtr> devices); service_manager::Connector* connector_; - device::mojom::HidManagerPtr hid_manager_; - mojo::AssociatedBinding<device::mojom::HidManagerClient> binding_; + mojo::Remote<device::mojom::HidManager> hid_manager_; + mojo::AssociatedReceiver<device::mojom::HidManagerClient> receiver_{this}; HidDeviceFilter filter_; base::WeakPtrFactory<FidoHidDiscovery> weak_factory_{this}; diff --git a/chromium/device/fido/hid/fido_hid_discovery_unittest.cc b/chromium/device/fido/hid/fido_hid_discovery_unittest.cc index 40d8d61845c..407af4a4bc5 100644 --- a/chromium/device/fido/hid/fido_hid_discovery_unittest.cc +++ b/chromium/device/fido/hid/fido_hid_discovery_unittest.cc @@ -4,8 +4,10 @@ #include "device/fido/hid/fido_hid_discovery.h" +#include <algorithm> #include <string> #include <utility> +#include <vector> #include "base/test/task_environment.h" #include "device/fido/fido_authenticator.h" @@ -52,13 +54,13 @@ TEST_F(FidoHidDiscoveryTest, TestAddRemoveDevice) { fake_hid_manager_.AddFidoHidDevice("known"); - EXPECT_CALL(observer, DiscoveryStarted(&discovery, true)); - discovery.set_observer(&observer); - discovery.Start(); - // Devices initially known to the service before discovery started should be // reported as KNOWN. - EXPECT_CALL(observer, AuthenticatorAdded(&discovery, IdMatches("known"))); + EXPECT_CALL(observer, + DiscoveryStarted(&discovery, true, + testing::ElementsAre(IdMatches("known")))); + discovery.set_observer(&observer); + discovery.Start(); task_environment_.RunUntilIdle(); // Devices added during the discovery should be reported as ADDED. diff --git a/chromium/device/fido/mac/discovery.cc b/chromium/device/fido/mac/discovery.cc index d7f79396bf6..80e4fce5012 100644 --- a/chromium/device/fido/mac/discovery.cc +++ b/chromium/device/fido/mac/discovery.cc @@ -26,13 +26,6 @@ void FidoTouchIdDiscovery::Start() { return; } - if (!TouchIdAuthenticator::IsAvailable(authenticator_config_)) { - observer()->DiscoveryStarted(this, /*success=*/false); - return; - } - - observer()->DiscoveryStarted(this, /*success=*/true); - // Start() is currently invoked synchronously in the // FidoRequestHandler ctor. Invoke AddAuthenticator() asynchronously // to avoid hairpinning FidoRequestHandler::AuthenticatorAdded() @@ -43,9 +36,12 @@ void FidoTouchIdDiscovery::Start() { } void FidoTouchIdDiscovery::AddAuthenticator() { - DCHECK(TouchIdAuthenticator::IsAvailable(authenticator_config_)); + if (!TouchIdAuthenticator::IsAvailable(authenticator_config_)) { + observer()->DiscoveryStarted(this, /*success=*/false); + return; + } authenticator_ = TouchIdAuthenticator::Create(authenticator_config_); - observer()->AuthenticatorAdded(this, authenticator_.get()); + observer()->DiscoveryStarted(this, /*success=*/true, {authenticator_.get()}); } } // namespace mac diff --git a/chromium/device/fido/make_credential_handler_unittest.cc b/chromium/device/fido/make_credential_handler_unittest.cc index b9f8adcd042..fd62b38b075 100644 --- a/chromium/device/fido/make_credential_handler_unittest.cc +++ b/chromium/device/fido/make_credential_handler_unittest.cc @@ -9,7 +9,6 @@ #include "base/bind_helpers.h" #include "base/test/task_environment.h" #include "base/threading/thread_task_runner_handle.h" -#include "build/build_config.h" #include "components/cbor/reader.h" #include "components/cbor/values.h" #include "device/bluetooth/bluetooth_adapter_factory.h" @@ -33,10 +32,6 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#if defined(OS_WIN) -#include "device/fido/win/fake_webauthn_api.h" -#endif // defined(OS_WIN) - using ::testing::_; using ::testing::DoAll; using ::testing::Invoke; @@ -91,9 +86,12 @@ class FidoMakeCredentialHandlerTest : public ::testing::Test { auto handler = std::make_unique<MakeCredentialRequestHandler>( nullptr, fake_discovery_factory_.get(), supported_transports_, std::move(request_parameter), - std::move(authenticator_selection_criteria), cb_.callback()); - if (pending_mock_platform_device_) + std::move(authenticator_selection_criteria), + /*allow_skipping_pin_touch=*/true, cb_.callback()); + if (pending_mock_platform_device_) { platform_discovery_->AddDevice(std::move(pending_mock_platform_device_)); + platform_discovery_->WaitForCallToStartAndSimulateSuccess(); + } return handler; } @@ -151,11 +149,6 @@ class FidoMakeCredentialHandlerTest : public ::testing::Test { TestMakeCredentialRequestCallback cb_; base::flat_set<FidoTransportProtocol> supported_transports_ = GetAllTransportProtocols(); - -#if defined(OS_WIN) - device::ScopedFakeWinWebAuthnApi win_webauthn_api_ = - device::ScopedFakeWinWebAuthnApi::MakeUnavailable(); -#endif // defined(OS_WIN) }; TEST_F(FidoMakeCredentialHandlerTest, TransportAvailabilityInfo) { diff --git a/chromium/device/fido/make_credential_request_handler.cc b/chromium/device/fido/make_credential_request_handler.cc index a76d0486414..4cca45d7e15 100644 --- a/chromium/device/fido/make_credential_request_handler.cc +++ b/chromium/device/fido/make_credential_request_handler.cc @@ -12,6 +12,7 @@ #include "base/metrics/histogram_functions.h" #include "base/stl_util.h" #include "build/build_config.h" +#include "components/cbor/diagnostic_writer.h" #include "components/device_event_log/device_event_log.h" #include "device/fido/fido_authenticator.h" #include "device/fido/fido_parsing_utils.h" @@ -177,6 +178,7 @@ MakeCredentialRequestHandler::MakeCredentialRequestHandler( const base::flat_set<FidoTransportProtocol>& supported_transports, CtapMakeCredentialRequest request, AuthenticatorSelectionCriteria authenticator_selection_criteria, + bool allow_skipping_pin_touch, CompletionCallback completion_callback) : FidoRequestHandlerBase( connector, @@ -187,7 +189,8 @@ MakeCredentialRequestHandler::MakeCredentialRequestHandler( completion_callback_(std::move(completion_callback)), request_(std::move(request)), authenticator_selection_criteria_( - std::move(authenticator_selection_criteria)) { + std::move(authenticator_selection_criteria)), + allow_skipping_pin_touch_(allow_skipping_pin_touch) { transport_availability_info().request_type = FidoRequestHandlerBase::RequestType::kMakeCredential; @@ -252,6 +255,11 @@ void MakeCredentialRequestHandler::DispatchRequest( switch (authenticator->WillNeedPINToMakeCredential(request_, observer())) { case MakeCredentialPINDisposition::kUsePIN: case MakeCredentialPINDisposition::kSetPIN: + // Skip asking for touch if this is the only available authenticator. + if (active_authenticators().size() == 1 && allow_skipping_pin_touch_) { + HandleTouch(authenticator); + return; + } // A PIN will be needed. Just request a touch to let the user select // this authenticator if they wish. authenticator->GetTouch( @@ -373,14 +381,43 @@ void MakeCredentialRequestHandler::HandleResponse( const auto rp_id_hash = fido_parsing_utils::CreateSHA256Hash(request_.rp.id); if (!response || response->GetRpIdHash() != rp_id_hash) { - FIDO_LOG(ERROR) << "Failing assertion request due to bad response from " - << authenticator->GetDisplayName(); + FIDO_LOG(ERROR) + << "Failing make credential request due to bad response from " + << authenticator->GetDisplayName(); std::move(completion_callback_) .Run(MakeCredentialStatus::kAuthenticatorResponseInvalid, base::nullopt, authenticator); return; } + const base::Optional<cbor::Value>& extensions = + response->attestation_object().authenticator_data().extensions(); + if (extensions) { + // The fact that |extensions| is a map is checked in + // |AuthenticatorData::DecodeAuthenticatorData|. + for (const auto& it : extensions->GetMap()) { + if (request_.cred_protect && + authenticator->Options()->supports_cred_protect && + it.first.is_string() && + it.first.GetString() == kExtensionCredProtect && + it.second.is_integer()) { + continue; + } + if (request_.hmac_secret && it.first.is_string() && + it.first.GetString() == kExtensionHmacSecret && it.second.is_bool()) { + continue; + } + + FIDO_LOG(ERROR) + << "Failing make credential request due to extensions block: " + << cbor::DiagnosticWriter::Write(*extensions); + std::move(completion_callback_) + .Run(MakeCredentialStatus::kAuthenticatorResponseInvalid, + base::nullopt, authenticator); + return; + } + } + if (authenticator->AuthenticatorTransport()) { base::UmaHistogramEnumeration( "WebAuthentication.MakeCredentialResponseTransport", diff --git a/chromium/device/fido/make_credential_request_handler.h b/chromium/device/fido/make_credential_request_handler.h index e4dcce2b1a3..a0128b67200 100644 --- a/chromium/device/fido/make_credential_request_handler.h +++ b/chromium/device/fido/make_credential_request_handler.h @@ -70,6 +70,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) MakeCredentialRequestHandler const base::flat_set<FidoTransportProtocol>& supported_transports, CtapMakeCredentialRequest request_parameter, AuthenticatorSelectionCriteria authenticator_criteria, + bool allow_skipping_pin_touch, CompletionCallback completion_callback); ~MakeCredentialRequestHandler() override; @@ -115,6 +116,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) MakeCredentialRequestHandler State state_ = State::kWaitingForTouch; CtapMakeCredentialRequest request_; AuthenticatorSelectionCriteria authenticator_selection_criteria_; + // If true, the request handler may skip the first touch to select a device + // that will require a PIN. + bool allow_skipping_pin_touch_; // authenticator_ points to the authenticator that will be used for this // operation. It's only set after the user touches an authenticator to select // it, after which point that authenticator will be used exclusively through diff --git a/chromium/device/fido/mock_fido_discovery_observer.h b/chromium/device/fido/mock_fido_discovery_observer.h index ce022186b6c..6be19886a72 100644 --- a/chromium/device/fido/mock_fido_discovery_observer.h +++ b/chromium/device/fido/mock_fido_discovery_observer.h @@ -6,6 +6,7 @@ #define DEVICE_FIDO_MOCK_FIDO_DISCOVERY_OBSERVER_H_ #include <string> +#include <vector> #include "base/component_export.h" #include "base/macros.h" @@ -21,7 +22,8 @@ class MockFidoDiscoveryObserver : public FidoDiscoveryBase::Observer { MockFidoDiscoveryObserver(); ~MockFidoDiscoveryObserver() override; - MOCK_METHOD2(DiscoveryStarted, void(FidoDiscoveryBase*, bool)); + MOCK_METHOD3(DiscoveryStarted, + void(FidoDiscoveryBase*, bool, std::vector<FidoAuthenticator*>)); MOCK_METHOD2(DiscoveryStopped, void(FidoDiscoveryBase*, bool)); MOCK_METHOD2(AuthenticatorAdded, void(FidoDiscoveryBase*, FidoAuthenticator*)); diff --git a/chromium/device/fido/u2f_command_constructor.cc b/chromium/device/fido/u2f_command_constructor.cc index ce2839e662c..e54cf880ff0 100644 --- a/chromium/device/fido/u2f_command_constructor.cc +++ b/chromium/device/fido/u2f_command_constructor.cc @@ -53,12 +53,12 @@ base::Optional<std::vector<uint8_t>> ConvertToU2fRegisterCommand( request.client_data_hash, is_invidual_attestation); } -base::Optional<std::vector<uint8_t>> ConvertToU2fSignCommand( +base::Optional<std::vector<uint8_t>> ConvertToU2fSignCommandWithBogusChallenge( const CtapMakeCredentialRequest& request, base::span<const uint8_t> key_handle) { return ConstructU2fSignCommand( fido_parsing_utils::CreateSHA256Hash(request.rp.id), - request.client_data_hash, key_handle); + kBogusChallenge, key_handle); } base::Optional<std::vector<uint8_t>> ConvertToU2fSignCommand( diff --git a/chromium/device/fido/u2f_command_constructor.h b/chromium/device/fido/u2f_command_constructor.h index fd3225d4901..e36fecc7112 100644 --- a/chromium/device/fido/u2f_command_constructor.h +++ b/chromium/device/fido/u2f_command_constructor.h @@ -37,9 +37,10 @@ COMPONENT_EXPORT(DEVICE_FIDO) base::Optional<std::vector<uint8_t>> ConvertToU2fRegisterCommand( const CtapMakeCredentialRequest& request); -// Extracts APDU encoded U2F sign command from CtapMakeCredentialRequest. +// Turns a CtapMakeCredentialRequest into an APDU encoded U2F sign command +// for the same RP and key handle, but a bogus challenge. COMPONENT_EXPORT(DEVICE_FIDO) -base::Optional<std::vector<uint8_t>> ConvertToU2fSignCommand( +base::Optional<std::vector<uint8_t>> ConvertToU2fSignCommandWithBogusChallenge( const CtapMakeCredentialRequest& request, base::span<const uint8_t> key_handle); diff --git a/chromium/device/fido/u2f_register_operation.cc b/chromium/device/fido/u2f_register_operation.cc index 3a85342a84e..1a7d3954b97 100644 --- a/chromium/device/fido/u2f_register_operation.cc +++ b/chromium/device/fido/u2f_register_operation.cc @@ -57,9 +57,11 @@ void U2fRegisterOperation::TrySign() { if (probing_alternative_rp_id_) { CtapMakeCredentialRequest sign_request(request()); sign_request.rp.id = *request().app_id; - sign_command = ConvertToU2fSignCommand(sign_request, excluded_key_handle()); + sign_command = ConvertToU2fSignCommandWithBogusChallenge( + sign_request, excluded_key_handle()); } else { - sign_command = ConvertToU2fSignCommand(request(), excluded_key_handle()); + sign_command = ConvertToU2fSignCommandWithBogusChallenge( + request(), excluded_key_handle()); } DispatchDeviceRequest( diff --git a/chromium/device/fido/u2f_register_operation_unittest.cc b/chromium/device/fido/u2f_register_operation_unittest.cc index 7f4eaf0ac70..bd55c89ebb4 100644 --- a/chromium/device/fido/u2f_register_operation_unittest.cc +++ b/chromium/device/fido/u2f_register_operation_unittest.cc @@ -155,17 +155,19 @@ TEST_F(U2fRegisterOperationTest, TestRegistrationWithExclusionList) { auto device = std::make_unique<MockFidoDevice>(); EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device")); device->ExpectWinkedAtLeastOnce(); - // 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/WrongLength 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. + // DeviceTransact() will be called three times including two sign-in calls + // with bogus challenges and one registration call. For the first two calls, + // device will invoke MockFidoDevice::WrongData/WrongLength 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::kU2fSignCommandApduWithKeyAlpha, + test_data::kU2fSignCommandApduWithKeyAlphaAndBogusChallenge, test_data::kU2fWrongDataApduResponse); - device->ExpectRequestAndRespondWith(test_data::kU2fSignCommandApduWithKeyBeta, - test_data::kU2fWrongLengthApduResponse); + device->ExpectRequestAndRespondWith( + test_data::kU2fSignCommandApduWithKeyBetaAndBogusChallenge, + test_data::kU2fWrongLengthApduResponse); device->ExpectRequestAndRespondWith( test_data::kU2fRegisterCommandApdu, test_data::kApduEncodedNoErrorRegisterResponse); @@ -210,12 +212,15 @@ TEST_F(U2fRegisterOperationTest, TestRegistrationWithDuplicateHandle) { // request is concluded with Ctap2ErrCredentialExcluded. ::testing::InSequence s; device->ExpectRequestAndRespondWith( - test_data::kU2fSignCommandApduWithKeyAlpha, + test_data::kU2fSignCommandApduWithKeyAlphaAndBogusChallenge, + test_data::kU2fWrongDataApduResponse); + device->ExpectRequestAndRespondWith( + test_data::kU2fSignCommandApduWithKeyBetaAndBogusChallenge, test_data::kU2fWrongDataApduResponse); - device->ExpectRequestAndRespondWith(test_data::kU2fSignCommandApduWithKeyBeta, - test_data::kU2fWrongDataApduResponse); + // The signature in the response is intentionally incorrect since nothing + // should depend on it being correct. device->ExpectRequestAndRespondWith( - test_data::kU2fSignCommandApduWithKeyGamma, + test_data::kU2fSignCommandApduWithKeyGammaAndBogusChallenge, test_data::kApduEncodedNoErrorSignResponse); auto u2f_register = std::make_unique<U2fRegisterOperation>( diff --git a/chromium/device/fido/virtual_ctap2_device.cc b/chromium/device/fido/virtual_ctap2_device.cc index b3450febf46..d3f9971cf4f 100644 --- a/chromium/device/fido/virtual_ctap2_device.cc +++ b/chromium/device/fido/virtual_ctap2_device.cc @@ -791,6 +791,11 @@ base::Optional<CtapDeviceResponseCode> VirtualCtap2Device::OnMakeCredential( cbor::Value( request.cred_protect->first == CredProtect::kUVRequired ? 3 : 2)); } + + if (config_.add_extra_extension) { + extensions_map.emplace(cbor::Value("unsolicited"), cbor::Value(42)); + } + if (!extensions_map.empty()) { extensions = cbor::Value(std::move(extensions_map)); } @@ -983,6 +988,15 @@ base::Optional<CtapDeviceResponseCode> VirtualCtap2Device::OnGetAssertion( return CtapDeviceResponseCode::kCtap2ErrNoCredentials; } + base::Optional<cbor::Value> extensions; + cbor::Value::MapValue extensions_map; + if (config_.add_extra_extension) { + extensions_map.emplace(cbor::Value("unsolicited"), cbor::Value(42)); + } + if (!extensions_map.empty()) { + extensions = cbor::Value(std::move(extensions_map)); + } + // This implementation does not sort credentials by creation time as the spec // requires. @@ -1005,7 +1019,8 @@ base::Optional<CtapDeviceResponseCode> VirtualCtap2Device::OnGetAssertion( auto authenticator_data = ConstructAuthenticatorData( rp_id_hash, user_verified, registration.second->counter, - std::move(opt_attested_cred_data), base::nullopt); + std::move(opt_attested_cred_data), + extensions ? base::make_optional(extensions->Clone()) : base::nullopt); auto signature_buffer = ConstructSignatureBuffer(authenticator_data, client_data_hash); diff --git a/chromium/device/fido/virtual_ctap2_device.h b/chromium/device/fido/virtual_ctap2_device.h index 62114aae5e1..a598c78b2e1 100644 --- a/chromium/device/fido/virtual_ctap2_device.h +++ b/chromium/device/fido/virtual_ctap2_device.h @@ -85,6 +85,10 @@ class COMPONENT_EXPORT(DEVICE_FIDO) VirtualCtap2Device // at a pre-defined byte length without concern for UTF-8 validity of the // result. bool allow_invalid_utf8_in_credential_entities = false; + + // add_extra_extension causes an unsolicited extension to be added in the + // authenticator extensions output. + bool add_extra_extension = false; }; VirtualCtap2Device(); diff --git a/chromium/device/fido/virtual_fido_device_factory.cc b/chromium/device/fido/virtual_fido_device_factory.cc index 69c3aef6dc7..fd2e2aaa3bf 100644 --- a/chromium/device/fido/virtual_fido_device_factory.cc +++ b/chromium/device/fido/virtual_fido_device_factory.cc @@ -57,8 +57,7 @@ class VirtualFidoDeviceDiscovery DISALLOW_COPY_AND_ASSIGN(VirtualFidoDeviceDiscovery); }; -VirtualFidoDeviceFactory::VirtualFidoDeviceFactory() - : state_(new VirtualFidoDevice::State) {} +VirtualFidoDeviceFactory::VirtualFidoDeviceFactory() = default; VirtualFidoDeviceFactory::~VirtualFidoDeviceFactory() = default; void VirtualFidoDeviceFactory::SetSupportedProtocol( diff --git a/chromium/device/fido/virtual_fido_device_factory.h b/chromium/device/fido/virtual_fido_device_factory.h index daf500bb345..44747edcc69 100644 --- a/chromium/device/fido/virtual_fido_device_factory.h +++ b/chromium/device/fido/virtual_fido_device_factory.h @@ -48,7 +48,7 @@ class VirtualFidoDeviceFactory : public device::FidoDiscoveryFactory { FidoTransportProtocol transport_ = FidoTransportProtocol::kUsbHumanInterfaceDevice; VirtualCtap2Device::Config ctap2_config_; - scoped_refptr<VirtualFidoDevice::State> state_; + scoped_refptr<VirtualFidoDevice::State> state_ = new VirtualFidoDevice::State; DISALLOW_COPY_AND_ASSIGN(VirtualFidoDeviceFactory); }; diff --git a/chromium/device/fido/win/authenticator.cc b/chromium/device/fido/win/authenticator.cc index afcd487d398..88f88042340 100644 --- a/chromium/device/fido/win/authenticator.cc +++ b/chromium/device/fido/win/authenticator.cc @@ -4,7 +4,6 @@ #include "device/fido/win/authenticator.h" -#include <Combaseapi.h> #include <windows.h> #include <utility> #include <vector> @@ -24,24 +23,24 @@ #include "device/fido/fido_constants.h" #include "device/fido/fido_transport_protocol.h" #include "device/fido/win/type_conversions.h" +#include "device/fido/win/webauthn_api.h" #include "third_party/microsoft_webauthn/webauthn.h" namespace device { // static -bool WinWebAuthnApiAuthenticator:: - IsUserVerifyingPlatformAuthenticatorAvailable() { +bool WinWebAuthnApiAuthenticator::IsUserVerifyingPlatformAuthenticatorAvailable( + WinWebAuthnApi* api) { BOOL result; - WinWebAuthnApi* api = WinWebAuthnApi::GetDefault(); - return api->IsAvailable() && + return api && api->IsAvailable() && api->IsUserVerifyingPlatformAuthenticatorAvailable(&result) == S_OK && result == TRUE; } -WinWebAuthnApiAuthenticator::WinWebAuthnApiAuthenticator(HWND current_window) - : FidoAuthenticator(), - current_window_(current_window), - win_api_(WinWebAuthnApi::GetDefault()) { +WinWebAuthnApiAuthenticator::WinWebAuthnApiAuthenticator( + HWND current_window, + WinWebAuthnApi* win_api) + : current_window_(current_window), win_api_(win_api) { CHECK(win_api_->IsAvailable()); CoCreateGuid(&cancellation_id_); } diff --git a/chromium/device/fido/win/authenticator.h b/chromium/device/fido/win/authenticator.h index 36adb1a8814..3eccf8ce791 100644 --- a/chromium/device/fido/win/authenticator.h +++ b/chromium/device/fido/win/authenticator.h @@ -5,6 +5,7 @@ #ifndef DEVICE_FIDO_WIN_AUTHENTICATOR_H_ #define DEVICE_FIDO_WIN_AUTHENTICATOR_H_ +#include <Combaseapi.h> #include <memory> #include <string> @@ -14,10 +15,11 @@ #include "base/optional.h" #include "base/sequence_checker.h" #include "device/fido/fido_authenticator.h" -#include "device/fido/win/webauthn_api.h" namespace device { +class WinWebAuthnApi; + // WinWebAuthnApiAuthenticator forwards WebAuthn requests to external // authenticators via the native Windows WebAuthentication API // (webauthn.dll). @@ -29,13 +31,14 @@ class COMPONENT_EXPORT(DEVICE_FIDO) WinWebAuthnApiAuthenticator public: // This method is safe to call without checking // WinWebAuthnApi::IsAvailable(). - static bool IsUserVerifyingPlatformAuthenticatorAvailable(); + static bool IsUserVerifyingPlatformAuthenticatorAvailable( + WinWebAuthnApi* api); // Instantiates an authenticator that uses the default WinWebAuthnApi. // // Callers must ensure that WinWebAuthnApi::IsAvailable() returns true // before creating instances of this class. - WinWebAuthnApiAuthenticator(HWND current_window); + WinWebAuthnApiAuthenticator(HWND current_window, WinWebAuthnApi* win_api_); ~WinWebAuthnApiAuthenticator() override; // SupportsCredProtectExtension returns whether the native API supports the diff --git a/chromium/device/fido/win/discovery.cc b/chromium/device/fido/win/discovery.cc index fa41083bec2..f5e4abf4b68 100644 --- a/chromium/device/fido/win/discovery.cc +++ b/chromium/device/fido/win/discovery.cc @@ -12,9 +12,11 @@ namespace device { WinWebAuthnApiAuthenticatorDiscovery::WinWebAuthnApiAuthenticatorDiscovery( - HWND parent_window) + HWND parent_window, + WinWebAuthnApi* api) : FidoDiscoveryBase(FidoTransportProtocol::kUsbHumanInterfaceDevice), - parent_window_(parent_window) {} + parent_window_(parent_window), + api_(api) {} WinWebAuthnApiAuthenticatorDiscovery::~WinWebAuthnApiAuthenticatorDiscovery() = default; @@ -25,13 +27,6 @@ void WinWebAuthnApiAuthenticatorDiscovery::Start() { return; } - if (!WinWebAuthnApi::GetDefault()->IsAvailable()) { - observer()->DiscoveryStarted(this, false /* discovery failed */); - return; - } - - observer()->DiscoveryStarted(this, true /* success */); - // Start() is currently invoked synchronously in the // FidoRequestHandler ctor. Invoke AddAuthenticator() asynchronously // to avoid hairpinning FidoRequestHandler::AuthenticatorAdded() @@ -43,13 +38,13 @@ void WinWebAuthnApiAuthenticatorDiscovery::Start() { } void WinWebAuthnApiAuthenticatorDiscovery::AddAuthenticator() { - if (!WinWebAuthnApi::GetDefault()->IsAvailable()) { - NOTREACHED(); + if (!api_->IsAvailable()) { + observer()->DiscoveryStarted(this, /*success=*/false); return; } authenticator_ = - std::make_unique<WinWebAuthnApiAuthenticator>(parent_window_); - observer()->AuthenticatorAdded(this, authenticator_.get()); + std::make_unique<WinWebAuthnApiAuthenticator>(parent_window_, api_); + observer()->DiscoveryStarted(this, /*success=*/true, {authenticator_.get()}); } } // namespace device diff --git a/chromium/device/fido/win/discovery.h b/chromium/device/fido/win/discovery.h index 10f2b008706..201e6017d9e 100644 --- a/chromium/device/fido/win/discovery.h +++ b/chromium/device/fido/win/discovery.h @@ -14,12 +14,14 @@ namespace device { +class WinWebAuthnApi; + // Instantiates the authenticator subclass for forwarding requests to external // authenticators via the Windows WebAuthn API. class COMPONENT_EXPORT(DEVICE_FIDO) WinWebAuthnApiAuthenticatorDiscovery : public FidoDiscoveryBase { public: - WinWebAuthnApiAuthenticatorDiscovery(HWND parent_window); + WinWebAuthnApiAuthenticatorDiscovery(HWND parent_window, WinWebAuthnApi* api); ~WinWebAuthnApiAuthenticatorDiscovery() override; // FidoDiscoveryBase: @@ -30,6 +32,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) WinWebAuthnApiAuthenticatorDiscovery std::unique_ptr<WinWebAuthnApiAuthenticator> authenticator_; const HWND parent_window_; + WinWebAuthnApi* api_; base::WeakPtrFactory<WinWebAuthnApiAuthenticatorDiscovery> weak_factory_{ this}; diff --git a/chromium/device/fido/win/fake_webauthn_api.cc b/chromium/device/fido/win/fake_webauthn_api.cc index 837a1bb7713..6c77584d668 100644 --- a/chromium/device/fido/win/fake_webauthn_api.cc +++ b/chromium/device/fido/win/fake_webauthn_api.cc @@ -12,54 +12,7 @@ namespace device { -namespace { - -WEBAUTHN_CREDENTIAL_ATTESTATION FakeAttestation() { - WEBAUTHN_CREDENTIAL_ATTESTATION attestation = {}; - attestation.dwVersion = WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_1; - attestation.cbAuthenticatorData = - sizeof(test_data::kCtap2MakeCredentialAuthData); - attestation.pbAuthenticatorData = reinterpret_cast<PBYTE>( - const_cast<uint8_t*>(device::test_data::kCtap2MakeCredentialAuthData)); - attestation.cbAttestation = - sizeof(test_data::kPackedAttestationStatementCBOR); - attestation.pbAttestation = reinterpret_cast<PBYTE>( - const_cast<uint8_t*>(device::test_data::kPackedAttestationStatementCBOR)); - attestation.cbAttestationObject = 0; - attestation.cbCredentialId = 0; - attestation.pwszFormatType = L"packed"; - attestation.dwAttestationDecodeType = 0; - return attestation; -} - -WEBAUTHN_ASSERTION FakeAssertion() { - WEBAUTHN_CREDENTIAL credential = {}; - // No constant macro available because 1 is the current version - credential.dwVersion = 1; - credential.cbId = sizeof(test_data::kCredentialId); - credential.pbId = - reinterpret_cast<PBYTE>(const_cast<uint8_t*>(test_data::kCredentialId)); - credential.pwszCredentialType = L"public-key"; - - WEBAUTHN_ASSERTION assertion = {}; - // No constant macro available because 1 is the current version - assertion.dwVersion = 1; - assertion.cbAuthenticatorData = sizeof(test_data::kTestSignAuthenticatorData); - assertion.pbAuthenticatorData = reinterpret_cast<PBYTE>( - const_cast<uint8_t*>(test_data::kTestSignAuthenticatorData)); - assertion.cbSignature = sizeof(test_data::kCtap2GetAssertionSignature); - assertion.pbSignature = reinterpret_cast<PBYTE>( - const_cast<uint8_t*>(test_data::kCtap2GetAssertionSignature)); - assertion.Credential = credential; - assertion.pbUserId = NULL; - assertion.cbUserId = 0; - return assertion; -} - -} // namespace - -FakeWinWebAuthnApi::FakeWinWebAuthnApi() - : attestation_(FakeAttestation()), assertion_(FakeAssertion()) {} +FakeWinWebAuthnApi::FakeWinWebAuthnApi() = default; FakeWinWebAuthnApi::~FakeWinWebAuthnApi() = default; bool FakeWinWebAuthnApi::IsAvailable() const { @@ -137,19 +90,48 @@ int FakeWinWebAuthnApi::Version() { return version_; } -ScopedFakeWinWebAuthnApi::ScopedFakeWinWebAuthnApi() : FakeWinWebAuthnApi() { - WinWebAuthnApi::SetDefaultForTesting(this); -} - -ScopedFakeWinWebAuthnApi::~ScopedFakeWinWebAuthnApi() { - WinWebAuthnApi::ClearDefaultForTesting(); +// static +WEBAUTHN_CREDENTIAL_ATTESTATION FakeWinWebAuthnApi::FakeAttestation() { + WEBAUTHN_CREDENTIAL_ATTESTATION attestation = {}; + attestation.dwVersion = WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_1; + attestation.cbAuthenticatorData = + sizeof(test_data::kCtap2MakeCredentialAuthData); + attestation.pbAuthenticatorData = reinterpret_cast<PBYTE>( + const_cast<uint8_t*>(device::test_data::kCtap2MakeCredentialAuthData)); + attestation.cbAttestation = + sizeof(test_data::kPackedAttestationStatementCBOR); + attestation.pbAttestation = reinterpret_cast<PBYTE>( + const_cast<uint8_t*>(device::test_data::kPackedAttestationStatementCBOR)); + attestation.cbAttestationObject = 0; + attestation.cbCredentialId = 0; + attestation.pwszFormatType = L"packed"; + attestation.dwAttestationDecodeType = 0; + return attestation; } // static -ScopedFakeWinWebAuthnApi ScopedFakeWinWebAuthnApi::MakeUnavailable() { - ScopedFakeWinWebAuthnApi api; - api.set_available(false); - return api; +WEBAUTHN_ASSERTION FakeWinWebAuthnApi::FakeAssertion() { + WEBAUTHN_CREDENTIAL credential = {}; + // No constant macro available because 1 is the current version + credential.dwVersion = 1; + credential.cbId = sizeof(test_data::kCredentialId); + credential.pbId = + reinterpret_cast<PBYTE>(const_cast<uint8_t*>(test_data::kCredentialId)); + credential.pwszCredentialType = L"public-key"; + + WEBAUTHN_ASSERTION assertion = {}; + // No constant macro available because 1 is the current version + assertion.dwVersion = 1; + assertion.cbAuthenticatorData = sizeof(test_data::kTestSignAuthenticatorData); + assertion.pbAuthenticatorData = reinterpret_cast<PBYTE>( + const_cast<uint8_t*>(test_data::kTestSignAuthenticatorData)); + assertion.cbSignature = sizeof(test_data::kCtap2GetAssertionSignature); + assertion.pbSignature = reinterpret_cast<PBYTE>( + const_cast<uint8_t*>(test_data::kCtap2GetAssertionSignature)); + assertion.Credential = credential; + assertion.pbUserId = nullptr; + assertion.cbUserId = 0; + return assertion; } } // namespace device diff --git a/chromium/device/fido/win/fake_webauthn_api.h b/chromium/device/fido/win/fake_webauthn_api.h index cc4d3c9433a..88a901df9f8 100644 --- a/chromium/device/fido/win/fake_webauthn_api.h +++ b/chromium/device/fido/win/fake_webauthn_api.h @@ -5,6 +5,7 @@ #ifndef DEVICE_FIDO_WIN_FAKE_WEBAUTHN_API_H_ #define DEVICE_FIDO_WIN_FAKE_WEBAUTHN_API_H_ +#include "base/component_export.h" #include "device/fido/public_key_credential_descriptor.h" #include "device/fido/public_key_credential_rp_entity.h" #include "device/fido/public_key_credential_user_entity.h" @@ -12,7 +13,7 @@ namespace device { -class FakeWinWebAuthnApi : public WinWebAuthnApi { +class COMPONENT_EXPORT(DEVICE_FIDO) FakeWinWebAuthnApi : public WinWebAuthnApi { public: FakeWinWebAuthnApi(); ~FakeWinWebAuthnApi() override; @@ -53,34 +54,17 @@ class FakeWinWebAuthnApi : public WinWebAuthnApi { int Version() override; private: + static WEBAUTHN_CREDENTIAL_ATTESTATION FakeAttestation(); + static WEBAUTHN_ASSERTION FakeAssertion(); + bool is_available_ = true; bool is_uvpaa_ = false; int version_ = WEBAUTHN_API_VERSION_2; - WEBAUTHN_CREDENTIAL_ATTESTATION attestation_; - WEBAUTHN_ASSERTION assertion_; + WEBAUTHN_CREDENTIAL_ATTESTATION attestation_ = FakeAttestation(); + WEBAUTHN_ASSERTION assertion_ = FakeAssertion(); HRESULT result_ = S_OK; }; -// ScopedFakeWinWebAuthnApi overrides the value returned -// by WinWebAuthnApi::GetDefault() with itself for the duration of its -// lifetime. -class ScopedFakeWinWebAuthnApi : public FakeWinWebAuthnApi { - public: - // MakeUnavailable() returns a ScopedFakeWinWebAuthnApi that simulates a - // system where the native WebAuthn API is unavailable. - // - // Tests that instantiate a FidoDiscoveryFactory and FidoRequestHandler - // should instantiate a ScopedFakeWinWebAuthnApi with this method to avoid - // invoking the real Windows WebAuthn API on systems where it is available. - // Note that individual tests can call set_available(true) prior to - // instantiating the FidoRequestHandler in order to make the fake simulate an - // available API. - static ScopedFakeWinWebAuthnApi MakeUnavailable(); - - ScopedFakeWinWebAuthnApi(); - ~ScopedFakeWinWebAuthnApi() override; -}; - } // namespace device #endif // DEVICE_FIDO_WIN_FAKE_WEBAUTHN_API_H_ diff --git a/chromium/device/fido/win/webauthn_api.cc b/chromium/device/fido/win/webauthn_api.cc index 05936a30aa3..11b81adf9e5 100644 --- a/chromium/device/fido/win/webauthn_api.cc +++ b/chromium/device/fido/win/webauthn_api.cc @@ -162,30 +162,12 @@ class WinWebAuthnApiImpl : public WinWebAuthnApi { decltype(&WebAuthNGetApiVersionNumber) get_api_version_number_ = nullptr; }; -static WinWebAuthnApi* kDefaultForTesting = nullptr; - // static WinWebAuthnApi* WinWebAuthnApi::GetDefault() { - if (kDefaultForTesting) { - return kDefaultForTesting; - } - static base::NoDestructor<WinWebAuthnApiImpl> api; return api.get(); } -// static -void WinWebAuthnApi::SetDefaultForTesting(WinWebAuthnApi* api) { - DCHECK(!kDefaultForTesting); - kDefaultForTesting = api; -} - -// static -void WinWebAuthnApi::ClearDefaultForTesting() { - DCHECK(kDefaultForTesting); - kDefaultForTesting = nullptr; -} - WinWebAuthnApi::WinWebAuthnApi() = default; WinWebAuthnApi::~WinWebAuthnApi() = default; diff --git a/chromium/device/fido/win/webauthn_api.h b/chromium/device/fido/win/webauthn_api.h index 92951364f69..85a1f60a5d6 100644 --- a/chromium/device/fido/win/webauthn_api.h +++ b/chromium/device/fido/win/webauthn_api.h @@ -70,11 +70,6 @@ class COMPONENT_EXPORT(DEVICE_FIDO) WinWebAuthnApi { protected: WinWebAuthnApi(); - - private: - friend class ScopedFakeWinWebAuthnApi; - static void SetDefaultForTesting(WinWebAuthnApi* api); - static void ClearDefaultForTesting(); }; std::pair<CtapDeviceResponseCode, diff --git a/chromium/device/gamepad/BUILD.gn b/chromium/device/gamepad/BUILD.gn index ec7c1aea0a4..8442e95c2b7 100644 --- a/chromium/device/gamepad/BUILD.gn +++ b/chromium/device/gamepad/BUILD.gn @@ -184,8 +184,10 @@ if (is_android) { ] deps = [ "//base:base_java", - "//third_party/android_deps:com_android_support_support_annotations_java", + "//base:jni_java", + "//third_party/android_deps:androidx_annotation_annotation_java", ] + annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] srcjar_deps = [ ":java_enums_srcjar" ] } diff --git a/chromium/device/gamepad/android/java/src/org/chromium/device/gamepad/GamepadList.java b/chromium/device/gamepad/android/java/src/org/chromium/device/gamepad/GamepadList.java index 0991f38be92..eb1b9862179 100644 --- a/chromium/device/gamepad/android/java/src/org/chromium/device/gamepad/GamepadList.java +++ b/chromium/device/gamepad/android/java/src/org/chromium/device/gamepad/GamepadList.java @@ -16,6 +16,7 @@ import android.view.MotionEvent; import org.chromium.base.ThreadUtils; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.NativeMethods; /** * Class to manage connected gamepad devices list. @@ -288,11 +289,12 @@ public class GamepadList { final GamepadDevice device = getDevice(i); if (device != null) { device.updateButtonsAndAxesMapping(); - nativeSetGamepadData(webGamepadsPtr, i, device.isStandardGamepad(), true, - device.getName(), device.getTimestamp(), device.getAxes(), - device.getButtons()); + GamepadListJni.get().setGamepadData(GamepadList.this, webGamepadsPtr, i, + device.isStandardGamepad(), true, device.getName(), + device.getTimestamp(), device.getAxes(), device.getButtons()); } else { - nativeSetGamepadData(webGamepadsPtr, i, false, false, null, 0, null, null); + GamepadListJni.get().setGamepadData( + GamepadList.this, webGamepadsPtr, i, false, false, null, 0, null, null); } } } @@ -316,10 +318,14 @@ public class GamepadList { } } - private native void nativeSetGamepadData(long webGamepadsPtr, int index, boolean mapping, - boolean connected, String devicename, long timestamp, float[] axes, float[] buttons); - private static class LazyHolder { private static final GamepadList INSTANCE = new GamepadList(); } + + @NativeMethods + interface Natives { + void setGamepadData(GamepadList caller, long webGamepadsPtr, int index, boolean mapping, + boolean connected, String devicename, long timestamp, float[] axes, + float[] buttons); + } } diff --git a/chromium/device/gamepad/android/junit/src/org/chromium/device/gamepad/GamepadMappingsTest.java b/chromium/device/gamepad/android/junit/src/org/chromium/device/gamepad/GamepadMappingsTest.java index bb5a8284c3a..599f9d24afa 100644 --- a/chromium/device/gamepad/android/junit/src/org/chromium/device/gamepad/GamepadMappingsTest.java +++ b/chromium/device/gamepad/android/junit/src/org/chromium/device/gamepad/GamepadMappingsTest.java @@ -52,7 +52,7 @@ public class GamepadMappingsTest { private float[] mRawAxes = new float[GamepadDevice.MAX_RAW_AXIS_VALUES]; @Before - public void setUp() throws Exception { + public void setUp() { // By default, we expect every button and axis to be mapped. mUnmappedButtons.clear(); mUnmappedAxes.clear(); @@ -72,7 +72,7 @@ public class GamepadMappingsTest { @Test @Feature({"Gamepad"}) - public void testShieldGamepadMappings() throws Exception { + public void testShieldGamepadMappings() { GamepadMappings mappings = GamepadMappings.getMappings(GamepadMappings.NVIDIA_SHIELD_DEVICE_NAME_PREFIX); mappings.mapToStandardGamepad(mMappedAxes, mMappedButtons, mRawAxes, mRawButtons); @@ -82,7 +82,7 @@ public class GamepadMappingsTest { @Test @Feature({"Gamepad"}) - public void testXBox360GamepadMappings() throws Exception { + public void testXBox360GamepadMappings() { GamepadMappings mappings = GamepadMappings.getMappings(GamepadMappings.MICROSOFT_XBOX_PAD_DEVICE_NAME); mappings.mapToStandardGamepad(mMappedAxes, mMappedButtons, mRawAxes, mRawButtons); @@ -92,7 +92,7 @@ public class GamepadMappingsTest { @Test @Feature({"Gamepad"}) - public void testPS3SixAxisGamepadMappings() throws Exception { + public void testPS3SixAxisGamepadMappings() { GamepadMappings mappings = GamepadMappings.getMappings(GamepadMappings.PS3_SIXAXIS_DEVICE_NAME); mappings.mapToStandardGamepad(mMappedAxes, mMappedButtons, mRawAxes, mRawButtons); @@ -119,7 +119,7 @@ public class GamepadMappingsTest { @Test @Feature({"Gamepad"}) - public void testSamsungEIGP20GamepadMappings() throws Exception { + public void testSamsungEIGP20GamepadMappings() { GamepadMappings mappings = GamepadMappings.getMappings(GamepadMappings.SAMSUNG_EI_GP20_DEVICE_NAME); mappings.mapToStandardGamepad(mMappedAxes, mMappedButtons, mRawAxes, mRawButtons); @@ -138,7 +138,7 @@ public class GamepadMappingsTest { @Test @Feature({"Gamepad"}) - public void testAmazonFireGamepadMappings() throws Exception { + public void testAmazonFireGamepadMappings() { GamepadMappings mappings = GamepadMappings.getMappings(GamepadMappings.AMAZON_FIRE_DEVICE_NAME); mappings.mapToStandardGamepad(mMappedAxes, mMappedButtons, mRawAxes, mRawButtons); @@ -157,7 +157,7 @@ public class GamepadMappingsTest { @Test @Feature({"Gamepad"}) - public void testUnknownXBox360GamepadMappings() throws Exception { + public void testUnknownXBox360GamepadMappings() { int[] axes = new int[] { MotionEvent.AXIS_X, MotionEvent.AXIS_Y, @@ -186,7 +186,7 @@ public class GamepadMappingsTest { @Test @Feature({"Gamepad"}) - public void testUnknownMogaProGamepadMappings() throws Exception { + public void testUnknownMogaProGamepadMappings() { int[] axes = new int[] { MotionEvent.AXIS_X, MotionEvent.AXIS_Y, @@ -215,7 +215,7 @@ public class GamepadMappingsTest { @Test @Feature({"Gamepad"}) - public void testUnknownXiaomiGamepadMappings() throws Exception { + public void testUnknownXiaomiGamepadMappings() { int[] axes = new int[] { MotionEvent.AXIS_X, MotionEvent.AXIS_Y, @@ -244,7 +244,7 @@ public class GamepadMappingsTest { @Test @Feature({"Gamepad"}) - public void testUnknownGpdXdGamepadMappings() throws Exception { + public void testUnknownGpdXdGamepadMappings() { int[] axes = new int[] { MotionEvent.AXIS_X, MotionEvent.AXIS_Y, @@ -270,7 +270,7 @@ public class GamepadMappingsTest { @Test @Ignore("https://crbug.com/719765") @Feature({"Gamepad"}) - public void testPS4GamepadMappings() throws Exception { + public void testPS4GamepadMappings() { GamepadMappings mappings = GamepadMappings.getMappings(GamepadMappings.PS_DUALSHOCK_4_PRODUCT_ID, GamepadMappings.PS_DUALSHOCK_4_VENDOR_ID); @@ -306,7 +306,7 @@ public class GamepadMappingsTest { @Test @Feature({"Gamepad"}) - public void testXboxOneSBluetooth2016FirmwareMappings() throws Exception { + public void testXboxOneSBluetooth2016FirmwareMappings() { if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { GamepadMappings mappings = GamepadMappings.getMappings(GamepadMappings.XBOX_ONE_S_2016_FIRMWARE_PRODUCT_ID, @@ -356,7 +356,7 @@ public class GamepadMappingsTest { @Test @Feature({"Gamepad"}) - public void testXboxOneSBluetoothUsesDefaultMappings() throws Exception { + public void testXboxOneSBluetoothUsesDefaultMappings() { // Test that Xbox One S gamepads with updated firmware connected over Bluetooth use the // default mapping. if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { diff --git a/chromium/device/gamepad/gamepad_data_fetcher.cc b/chromium/device/gamepad/gamepad_data_fetcher.cc index a33216dfad0..8c64640164e 100644 --- a/chromium/device/gamepad/gamepad_data_fetcher.cc +++ b/chromium/device/gamepad/gamepad_data_fetcher.cc @@ -19,14 +19,17 @@ void RunCallbackOnCallbackThread( } } // namespace -GamepadDataFetcher::GamepadDataFetcher() : provider_(nullptr) {} +GamepadDataFetcher::GamepadDataFetcher() = default; GamepadDataFetcher::~GamepadDataFetcher() = default; -void GamepadDataFetcher::InitializeProvider(GamepadPadStateProvider* provider) { +void GamepadDataFetcher::InitializeProvider( + GamepadPadStateProvider* provider, + service_manager::Connector* service_manager_connector) { DCHECK(provider); provider_ = provider; + service_manager_connector_ = service_manager_connector; OnAddedToProvider(); } @@ -48,6 +51,10 @@ void GamepadDataFetcher::ResetVibration( mojom::GamepadHapticsResult::GamepadHapticsResultError); } +bool GamepadDataFetcher::DisconnectUnrecognizedGamepad(int source_id) { + return false; +} + // static int64_t GamepadDataFetcher::TimeInMicroseconds(base::TimeTicks update_time) { return update_time.since_origin().InMicroseconds(); diff --git a/chromium/device/gamepad/gamepad_data_fetcher.h b/chromium/device/gamepad/gamepad_data_fetcher.h index 500e9cdd3b8..bb062002349 100644 --- a/chromium/device/gamepad/gamepad_data_fetcher.h +++ b/chromium/device/gamepad/gamepad_data_fetcher.h @@ -12,9 +12,13 @@ #include "device/gamepad/public/cpp/gamepad.h" #include "device/gamepad/public/mojom/gamepad.mojom.h" +namespace service_manager { +class Connector; +} // namespace service_manager + namespace device { -// Abstract interface for imlementing platform- (and test-) specific behavior +// Abstract interface for implementing platform- (and test-) specific behavior // for getting the gamepad data. class DEVICE_GAMEPAD_EXPORT GamepadDataFetcher { public: @@ -34,13 +38,24 @@ class DEVICE_GAMEPAD_EXPORT GamepadDataFetcher { scoped_refptr<base::SequencedTaskRunner>); virtual GamepadSource source() = 0; + + // Shuts down the gamepad with given |source_id| and removes it from the data + // fetchers list of devices. Default implementation used on data fetchers for + // recognized gamepads because it should never be called on those gamepads. + // Returns a boolean that is true if the gamepad was successfully + // disconnected. + virtual bool DisconnectUnrecognizedGamepad(int source_id); + GamepadPadStateProvider* provider() { return provider_; } + service_manager::Connector* connector() const { + return service_manager_connector_; + } - PadState* GetPadState(int source_id) { + PadState* GetPadState(int source_id, bool new_pad_recognized = true) { if (!provider_) return nullptr; - return provider_->GetPadState(source(), source_id); + return provider_->GetPadState(source(), source_id, new_pad_recognized); } // Returns the current time value in microseconds. Data fetchers should use @@ -69,7 +84,9 @@ class DEVICE_GAMEPAD_EXPORT GamepadDataFetcher { friend GamepadPadStateProvider; // To be called by the GamepadPadStateProvider on the polling thread; - void InitializeProvider(GamepadPadStateProvider* provider); + void InitializeProvider( + GamepadPadStateProvider* provider, + service_manager::Connector* service_manager_connector); // This call will happen on the gamepad polling thread. Any initialization // that needs to happen on that thread should be done here, not in the @@ -77,7 +94,13 @@ class DEVICE_GAMEPAD_EXPORT GamepadDataFetcher { virtual void OnAddedToProvider() {} private: - GamepadPadStateProvider* provider_; + // GamepadPadStateProvider is the base class of GamepadProvider, which owns + // this data fetcher. + GamepadPadStateProvider* provider_ = nullptr; + + // The service manager connector is owned by the provider, which destroys the + // data fetcher prior to destroying the connector. + service_manager::Connector* service_manager_connector_ = nullptr; }; // Factory class for creating a GamepadDataFetcher. Used by the diff --git a/chromium/device/gamepad/gamepad_haptics_manager.cc b/chromium/device/gamepad/gamepad_haptics_manager.cc index 8171275c221..abdbc4e8108 100644 --- a/chromium/device/gamepad/gamepad_haptics_manager.cc +++ b/chromium/device/gamepad/gamepad_haptics_manager.cc @@ -8,7 +8,7 @@ #include "base/memory/ptr_util.h" #include "device/gamepad/gamepad_service.h" -#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/cpp/bindings/self_owned_receiver.h" namespace device { @@ -18,9 +18,9 @@ GamepadHapticsManager::~GamepadHapticsManager() = default; // static void GamepadHapticsManager::Create( - mojom::GamepadHapticsManagerRequest request) { - mojo::MakeStrongBinding(std::make_unique<GamepadHapticsManager>(), - std::move(request)); + mojo::PendingReceiver<mojom::GamepadHapticsManager> receiver) { + mojo::MakeSelfOwnedReceiver(std::make_unique<GamepadHapticsManager>(), + std::move(receiver)); } void GamepadHapticsManager::PlayVibrationEffectOnce( diff --git a/chromium/device/gamepad/gamepad_haptics_manager.h b/chromium/device/gamepad/gamepad_haptics_manager.h index a95d65691d2..7e7d8e30735 100644 --- a/chromium/device/gamepad/gamepad_haptics_manager.h +++ b/chromium/device/gamepad/gamepad_haptics_manager.h @@ -17,7 +17,8 @@ class DEVICE_GAMEPAD_EXPORT GamepadHapticsManager GamepadHapticsManager(); ~GamepadHapticsManager() override; - static void Create(mojom::GamepadHapticsManagerRequest request); + static void Create( + mojo::PendingReceiver<mojom::GamepadHapticsManager> receiver); // mojom::GamepadHapticsManager implementation. void PlayVibrationEffectOnce(uint32_t pad_index, diff --git a/chromium/device/gamepad/gamepad_monitor.cc b/chromium/device/gamepad/gamepad_monitor.cc index 00222ec0d41..a4e24a81fdf 100644 --- a/chromium/device/gamepad/gamepad_monitor.cc +++ b/chromium/device/gamepad/gamepad_monitor.cc @@ -15,17 +15,18 @@ namespace device { -GamepadMonitor::GamepadMonitor() : is_started_(false) {} +GamepadMonitor::GamepadMonitor() = default; GamepadMonitor::~GamepadMonitor() { - if (is_started_) + if (is_registered_consumer_) GamepadService::GetInstance()->RemoveConsumer(this); } // static -void GamepadMonitor::Create(mojom::GamepadMonitorRequest request) { - mojo::MakeStrongBinding(std::make_unique<GamepadMonitor>(), - std::move(request)); +void GamepadMonitor::Create( + mojo::PendingReceiver<mojom::GamepadMonitor> receiver) { + mojo::MakeSelfOwnedReceiver(std::make_unique<GamepadMonitor>(), + std::move(receiver)); } void GamepadMonitor::OnGamepadConnected(uint32_t index, @@ -49,6 +50,7 @@ void GamepadMonitor::OnGamepadButtonOrAxisChanged(uint32_t index, void GamepadMonitor::GamepadStartPolling(GamepadStartPollingCallback callback) { DCHECK(!is_started_); is_started_ = true; + is_registered_consumer_ = true; GamepadService* service = GamepadService::GetInstance(); if (!service->ConsumerBecameActive(this)) { diff --git a/chromium/device/gamepad/gamepad_monitor.h b/chromium/device/gamepad/gamepad_monitor.h index ce597e25872..9092f8c258c 100644 --- a/chromium/device/gamepad/gamepad_monitor.h +++ b/chromium/device/gamepad/gamepad_monitor.h @@ -10,6 +10,7 @@ #include "device/gamepad/gamepad_consumer.h" #include "device/gamepad/gamepad_export.h" #include "device/gamepad/public/mojom/gamepad.mojom.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/remote.h" namespace device { @@ -20,7 +21,7 @@ class DEVICE_GAMEPAD_EXPORT GamepadMonitor : public GamepadConsumer, GamepadMonitor(); ~GamepadMonitor() override; - static void Create(mojom::GamepadMonitorRequest request); + static void Create(mojo::PendingReceiver<mojom::GamepadMonitor> receiver); // GamepadConsumer implementation. void OnGamepadConnected(uint32_t index, const Gamepad& gamepad) override; @@ -36,7 +37,12 @@ class DEVICE_GAMEPAD_EXPORT GamepadMonitor : public GamepadConsumer, private: mojo::Remote<mojom::GamepadObserver> gamepad_observer_remote_; - bool is_started_; + + // True if this monitor is an active gamepad consumer. + bool is_started_ = false; + + // True if this monitor has been registered with the gamepad service. + bool is_registered_consumer_ = false; DISALLOW_COPY_AND_ASSIGN(GamepadMonitor); }; diff --git a/chromium/device/gamepad/gamepad_pad_state_provider.cc b/chromium/device/gamepad/gamepad_pad_state_provider.cc index 11f1f37e4bd..fcebacae728 100644 --- a/chromium/device/gamepad/gamepad_pad_state_provider.cc +++ b/chromium/device/gamepad/gamepad_pad_state_provider.cc @@ -17,6 +17,9 @@ const float kMinAxisResetValue = 0.1f; } // namespace +PadState::PadState() = default; +PadState::~PadState() = default; + GamepadPadStateProvider::GamepadPadStateProvider() { pad_states_.reset(new PadState[Gamepads::kItemsLengthCap]); @@ -27,9 +30,11 @@ GamepadPadStateProvider::GamepadPadStateProvider() { GamepadPadStateProvider::~GamepadPadStateProvider() = default; PadState* GamepadPadStateProvider::GetPadState(GamepadSource source, - int source_id) { + int source_id, + bool new_gamepad_recognized) { // Check to see if the device already has a reserved slot PadState* empty_slot = nullptr; + PadState* unrecognized_slot = nullptr; for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i) { PadState& state = pad_states_.get()[i]; if (state.source == source && state.source_id == source_id) { @@ -39,6 +44,14 @@ PadState* GamepadPadStateProvider::GetPadState(GamepadSource source, } if (!empty_slot && state.source == GAMEPAD_SOURCE_NONE) empty_slot = &state; + if (!state.is_recognized) + unrecognized_slot = &state; + } + + if (!empty_slot && unrecognized_slot && new_gamepad_recognized) { + DisconnectUnrecognizedGamepad(unrecognized_slot->source, + unrecognized_slot->source_id); + empty_slot = unrecognized_slot; } if (empty_slot) { empty_slot->source = source; @@ -46,6 +59,7 @@ PadState* GamepadPadStateProvider::GetPadState(GamepadSource source, empty_slot->is_active = true; empty_slot->is_newly_active = true; empty_slot->is_initialized = false; + empty_slot->is_recognized = new_gamepad_recognized; } return empty_slot; } @@ -66,8 +80,9 @@ void GamepadPadStateProvider::ClearPadState(PadState& state) { } void GamepadPadStateProvider::InitializeDataFetcher( - GamepadDataFetcher* fetcher) { - fetcher->InitializeProvider(this); + GamepadDataFetcher* fetcher, + service_manager::Connector* service_manager_connector) { + fetcher->InitializeProvider(this, service_manager_connector); } void GamepadPadStateProvider::MapAndSanitizeGamepadData(PadState* pad_state, diff --git a/chromium/device/gamepad/gamepad_pad_state_provider.h b/chromium/device/gamepad/gamepad_pad_state_provider.h index 621072cbda0..2ab4848b423 100644 --- a/chromium/device/gamepad/gamepad_pad_state_provider.h +++ b/chromium/device/gamepad/gamepad_pad_state_provider.h @@ -14,6 +14,10 @@ #include "device/gamepad/gamepad_standard_mappings.h" #include "device/gamepad/public/cpp/gamepad.h" +namespace service_manager { +class Connector; +} // namespace service_manager + namespace device { class GamepadDataFetcher; @@ -42,6 +46,9 @@ enum GamepadSource { }; struct PadState { + PadState(); + ~PadState(); + // Which data fetcher provided this gamepad's data. GamepadSource source; // Data fetcher-specific identifier for this gamepad. @@ -60,6 +67,11 @@ struct PadState { // gamepad has been completed. bool is_initialized; + // Set by the data fetcher to indicate whether this gamepad's ids are + // recognized as a specific gamepad. It is then used to prioritize recognized + // gamepads when finding an empty slot for any new gamepads when activated. + bool is_recognized; + // Gamepad data, unmapped. Gamepad data; @@ -92,8 +104,12 @@ class DEVICE_GAMEPAD_EXPORT GamepadPadStateProvider { // Gets a PadState object for the given source and id. If the device hasn't // been encountered before one of the remaining slots will be reserved for it. - // If no slots are available will return NULL. - PadState* GetPadState(GamepadSource source, int source_id); + // If no slots are available this returns nullptr. However, if one of those + // slots contains an unrecognized gamepad and |new_gamepad_recognized| is true + // that slot will be reset and returned. + PadState* GetPadState(GamepadSource source, + int source_id, + bool new_gamepad_recognized); // Gets a PadState object for a connected gamepad by specifying its index in // the pad_states_ array. Returns NULL if there is no connected gamepad at @@ -103,7 +119,9 @@ class DEVICE_GAMEPAD_EXPORT GamepadPadStateProvider { protected: void ClearPadState(PadState& state); - void InitializeDataFetcher(GamepadDataFetcher* fetcher); + void InitializeDataFetcher( + GamepadDataFetcher* fetcher, + service_manager::Connector* service_manager_connector); void MapAndSanitizeGamepadData(PadState* pad_state, Gamepad* pad, @@ -111,6 +129,13 @@ class DEVICE_GAMEPAD_EXPORT GamepadPadStateProvider { // Tracks the state of each gamepad slot. std::unique_ptr<PadState[]> pad_states_; + + private: + // Calls the DisconnectUnrecognizedGamepad method on the data fetcher + // associated with the given |source|. The actual implementation is always + // in the |gamepad_provider|. + virtual void DisconnectUnrecognizedGamepad(GamepadSource source, + int source_id) = 0; }; } // namespace device diff --git a/chromium/device/gamepad/gamepad_platform_data_fetcher_linux.cc b/chromium/device/gamepad/gamepad_platform_data_fetcher_linux.cc index 64239e65e93..f0246cf5814 100644 --- a/chromium/device/gamepad/gamepad_platform_data_fetcher_linux.cc +++ b/chromium/device/gamepad/gamepad_platform_data_fetcher_linux.cc @@ -106,7 +106,7 @@ void GamepadPlatformDataFetcherLinux::RefreshDevice(udev_device* dev) { if (pad_info.type == UdevGamepadLinux::Type::JOYDEV) { // If |syspath_prefix| is empty, the device was already disconnected. if (pad_info.syspath_prefix.empty()) - RemoveDeviceAtIndex(pad_info.index); + DisconnectUnrecognizedGamepad(pad_info.index); else RefreshJoydevDevice(dev, pad_info); } else if (pad_info.type == UdevGamepadLinux::Type::EVDEV) { @@ -138,15 +138,16 @@ void GamepadPlatformDataFetcherLinux::RemoveDevice(GamepadDeviceLinux* device) { } } -void GamepadPlatformDataFetcherLinux::RemoveDeviceAtIndex(int index) { +bool GamepadPlatformDataFetcherLinux::DisconnectUnrecognizedGamepad(int index) { for (auto it = devices_.begin(); it != devices_.end(); ++it) { const auto& device = *it; if (device->GetJoydevIndex() == index) { device->Shutdown(); devices_.erase(it); - return; + return true; } } + return false; } GamepadDeviceLinux* GamepadPlatformDataFetcherLinux::GetOrCreateMatchingDevice( @@ -195,7 +196,10 @@ void GamepadPlatformDataFetcherLinux::RefreshJoydevDevice( return; } - PadState* state = GetPadState(joydev_index); + bool is_recognized = GamepadIdList::Get().GetGamepadId( + vendor_id, product_id) != GamepadId::kUnknownGamepad; + + PadState* state = GetPadState(joydev_index, is_recognized); if (!state) { // No slot available for device, don't use. device->CloseJoydevNode(); @@ -217,12 +221,10 @@ void GamepadPlatformDataFetcherLinux::RefreshJoydevDevice( return; } - // Joydev uses its own internal list of device IDs to identify known gamepads. // If the device is on our list, record it by ID. If the device is unknown, // record that an unknown gamepad was enumerated. - GamepadId gamepad_id = - GamepadIdList::Get().GetGamepadId(vendor_id, product_id); - if (gamepad_id == GamepadId::kUnknownGamepad) + + if (is_recognized) RecordUnknownGamepad(source()); else RecordConnectedGamepad(vendor_id, product_id); diff --git a/chromium/device/gamepad/gamepad_platform_data_fetcher_linux.h b/chromium/device/gamepad/gamepad_platform_data_fetcher_linux.h index 8d88425ca81..78df7a12c63 100644 --- a/chromium/device/gamepad/gamepad_platform_data_fetcher_linux.h +++ b/chromium/device/gamepad/gamepad_platform_data_fetcher_linux.h @@ -48,6 +48,7 @@ class DEVICE_GAMEPAD_EXPORT GamepadPlatformDataFetcherLinux // GamepadDataFetcher implementation. void GetGamepadData(bool devices_changed_hint) override; + bool DisconnectUnrecognizedGamepad(int source_id) override; void PlayEffect(int pad_index, mojom::GamepadHapticEffectType, @@ -75,7 +76,6 @@ class DEVICE_GAMEPAD_EXPORT GamepadPlatformDataFetcherLinux GamepadDeviceLinux* GetOrCreateMatchingDevice( const UdevGamepadLinux& pad_info); void RemoveDevice(GamepadDeviceLinux* device); - void RemoveDeviceAtIndex(int index); // UdevWatcher::Observer overrides void OnDeviceAdded(ScopedUdevDevicePtr device) override; diff --git a/chromium/device/gamepad/gamepad_platform_data_fetcher_mac.h b/chromium/device/gamepad/gamepad_platform_data_fetcher_mac.h index df9df56c105..67b6c12ea24 100644 --- a/chromium/device/gamepad/gamepad_platform_data_fetcher_mac.h +++ b/chromium/device/gamepad/gamepad_platform_data_fetcher_mac.h @@ -66,18 +66,9 @@ class GamepadPlatformDataFetcherMac : public GamepadDataFetcher { // GamepadDataFetcher private implementation. void OnAddedToProvider() override; - // Returns the index of the first empty slot, or Gamepads::kItemsLengthCap if - // there are no empty slots. - size_t GetEmptySlot(); - - // Returns the index of the slot allocated for this device, or the first empty - // slot if none is yet allocated. If there is no allocated or empty slots, - // returns Gamepads::kItemsLengthCap. - size_t GetSlotForDevice(IOHIDDeviceRef device); - - // Returns the index of the slot allocated for the device with the specified - // |location_id|, or Gamepads::kItemsLengthCap if none is yet allocated. - size_t GetSlotForLocation(int location_id); + // Returns the GamepadDeviceMac from |devices_| that has the given device + // reference. Returns nullptr if the device is not in |devices_|. + GamepadDeviceMac* GetGamepadFromHidDevice(IOHIDDeviceRef device); // Query device info for |device| and add it to |devices_| if it is a // gamepad. @@ -96,11 +87,14 @@ class GamepadPlatformDataFetcherMac : public GamepadDataFetcher { // Unregister from connection events and value change notifications. void UnregisterFromNotifications(); + bool DisconnectUnrecognizedGamepad(int source_id) override; + bool enabled_ = false; bool paused_ = false; base::ScopedCFTypeRef<IOHIDManagerRef> hid_manager_ref_; - std::unique_ptr<GamepadDeviceMac> devices_[Gamepads::kItemsLengthCap]; + // A map of all devices using this data fetcher with the source_id as the key. + std::unordered_map<int, std::unique_ptr<GamepadDeviceMac>> devices_; DISALLOW_COPY_AND_ASSIGN(GamepadPlatformDataFetcherMac); }; diff --git a/chromium/device/gamepad/gamepad_platform_data_fetcher_mac.mm b/chromium/device/gamepad/gamepad_platform_data_fetcher_mac.mm index 391419fc9c0..4475b7e21ca 100644 --- a/chromium/device/gamepad/gamepad_platform_data_fetcher_mac.mm +++ b/chromium/device/gamepad/gamepad_platform_data_fetcher_mac.mm @@ -102,9 +102,8 @@ void GamepadPlatformDataFetcherMac::PauseHint(bool pause) { GamepadPlatformDataFetcherMac::~GamepadPlatformDataFetcherMac() { UnregisterFromNotifications(); - for (size_t slot = 0; slot < Gamepads::kItemsLengthCap; ++slot) { - if (devices_[slot] != nullptr) - devices_[slot]->Shutdown(); + for (auto& iter : devices_) { + iter.second->Shutdown(); } } @@ -134,31 +133,15 @@ void GamepadPlatformDataFetcherMac::ValueChangedCallback(void* context, InstanceFromContext(context)->ValueChanged(ref); } -size_t GamepadPlatformDataFetcherMac::GetEmptySlot() { - // Find a free slot for this device. - for (size_t slot = 0; slot < Gamepads::kItemsLengthCap; ++slot) { - if (devices_[slot] == nullptr) - return slot; +GamepadDeviceMac* GamepadPlatformDataFetcherMac::GetGamepadFromHidDevice( + IOHIDDeviceRef device) { + for (auto& iter : devices_) { + if (iter.second->IsSameDevice(device)) { + return iter.second.get(); + } } - return Gamepads::kItemsLengthCap; -} -size_t GamepadPlatformDataFetcherMac::GetSlotForDevice(IOHIDDeviceRef device) { - for (size_t slot = 0; slot < Gamepads::kItemsLengthCap; ++slot) { - // If we already have this device, and it's already connected, don't do - // anything now. - if (devices_[slot] != nullptr && devices_[slot]->IsSameDevice(device)) - return Gamepads::kItemsLengthCap; - } - return GetEmptySlot(); -} - -size_t GamepadPlatformDataFetcherMac::GetSlotForLocation(int location_id) { - for (size_t slot = 0; slot < Gamepads::kItemsLengthCap; ++slot) { - if (devices_[slot] && devices_[slot]->GetLocationId() == location_id) - return slot; - } - return Gamepads::kItemsLengthCap; + return nullptr; } void GamepadPlatformDataFetcherMac::DeviceAdd(IOHIDDeviceRef device) { @@ -172,9 +155,6 @@ void GamepadPlatformDataFetcherMac::DeviceAdd(IOHIDDeviceRef device) { IOHIDDeviceGetProperty(device, CFSTR(kIOHIDLocationIDKey)))); int location_int = [location_id intValue]; - // Find an index for this device. - size_t slot = GetSlotForDevice(device); - NSNumber* vendor_id = CFToNSCast(CFCastStrict<CFNumberRef>( IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)))); NSNumber* product_id = CFToNSCast(CFCastStrict<CFNumberRef>( @@ -201,21 +181,23 @@ void GamepadPlatformDataFetcherMac::DeviceAdd(IOHIDDeviceRef device) { const auto& gamepad_id_list = GamepadIdList::Get(); DCHECK_EQ(kXInputTypeNone, gamepad_id_list.GetXInputType(vendor_int, product_int)); - RecordConnectedGamepad(vendor_int, product_int); - // We can't handle this many connected devices. - if (slot == Gamepads::kItemsLengthCap) + if (devices_.find(location_int) != devices_.end()) return; + RecordConnectedGamepad(vendor_int, product_int); + // The SteelSeries Nimbus and other Made for iOS gamepads should be handled - // through the GameController interface. Blacklist it here so it doesn't - // take up an additional gamepad slot. + // through the GameController interface. if (gamepad_id_list.GetGamepadId(vendor_int, product_int) == GamepadId::kSteelSeriesProduct1420) { return; } - PadState* state = GetPadState(location_int); + bool is_recognized = gamepad_id_list.GetGamepadId(vendor_int, product_int) != + GamepadId::kUnknownGamepad; + + PadState* state = GetPadState(location_int, is_recognized); if (!state) return; // No available slot for this device @@ -232,34 +214,42 @@ void GamepadPlatformDataFetcherMac::DeviceAdd(IOHIDDeviceRef device) { state->data.mapping = state->mapper ? GamepadMapping::kStandard : GamepadMapping::kNone; - devices_[slot] = std::make_unique<GamepadDeviceMac>(location_int, device, - vendor_int, product_int); - if (!devices_[slot]->AddButtonsAndAxes(&state->data)) { - devices_[slot]->Shutdown(); - devices_[slot] = nullptr; + auto new_device = std::make_unique<GamepadDeviceMac>(location_int, device, + vendor_int, product_int); + if (!new_device->AddButtonsAndAxes(&state->data)) { + new_device->Shutdown(); return; } state->data.vibration_actuator.type = GamepadHapticActuatorType::kDualRumble; - state->data.vibration_actuator.not_null = devices_[slot]->SupportsVibration(); + state->data.vibration_actuator.not_null = new_device->SupportsVibration(); state->data.connected = true; + + devices_.emplace(location_int, std::move(new_device)); +} + +bool GamepadPlatformDataFetcherMac::DisconnectUnrecognizedGamepad( + int source_id) { + auto gamepad_iter = devices_.find(source_id); + if (gamepad_iter == devices_.end()) + return false; + gamepad_iter->second->Shutdown(); + devices_.erase(gamepad_iter); + return true; } void GamepadPlatformDataFetcherMac::DeviceRemove(IOHIDDeviceRef device) { if (!enabled_) return; - // Find the index for this device. - size_t slot; - for (slot = 0; slot < Gamepads::kItemsLengthCap; ++slot) { - if (devices_[slot] != nullptr && devices_[slot]->IsSameDevice(device)) - break; - } - if (slot < Gamepads::kItemsLengthCap) { - devices_[slot]->Shutdown(); - devices_[slot] = nullptr; - } + GamepadDeviceMac* gamepad_device = GetGamepadFromHidDevice(device); + + if (!gamepad_device) + return; + + gamepad_device->Shutdown(); + devices_.erase(gamepad_device->GetLocationId()); } void GamepadPlatformDataFetcherMac::ValueChanged(IOHIDValueRef value) { @@ -269,20 +259,16 @@ void GamepadPlatformDataFetcherMac::ValueChanged(IOHIDValueRef value) { IOHIDElementRef element = IOHIDValueGetElement(value); IOHIDDeviceRef device = IOHIDElementGetDevice(element); - // Find device slot. - size_t slot; - for (slot = 0; slot < Gamepads::kItemsLengthCap; ++slot) { - if (devices_[slot] != nullptr && devices_[slot]->IsSameDevice(device)) - break; - } - if (slot == Gamepads::kItemsLengthCap) + GamepadDeviceMac* gamepad_device = GetGamepadFromHidDevice(device); + + if (!gamepad_device) return; - PadState* state = GetPadState(devices_[slot]->GetLocationId()); + PadState* state = GetPadState(gamepad_device->GetLocationId()); if (!state) return; - devices_[slot]->UpdateGamepadForValue(value, &state->data); + gamepad_device->UpdateGamepadForValue(value, &state->data); } void GamepadPlatformDataFetcherMac::GetGamepadData(bool) { @@ -290,9 +276,8 @@ void GamepadPlatformDataFetcherMac::GetGamepadData(bool) { return; // Loop through and GetPadState to indicate the devices are still connected. - for (size_t slot = 0; slot < Gamepads::kItemsLengthCap; ++slot) { - if (devices_[slot]) - GetPadState(devices_[slot]->GetLocationId()); + for (const auto& iter : devices_) { + GetPadState(iter.first); } } @@ -302,8 +287,8 @@ void GamepadPlatformDataFetcherMac::PlayEffect( mojom::GamepadEffectParametersPtr params, mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback callback, scoped_refptr<base::SequencedTaskRunner> callback_runner) { - size_t slot = GetSlotForLocation(source_id); - if (slot == Gamepads::kItemsLengthCap) { + auto device_iter = devices_.find(source_id); + if (device_iter == devices_.end()) { // No connected gamepad with this location. Probably the effect was issued // while the gamepad was still connected, so handle this as if it were // preempted by a disconnect. @@ -312,16 +297,16 @@ void GamepadPlatformDataFetcherMac::PlayEffect( mojom::GamepadHapticsResult::GamepadHapticsResultPreempted); return; } - devices_[slot]->PlayEffect(type, std::move(params), std::move(callback), - std::move(callback_runner)); + device_iter->second->PlayEffect(type, std::move(params), std::move(callback), + std::move(callback_runner)); } void GamepadPlatformDataFetcherMac::ResetVibration( int source_id, mojom::GamepadHapticsManager::ResetVibrationActuatorCallback callback, scoped_refptr<base::SequencedTaskRunner> callback_runner) { - size_t slot = GetSlotForLocation(source_id); - if (slot == Gamepads::kItemsLengthCap) { + auto device_iter = devices_.find(source_id); + if (device_iter == devices_.end()) { // No connected gamepad with this location. Since the gamepad is already // disconnected, allow the reset to report success. RunVibrationCallback( @@ -329,8 +314,8 @@ void GamepadPlatformDataFetcherMac::ResetVibration( mojom::GamepadHapticsResult::GamepadHapticsResultComplete); return; } - devices_[slot]->ResetVibration(std::move(callback), - std::move(callback_runner)); + device_iter->second->ResetVibration(std::move(callback), + std::move(callback_runner)); } } // namespace device diff --git a/chromium/device/gamepad/gamepad_platform_data_fetcher_win.cc b/chromium/device/gamepad/gamepad_platform_data_fetcher_win.cc index e475eb14003..1367ea1f3da 100644 --- a/chromium/device/gamepad/gamepad_platform_data_fetcher_win.cc +++ b/chromium/device/gamepad/gamepad_platform_data_fetcher_win.cc @@ -171,7 +171,7 @@ void GamepadPlatformDataFetcherWin::GetGamepadData(bool devices_changed_hint) { } void GamepadPlatformDataFetcherWin::GetXInputPadData(int i) { - PadState* pad_state = provider()->GetPadState(GAMEPAD_SOURCE_WIN_XINPUT, i); + PadState* pad_state = GetPadState(i); if (!pad_state) return; diff --git a/chromium/device/gamepad/gamepad_provider.cc b/chromium/device/gamepad/gamepad_provider.cc index 2e082543b17..ea301b8a91a 100644 --- a/chromium/device/gamepad/gamepad_provider.cc +++ b/chromium/device/gamepad/gamepad_provider.cc @@ -26,6 +26,7 @@ #include "device/gamepad/gamepad_user_gesture.h" #include "device/gamepad/public/cpp/gamepad_features.h" #include "mojo/public/cpp/system/platform_handle.h" +#include "services/service_manager/public/cpp/connector.h" namespace device { @@ -40,29 +41,23 @@ GamepadProvider::ClosureAndThread::ClosureAndThread( GamepadProvider::ClosureAndThread::~ClosureAndThread() = default; GamepadProvider::GamepadProvider( - GamepadConnectionChangeClient* connection_change_client) - : is_paused_(true), - have_scheduled_do_poll_(false), - devices_changed_(true), - ever_had_user_gesture_(false), - sanitize_(true), - gamepad_shared_buffer_(std::make_unique<GamepadSharedBuffer>()), - connection_change_client_(connection_change_client) { + GamepadConnectionChangeClient* connection_change_client, + std::unique_ptr<service_manager::Connector> service_manager_connector) + : gamepad_shared_buffer_(std::make_unique<GamepadSharedBuffer>()), + connection_change_client_(connection_change_client), + service_manager_connector_(std::move(service_manager_connector)) { Initialize(std::unique_ptr<GamepadDataFetcher>()); } GamepadProvider::GamepadProvider( GamepadConnectionChangeClient* connection_change_client, + std::unique_ptr<service_manager::Connector> service_manager_connector, std::unique_ptr<GamepadDataFetcher> fetcher, std::unique_ptr<base::Thread> polling_thread) - : is_paused_(true), - have_scheduled_do_poll_(false), - devices_changed_(true), - ever_had_user_gesture_(false), - sanitize_(true), - gamepad_shared_buffer_(std::make_unique<GamepadSharedBuffer>()), + : gamepad_shared_buffer_(std::make_unique<GamepadSharedBuffer>()), polling_thread_(std::move(polling_thread)), - connection_change_client_(connection_change_client) { + connection_change_client_(connection_change_client), + service_manager_connector_(std::move(service_manager_connector)) { Initialize(std::move(fetcher)); } @@ -80,6 +75,11 @@ GamepadProvider::~GamepadProvider() { FROM_HERE, base::BindOnce(&GamepadFetcherVector::clear, base::Unretained(&data_fetchers_))); + // The service manager connector is bound to the polling thread and must be + // destroyed on that thread. + polling_thread_->task_runner()->DeleteSoon( + FROM_HERE, std::move(service_manager_connector_)); + // Use Stop() to join the polling thread, as there may be pending callbacks // which dereference |polling_thread_|. polling_thread_->Stop(); @@ -274,7 +274,7 @@ void GamepadProvider::DoAddGamepadDataFetcher( if (!fetcher) return; - InitializeDataFetcher(fetcher.get()); + InitializeDataFetcher(fetcher.get(), service_manager_connector_.get()); data_fetchers_.push_back(std::move(fetcher)); } @@ -385,6 +385,17 @@ void GamepadProvider::DoPoll() { ScheduleDoPoll(); } +void GamepadProvider::DisconnectUnrecognizedGamepad(GamepadSource source, + int source_id) { + for (auto& fetcher : data_fetchers_) { + if (fetcher->source() == source) { + bool disconnected = fetcher->DisconnectUnrecognizedGamepad(source_id); + DCHECK(disconnected); + return; + } + } +} + void GamepadProvider::ScheduleDoPoll() { DCHECK(polling_thread_->task_runner()->BelongsToCurrentThread()); if (have_scheduled_do_poll_) diff --git a/chromium/device/gamepad/gamepad_provider.h b/chromium/device/gamepad/gamepad_provider.h index 2319efb1a52..c1c555bf9b3 100644 --- a/chromium/device/gamepad/gamepad_provider.h +++ b/chromium/device/gamepad/gamepad_provider.h @@ -26,7 +26,11 @@ namespace base { class SingleThreadTaskRunner; class Thread; -} +} // namespace base + +namespace service_manager { +class Connector; +} // namespace service_manager namespace device { @@ -43,14 +47,17 @@ class DEVICE_GAMEPAD_EXPORT GamepadProvider : public GamepadPadStateProvider, public base::SystemMonitor::DevicesChangedObserver { public: - explicit GamepadProvider( - GamepadConnectionChangeClient* connection_change_client); + GamepadProvider( + GamepadConnectionChangeClient* connection_change_client, + std::unique_ptr<service_manager::Connector> service_manager_connector); // Manually specifies the data fetcher and polling thread. The polling thread // will be created normally if |polling_thread| is nullptr. Used for testing. - GamepadProvider(GamepadConnectionChangeClient* connection_change_client, - std::unique_ptr<GamepadDataFetcher> fetcher, - std::unique_ptr<base::Thread> polling_thread); + GamepadProvider( + GamepadConnectionChangeClient* connection_change_client, + std::unique_ptr<service_manager::Connector> service_manager_connector, + std::unique_ptr<GamepadDataFetcher> fetcher, + std::unique_ptr<base::Thread> polling_thread); ~GamepadProvider() override; @@ -115,6 +122,10 @@ class DEVICE_GAMEPAD_EXPORT GamepadProvider // true if any user gesture observers were notified. bool CheckForUserGesture(); + // GamepadPadStateProvider implementation. + void DisconnectUnrecognizedGamepad(GamepadSource source, + int source_id) override; + void PlayEffectOnPollingThread( uint32_t pad_index, mojom::GamepadHapticEffectType, @@ -133,12 +144,12 @@ class DEVICE_GAMEPAD_EXPORT GamepadProvider // Keeps track of when the background thread is paused. Access to is_paused_ // must be guarded by is_paused_lock_. base::Lock is_paused_lock_; - bool is_paused_; + bool is_paused_ = true; // Keep track of when a polling task is schedlued, so as to prevent us from // accidentally scheduling more than one at any time, when rapidly toggling // |is_paused_|. - bool have_scheduled_do_poll_; + bool have_scheduled_do_poll_ = false; // Lists all observers registered for user gestures, and the thread which // to issue the callbacks on. Since we always issue the callback on the @@ -164,10 +175,10 @@ class DEVICE_GAMEPAD_EXPORT GamepadProvider // tests. Access to devices_changed_ must be guarded by // devices_changed_lock_. base::Lock devices_changed_lock_; - bool devices_changed_; + bool devices_changed_ = true; - bool ever_had_user_gesture_; - bool sanitize_; + bool ever_had_user_gesture_ = false; + bool sanitize_ = true; // Only used on the polling thread. using GamepadFetcherVector = std::vector<std::unique_ptr<GamepadDataFetcher>>; @@ -181,6 +192,10 @@ class DEVICE_GAMEPAD_EXPORT GamepadProvider GamepadConnectionChangeClient* connection_change_client_; + // Service manager connector, to allow data fetchers to access the device + // service from the polling thread. + std::unique_ptr<service_manager::Connector> service_manager_connector_; + DISALLOW_COPY_AND_ASSIGN(GamepadProvider); }; diff --git a/chromium/device/gamepad/gamepad_provider_unittest.cc b/chromium/device/gamepad/gamepad_provider_unittest.cc index 2829c0e0cc7..2571b60a3e1 100644 --- a/chromium/device/gamepad/gamepad_provider_unittest.cc +++ b/chromium/device/gamepad/gamepad_provider_unittest.cc @@ -15,6 +15,7 @@ #include "build/build_config.h" #include "device/gamepad/gamepad_data_fetcher.h" #include "device/gamepad/gamepad_test_helpers.h" +#include "services/service_manager/public/cpp/connector.h" #include "testing/gtest/include/gtest/gtest.h" namespace device { @@ -44,10 +45,12 @@ class UserGestureListener { class GamepadProviderTest : public testing::Test, public GamepadTestHelper { public: GamepadProvider* CreateProvider(const Gamepads& test_data) { - mock_data_fetcher_ = new MockGamepadDataFetcher(test_data); - provider_.reset(new GamepadProvider( - nullptr, std::unique_ptr<GamepadDataFetcher>(mock_data_fetcher_), - std::unique_ptr<base::Thread>())); + auto fetcher = std::make_unique<MockGamepadDataFetcher>(test_data); + mock_data_fetcher_ = fetcher.get(); + provider_ = std::make_unique<GamepadProvider>( + /*connection_change_client=*/nullptr, + /*service_manager_connector=*/nullptr, std::move(fetcher), + /*polling_thread=*/nullptr); return provider_.get(); } diff --git a/chromium/device/gamepad/gamepad_service.cc b/chromium/device/gamepad/gamepad_service.cc index 320a082bef8..d3998dc5b4f 100644 --- a/chromium/device/gamepad/gamepad_service.cc +++ b/chromium/device/gamepad/gamepad_service.cc @@ -22,36 +22,33 @@ namespace device { namespace { -GamepadService* g_gamepad_service = 0; -} +GamepadService* g_gamepad_service = nullptr; +} // namespace GamepadService::GamepadService() - : main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), - num_active_consumers_(0), - gesture_callback_pending_(false) { + : main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()) { SetInstance(this); } -GamepadService::GamepadService( - std::unique_ptr<device::GamepadDataFetcher> fetcher) - : provider_(new device::GamepadProvider(this, - std::move(fetcher), - std::unique_ptr<base::Thread>())), - main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), - num_active_consumers_(0), - gesture_callback_pending_(false) { +GamepadService::GamepadService(std::unique_ptr<GamepadDataFetcher> fetcher) + : provider_(std::make_unique<GamepadProvider>( + /*connection_change_client=*/this, + /*service_manager_connector=*/nullptr, + std::move(fetcher), + /*polling_thread=*/nullptr)), + main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()) { SetInstance(this); } -GamepadService::~GamepadService() { - SetInstance(NULL); -} +GamepadService::~GamepadService() = default; void GamepadService::SetInstance(GamepadService* instance) { // Unit tests can create multiple instances but only one should exist at any - // given time so g_gamepad_service should only go from NULL to non-NULL and - // vica versa. + // given time so |g_gamepad_service| should only go from nullptr to + // non-nullptr and vice versa. CHECK(!!instance != !!g_gamepad_service); + if (g_gamepad_service) + delete g_gamepad_service; g_gamepad_service = instance; } @@ -72,15 +69,13 @@ void GamepadService::StartUp( GamepadDataFetcherManager::GetInstance(); } -service_manager::Connector* GamepadService::GetConnector() { - return service_manager_connector_.get(); -} - bool GamepadService::ConsumerBecameActive(GamepadConsumer* consumer) { DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); - if (!provider_) - provider_.reset(new device::GamepadProvider(this)); + if (!provider_) { + provider_ = std::make_unique<GamepadProvider>( + /*connection_change_client=*/this, service_manager_connector_->Clone()); + } std::pair<ConsumerSet::iterator, bool> insert_result = consumers_.insert(consumer); @@ -147,9 +142,9 @@ bool GamepadService::RemoveConsumer(GamepadConsumer* consumer) { auto it = consumers_.find(consumer); if (it == consumers_.end()) return false; - DCHECK_GT(num_active_consumers_, 0); if (it->is_active && --num_active_consumers_ == 0) provider_->Pause(); + DCHECK_GE(num_active_consumers_, 0); consumers_.erase(it); inactive_consumer_state_.erase(consumer); return true; diff --git a/chromium/device/gamepad/gamepad_service.h b/chromium/device/gamepad/gamepad_service.h index fff121234cf..cbfe58184f3 100644 --- a/chromium/device/gamepad/gamepad_service.h +++ b/chromium/device/gamepad/gamepad_service.h @@ -19,17 +19,13 @@ #include "device/gamepad/gamepad_provider.h" #include "device/gamepad/public/mojom/gamepad.mojom.h" -namespace { +namespace base { class SingleThreadTaskRunner; -} - -namespace content { -class GamepadServiceTestConstructor; -} +} // namespace base namespace service_manager { class Connector; -} +} // namespace service_manager namespace device { class GamepadConsumer; @@ -48,11 +44,12 @@ class DEVICE_GAMEPAD_EXPORT GamepadService // Sets the GamepadService instance. Exposed for tests. static void SetInstance(GamepadService*); + // Initializes the GamepadService. |service_manager_connector| will be + // passed to the GamepadProvider once it is created, to allow data fetchers + // to access the device service from the polling thread. void StartUp( std::unique_ptr<service_manager::Connector> service_manager_connector); - service_manager::Connector* GetConnector(); - // Increments the number of users of the provider. The Provider is running // when there's > 0 users, and is paused when the count drops to 0. // |consumer| is registered to listen for gamepad connections. If this is the @@ -120,19 +117,18 @@ class DEVICE_GAMEPAD_EXPORT GamepadService uint32_t pad_index, mojom::GamepadHapticsManager::ResetVibrationActuatorCallback); + // Constructor for testing. This specifies the data fetcher to use for a + // provider, bypassing the default platform one. + GamepadService(std::unique_ptr<GamepadDataFetcher> fetcher); + + virtual ~GamepadService(); + private: friend struct base::DefaultSingletonTraits<GamepadService>; - friend class GamepadServiceTestConstructor; friend class GamepadServiceTest; GamepadService(); - // Constructor for testing. This specifies the data fetcher to use for a - // provider, bypassing the default platform one. - GamepadService(std::unique_ptr<device::GamepadDataFetcher> fetcher); - - virtual ~GamepadService(); - void OnUserGesture(); void OnGamepadConnectionChange(bool connected, @@ -153,7 +149,7 @@ class DEVICE_GAMEPAD_EXPORT GamepadService mutable bool did_observe_user_gesture = false; }; - std::unique_ptr<device::GamepadProvider> provider_; + std::unique_ptr<GamepadProvider> provider_; scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_; @@ -165,10 +161,12 @@ class DEVICE_GAMEPAD_EXPORT GamepadService ConsumerConnectedStateMap inactive_consumer_state_; - int num_active_consumers_; + // The number of active consumers in |consumers_|. + int num_active_consumers_ = 0; - bool gesture_callback_pending_; + bool gesture_callback_pending_ = false; + // Service manager connector. Must be used only on the main thread. std::unique_ptr<service_manager::Connector> service_manager_connector_; DISALLOW_COPY_AND_ASSIGN(GamepadService); diff --git a/chromium/device/gamepad/gamepad_service_unittest.cc b/chromium/device/gamepad/gamepad_service_unittest.cc index 9c0245d3d87..81602220d4a 100644 --- a/chromium/device/gamepad/gamepad_service_unittest.cc +++ b/chromium/device/gamepad/gamepad_service_unittest.cc @@ -21,7 +21,7 @@ namespace { constexpr int kNumberOfGamepads = Gamepads::kItemsLengthCap; } // namespace -class ConnectionListener : public device::GamepadConsumer { +class ConnectionListener : public GamepadConsumer { public: ConnectionListener() = default; @@ -214,10 +214,11 @@ TEST_F(GamepadServiceTest, ReloadTest) { EXPECT_EQ(0, consumer->disconnected_counter()); } -// Flaky, see https://crbug.com/795170 -TEST_F(GamepadServiceTest, DISABLED_SecondConsumerGestureTest) { +TEST_F(GamepadServiceTest, SecondConsumerGestureTest) { auto* consumer1 = CreateConsumer(); auto* consumer2 = CreateConsumer(); + EXPECT_TRUE(service()->ConsumerBecameActive(consumer1)); + WaitForData(); EXPECT_EQ(0, consumer1->connected_counter()); EXPECT_EQ(0, consumer1->disconnected_counter()); @@ -477,8 +478,7 @@ TEST_F(GamepadServiceTest, DisconnectAndConnectWhileInactiveTest) { EXPECT_EQ(0, consumer2->disconnected_counter()); } -// Flaky, see https://crbug.com/795170 -TEST_F(GamepadServiceTest, DISABLED_ActiveConsumerBecameActive) { +TEST_F(GamepadServiceTest, ActiveConsumerBecameActive) { // Mark |consumer| active. auto* consumer = CreateConsumer(); EXPECT_TRUE(service()->ConsumerBecameActive(consumer)); @@ -487,8 +487,7 @@ TEST_F(GamepadServiceTest, DISABLED_ActiveConsumerBecameActive) { EXPECT_FALSE(service()->ConsumerBecameActive(consumer)); } -// Flaky, see https://crbug.com/795170 -TEST_F(GamepadServiceTest, DISABLED_InactiveConsumerBecameInactive) { +TEST_F(GamepadServiceTest, InactiveConsumerBecameInactive) { // Mark |consumer| active. auto* consumer = CreateConsumer(); EXPECT_TRUE(service()->ConsumerBecameActive(consumer)); @@ -500,8 +499,7 @@ TEST_F(GamepadServiceTest, DISABLED_InactiveConsumerBecameInactive) { EXPECT_FALSE(service()->ConsumerBecameInactive(consumer)); } -// Flaky, see https://crbug.com/795170 -TEST_F(GamepadServiceTest, DISABLED_UnregisteredConsumerBecameInactive) { +TEST_F(GamepadServiceTest, UnregisteredConsumerBecameInactive) { auto* consumer = CreateConsumer(); // |consumer| has not yet been added to the gamepad service through a call to @@ -509,8 +507,7 @@ TEST_F(GamepadServiceTest, DISABLED_UnregisteredConsumerBecameInactive) { EXPECT_FALSE(service()->ConsumerBecameInactive(consumer)); } -// Flaky, see https://crbug.com/795170 -TEST_F(GamepadServiceTest, DISABLED_RemoveUnregisteredConsumer) { +TEST_F(GamepadServiceTest, RemoveUnregisteredConsumer) { auto* consumer = CreateConsumer(); // |consumer| has not yet been added to the gamepad service through a call to diff --git a/chromium/device/gamepad/gamepad_test_helpers.h b/chromium/device/gamepad/gamepad_test_helpers.h index 1ea3643d12a..c41ecab25e2 100644 --- a/chromium/device/gamepad/gamepad_test_helpers.h +++ b/chromium/device/gamepad/gamepad_test_helpers.h @@ -60,7 +60,7 @@ class GamepadTestHelper { private: // This must be constructed before the system monitor. - base::test::TaskEnvironment task_environment_; + base::test::SingleThreadTaskEnvironment task_environment_; DISALLOW_COPY_AND_ASSIGN(GamepadTestHelper); }; diff --git a/chromium/device/gamepad/nintendo_controller.cc b/chromium/device/gamepad/nintendo_controller.cc index d47cd32af5a..375c8df30c1 100644 --- a/chromium/device/gamepad/nintendo_controller.cc +++ b/chromium/device/gamepad/nintendo_controller.cc @@ -8,6 +8,7 @@ #include <utility> #include "base/bind.h" +#include "base/numerics/ranges.h" #include "base/strings/stringprintf.h" #include "device/gamepad/gamepad_data_fetcher.h" #include "device/gamepad/gamepad_id_list.h" @@ -720,9 +721,9 @@ void FrequencyToHex(float frequency, int freq = static_cast<int>(frequency); int amp = static_cast<int>(amplitude * kVibrationAmplitudeMax); // Clamp the target frequency and amplitude to a safe range. - freq = std::min(std::max(freq, kVibrationFrequencyHzMin), - kVibrationFrequencyHzMax); - amp = std::min(std::max(amp, 0), kVibrationAmplitudeMax); + freq = base::ClampToRange(freq, kVibrationFrequencyHzMin, + kVibrationFrequencyHzMax); + amp = base::ClampToRange(amp, 0, kVibrationAmplitudeMax); const auto* best_vf = &kVibrationFrequency[0]; for (size_t i = 1; i < kVibrationFrequencySize; ++i) { const auto* vf = &kVibrationFrequency[i]; @@ -1146,9 +1147,10 @@ void NintendoController::Connect(mojom::HidManager::ConnectCallback callback) { std::move(callback)); } -void NintendoController::OnConnect(mojom::HidConnectionPtr connection) { +void NintendoController::OnConnect( + mojo::PendingRemote<mojom::HidConnection> connection) { if (connection) { - connection_ = std::move(connection); + connection_.Bind(std::move(connection)); ReadInputReport(); StartInitSequence(); } @@ -1608,8 +1610,7 @@ void NintendoController::RequestSetHomeLight( } void NintendoController::RequestSetHomeLightIntensity(double intensity) { - // Clamp |intensity| to [0,1]. - intensity = std::max(0.0, std::min(1.0, intensity)); + intensity = base::ClampToRange(intensity, 0.0, 1.0); uint8_t led_intensity = std::round(intensity * 0x0f); // Each pair of bytes in the minicycle data describes two minicyles. // The first byte holds two 4-bit values encoding minicycle intensities. diff --git a/chromium/device/gamepad/nintendo_controller.h b/chromium/device/gamepad/nintendo_controller.h index 0a2cb9c1559..4fcda6b12e0 100644 --- a/chromium/device/gamepad/nintendo_controller.h +++ b/chromium/device/gamepad/nintendo_controller.h @@ -15,6 +15,8 @@ #include "device/gamepad/gamepad_id_list.h" #include "device/gamepad/gamepad_standard_mappings.h" #include "device/gamepad/public/cpp/gamepad.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/remote.h" #include "services/device/public/mojom/hid.mojom.h" namespace device { @@ -238,7 +240,7 @@ class NintendoController final : public AbstractHapticGamepad { void Connect(mojom::HidManager::ConnectCallback callback); // Completion callback for the HID connection request. - void OnConnect(mojom::HidConnectionPtr connection); + void OnConnect(mojo::PendingRemote<mojom::HidConnection> connection); // Initiate the sequence of exchanges to prepare the device to provide // controller data. @@ -398,7 +400,7 @@ class NintendoController final : public AbstractHapticGamepad { mojom::HidManager* const hid_manager_; // The open connection to the underlying HID device. - mojom::HidConnectionPtr connection_; + mojo::Remote<mojom::HidConnection> connection_; // A closure, provided in the call to Open, to be called once the device // becomes ready. diff --git a/chromium/device/gamepad/nintendo_data_fetcher.cc b/chromium/device/gamepad/nintendo_data_fetcher.cc index 6711187fbd1..ee356fcfe22 100644 --- a/chromium/device/gamepad/nintendo_data_fetcher.cc +++ b/chromium/device/gamepad/nintendo_data_fetcher.cc @@ -14,7 +14,7 @@ namespace device { -NintendoDataFetcher::NintendoDataFetcher() : binding_(this) {} +NintendoDataFetcher::NintendoDataFetcher() = default; NintendoDataFetcher::~NintendoDataFetcher() { for (auto& entry : controllers_) { @@ -30,15 +30,12 @@ GamepadSource NintendoDataFetcher::source() { void NintendoDataFetcher::OnAddedToProvider() { // Open a connection to the HID service. On a successful connection, // OnGetDevices will be called with a list of connected HID devices. - auto* connector = GamepadService::GetInstance()->GetConnector(); - DCHECK(connector); - connector->BindInterface(mojom::kServiceName, - mojo::MakeRequest(&hid_manager_)); - mojom::HidManagerClientAssociatedPtrInfo client; - binding_.Bind(mojo::MakeRequest(&client)); + connector()->Connect(mojom::kServiceName, + hid_manager_.BindNewPipeAndPassReceiver()); hid_manager_->GetDevicesAndSetClient( - std::move(client), base::BindOnce(&NintendoDataFetcher::OnGetDevices, - weak_factory_.GetWeakPtr())); + receiver_.BindNewEndpointAndPassRemote(), + base::BindOnce(&NintendoDataFetcher::OnGetDevices, + weak_factory_.GetWeakPtr())); } void NintendoDataFetcher::OnGetDevices( diff --git a/chromium/device/gamepad/nintendo_data_fetcher.h b/chromium/device/gamepad/nintendo_data_fetcher.h index c0964a2e875..776544ed703 100644 --- a/chromium/device/gamepad/nintendo_data_fetcher.h +++ b/chromium/device/gamepad/nintendo_data_fetcher.h @@ -14,7 +14,8 @@ #include "device/gamepad/gamepad_data_fetcher.h" #include "device/gamepad/nintendo_controller.h" #include "device/gamepad/public/cpp/gamepads.h" -#include "mojo/public/cpp/bindings/associated_binding.h" +#include "mojo/public/cpp/bindings/associated_receiver.h" +#include "mojo/public/cpp/bindings/remote.h" #include "services/device/public/mojom/hid.mojom.h" namespace device { @@ -110,8 +111,8 @@ class DEVICE_GAMEPAD_EXPORT NintendoDataFetcher : public GamepadDataFetcher, // A mapping from source ID to connected Nintendo Switch devices. ControllerMap controllers_; - mojom::HidManagerPtr hid_manager_; - mojo::AssociatedBinding<mojom::HidManagerClient> binding_; + mojo::Remote<mojom::HidManager> hid_manager_; + mojo::AssociatedReceiver<mojom::HidManagerClient> receiver_{this}; base::WeakPtrFactory<NintendoDataFetcher> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(NintendoDataFetcher); diff --git a/chromium/device/gamepad/nintendo_data_fetcher_unittest.cc b/chromium/device/gamepad/nintendo_data_fetcher_unittest.cc index 32d390ce6aa..977fd193f5a 100644 --- a/chromium/device/gamepad/nintendo_data_fetcher_unittest.cc +++ b/chromium/device/gamepad/nintendo_data_fetcher_unittest.cc @@ -19,15 +19,6 @@ #include "services/service_manager/public/cpp/connector.h" #include "testing/gtest/include/gtest/gtest.h" -// TODO(crbug.com/961039): Fix memory leaks in tests and re-enable on LSAN. -#ifdef LEAK_SANITIZER -#define MAYBE_AddAndRemoveSwitchPro DISABLED_AddAndRemoveSwitchPro -#define MAYBE_UnsupportedDeviceIsIgnored DISABLED_UnsupportedDeviceIsIgnored -#else -#define MAYBE_AddAndRemoveSwitchPro AddAndRemoveSwitchPro -#define MAYBE_UnsupportedDeviceIsIgnored UnsupportedDeviceIsIgnored -#endif - namespace device { namespace { @@ -65,8 +56,9 @@ class NintendoDataFetcherTest : public DeviceServiceTestBase { fetcher_ = fetcher.get(); auto polling_thread = std::make_unique<base::Thread>("polling thread"); polling_thread_ = polling_thread.get(); - provider_.reset(new GamepadProvider(nullptr, std::move(fetcher), - std::move(polling_thread))); + provider_ = std::make_unique<GamepadProvider>( + /*connection_change_client=*/nullptr, connector()->Clone(), + std::move(fetcher), std::move(polling_thread)); RunUntilIdle(); } @@ -89,7 +81,7 @@ class NintendoDataFetcherTest : public DeviceServiceTestBase { DISALLOW_COPY_AND_ASSIGN(NintendoDataFetcherTest); }; -TEST_F(NintendoDataFetcherTest, MAYBE_UnsupportedDeviceIsIgnored) { +TEST_F(NintendoDataFetcherTest, UnsupportedDeviceIsIgnored) { // Simulate an unsupported, non-Nintendo HID device. auto collection = mojom::HidCollectionInfo::New(); collection->usage = mojom::HidUsageAndPage::New(0, 0); @@ -110,7 +102,7 @@ TEST_F(NintendoDataFetcherTest, MAYBE_UnsupportedDeviceIsIgnored) { RunUntilIdle(); } -TEST_F(NintendoDataFetcherTest, MAYBE_AddAndRemoveSwitchPro) { +TEST_F(NintendoDataFetcherTest, AddAndRemoveSwitchPro) { // Simulate a Switch Pro over USB. auto collection = mojom::HidCollectionInfo::New(); collection->usage = mojom::HidUsageAndPage::New(0, 0); diff --git a/chromium/device/gamepad/public/cpp/gamepad_mojom_traits_unittest.cc b/chromium/device/gamepad/public/cpp/gamepad_mojom_traits_unittest.cc index 13ab0e9a490..daf77d44026 100644 --- a/chromium/device/gamepad/public/cpp/gamepad_mojom_traits_unittest.cc +++ b/chromium/device/gamepad/public/cpp/gamepad_mojom_traits_unittest.cc @@ -164,7 +164,7 @@ class GamepadStructTraitsTest : public testing::Test { GamepadStructTraitsTest() {} private: - base::test::TaskEnvironment task_environment_; + base::test::SingleThreadTaskEnvironment task_environment_; DISALLOW_COPY_AND_ASSIGN(GamepadStructTraitsTest); }; diff --git a/chromium/device/gamepad/raw_input_data_fetcher_win.cc b/chromium/device/gamepad/raw_input_data_fetcher_win.cc index 2132d33dbfc..6a7f9be19e7 100644 --- a/chromium/device/gamepad/raw_input_data_fetcher_win.cc +++ b/chromium/device/gamepad/raw_input_data_fetcher_win.cc @@ -107,6 +107,17 @@ void RawInputDataFetcher::StopMonitor() { window_.reset(); } +bool RawInputDataFetcher::DisconnectUnrecognizedGamepad(int source_id) { + for (auto it = controllers_.begin(); it != controllers_.end(); ++it) { + if (it->second->GetSourceId() == source_id) { + it->second->Shutdown(); + controllers_.erase(it); + return true; + } + } + return false; +} + void RawInputDataFetcher::ClearControllers() { for (const auto& entry : controllers_) entry.second->Shutdown(); @@ -179,6 +190,10 @@ void RawInputDataFetcher::EnumerateDevices() { continue; } + bool is_recognized = + GamepadId::kUnknownGamepad != + GamepadIdList::Get().GetGamepadId(vendor_int, product_int); + // Record gamepad metrics before excluding XInput devices. This allows // us to recognize XInput devices even though the XInput API masks // the vendor and product IDs. @@ -194,7 +209,7 @@ void RawInputDataFetcher::EnumerateDevices() { continue; } - PadState* state = GetPadState(source_id); + PadState* state = GetPadState(source_id, is_recognized); if (!state) { new_device->Shutdown(); continue; // No slot available for this gamepad. diff --git a/chromium/device/gamepad/raw_input_data_fetcher_win.h b/chromium/device/gamepad/raw_input_data_fetcher_win.h index 3d51f7463f6..6d76268ec49 100644 --- a/chromium/device/gamepad/raw_input_data_fetcher_win.h +++ b/chromium/device/gamepad/raw_input_data_fetcher_win.h @@ -49,6 +49,8 @@ class RawInputDataFetcher : public GamepadDataFetcher, mojom::GamepadHapticsManager::ResetVibrationActuatorCallback, scoped_refptr<base::SequencedTaskRunner>) override; + bool DisconnectUnrecognizedGamepad(int source_id) override; + private: void OnAddedToProvider() override; diff --git a/chromium/device/gamepad/xinput_haptic_gamepad_win.cc b/chromium/device/gamepad/xinput_haptic_gamepad_win.cc index b0e03dcd63f..8666c31824a 100644 --- a/chromium/device/gamepad/xinput_haptic_gamepad_win.cc +++ b/chromium/device/gamepad/xinput_haptic_gamepad_win.cc @@ -4,6 +4,8 @@ #include "device/gamepad/xinput_haptic_gamepad_win.h" +#include "base/trace_event/trace_event.h" + namespace { const long kRumbleMagnitudeMax = 0xffff; } // namespace diff --git a/chromium/device/vr/BUILD.gn b/chromium/device/vr/BUILD.gn index cf711256acc..089939493f8 100644 --- a/chromium/device/vr/BUILD.gn +++ b/chromium/device/vr/BUILD.gn @@ -241,8 +241,8 @@ if (enable_vr) { "openxr/openxr_controller.h", "openxr/openxr_device.cc", "openxr/openxr_device.h", - "openxr/openxr_gamepad_helper.cc", - "openxr/openxr_gamepad_helper.h", + "openxr/openxr_input_helper.cc", + "openxr/openxr_input_helper.h", "openxr/openxr_render_loop.cc", "openxr/openxr_render_loop.h", "openxr/openxr_util.cc", @@ -401,8 +401,10 @@ if (enable_gvr_services) { java_files = java_sources_needing_jni deps = [ "//base:base_java", + "//base:jni_java", "//third_party/gvr-android-sdk:gvr_common_java", "//ui/android:ui_java", ] + annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] } } diff --git a/chromium/device/vr/android/gvr/gvr_delegate.cc b/chromium/device/vr/android/gvr/gvr_delegate.cc index 9b596dae2f8..a09b8378dbc 100644 --- a/chromium/device/vr/android/gvr/gvr_delegate.cc +++ b/chromium/device/vr/android/gvr/gvr_delegate.cc @@ -151,6 +151,22 @@ mojom::VRPosePtr GvrDelegate::GetVRPosePtrWithNeckModel( pose->angular_velocity = GetAngularVelocityFromPoses(*head_mat_ptr, head_mat_2, epsilon_seconds); + // The position is emulated unless the current tracking status is 6DoF and is + // not still initializing or invalid. + pose->emulated_position = true; + gvr::Properties props = gvr_api->GetCurrentProperties(); + gvr::Value head_tracking_status; + if (props.Get(GVR_PROPERTY_TRACKING_STATUS, &head_tracking_status)) { + bool has_6dof = + !!(head_tracking_status.fl & GVR_TRACKING_STATUS_FLAG_HAS_6DOF); + bool initialized = + !(head_tracking_status.fl & GVR_TRACKING_STATUS_FLAG_INITIALIZING); + bool valid = !(head_tracking_status.fl & GVR_TRACKING_STATUS_FLAG_INVALID); + if (has_6dof && initialized && valid) { + pose->emulated_position = false; + } + } + return pose; } diff --git a/chromium/device/vr/android/gvr/gvr_device.cc b/chromium/device/vr/android/gvr/gvr_device.cc index 084f1869c0f..116982b1d17 100644 --- a/chromium/device/vr/android/gvr/gvr_device.cc +++ b/chromium/device/vr/android/gvr/gvr_device.cc @@ -20,6 +20,7 @@ #include "device/vr/android/gvr/gvr_device_provider.h" #include "device/vr/android/gvr/gvr_utils.h" #include "device/vr/jni_headers/NonPresentingGvrContext_jni.h" +#include "mojo/public/cpp/bindings/pending_remote.h" #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/transform.h" @@ -141,9 +142,7 @@ mojom::VRDisplayInfoPtr CreateVRDisplayInfo(gvr::GvrApi* gvr_api, } // namespace -GvrDevice::GvrDevice() - : VRDeviceBase(mojom::XRDeviceId::GVR_DEVICE_ID), - exclusive_controller_binding_(this) { +GvrDevice::GvrDevice() : VRDeviceBase(mojom::XRDeviceId::GVR_DEVICE_ID) { GvrDelegateProviderFactory::SetDevice(this); } @@ -156,7 +155,8 @@ GvrDevice::~GvrDevice() { } if (pending_request_session_callback_) { - std::move(pending_request_session_callback_).Run(nullptr, nullptr); + std::move(pending_request_session_callback_) + .Run(nullptr, mojo::NullRemote()); } GvrDelegateProviderFactory::SetDevice(nullptr); @@ -171,7 +171,7 @@ void GvrDevice::RequestSession( mojom::XRRuntime::RequestSessionCallback callback) { // We can only process one request at a time. if (pending_request_session_callback_) { - std::move(callback).Run(nullptr, nullptr); + std::move(callback).Run(nullptr, mojo::NullRemote()); return; } pending_request_session_callback_ = std::move(callback); @@ -189,25 +189,26 @@ void GvrDevice::OnStartPresentResult( DCHECK(pending_request_session_callback_); if (!session) { - std::move(pending_request_session_callback_).Run(nullptr, nullptr); + std::move(pending_request_session_callback_) + .Run(nullptr, mojo::NullRemote()); return; } 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)); + exclusive_controller_receiver_.reset(); + + std::move(pending_request_session_callback_) + .Run(std::move(session), + exclusive_controller_receiver_.BindNewPipeAndPassRemote()); // Unretained is safe because the error handler won't be called after the // binding has been destroyed. - exclusive_controller_binding_.set_connection_error_handler( + exclusive_controller_receiver_.set_disconnect_handler( base::BindOnce(&GvrDevice::OnPresentingControllerMojoConnectionError, base::Unretained(this))); - std::move(pending_request_session_callback_) - .Run(std::move(session), std::move(session_controller)); } // XRSessionController @@ -225,7 +226,7 @@ void GvrDevice::StopPresenting() { if (delegate_provider) delegate_provider->ExitWebVRPresent(); OnExitPresent(); - exclusive_controller_binding_.Close(); + exclusive_controller_receiver_.reset(); } void GvrDevice::OnListeningForActivate(bool listening) { @@ -312,13 +313,15 @@ void GvrDevice::OnInitRequestSessionFinished( DCHECK(pending_request_session_callback_); if (!success) { - std::move(pending_request_session_callback_).Run(nullptr, nullptr); + std::move(pending_request_session_callback_) + .Run(nullptr, mojo::NullRemote()); return; } GvrDelegateProvider* delegate_provider = GetGvrDelegateProvider(); if (!delegate_provider) { - std::move(pending_request_session_callback_).Run(nullptr, nullptr); + std::move(pending_request_session_callback_) + .Run(nullptr, mojo::NullRemote()); return; } diff --git a/chromium/device/vr/android/gvr/gvr_device.h b/chromium/device/vr/android/gvr/gvr_device.h index 4c91a525be3..bf4c03fa04c 100644 --- a/chromium/device/vr/android/gvr/gvr_device.h +++ b/chromium/device/vr/android/gvr/gvr_device.h @@ -12,6 +12,7 @@ #include "base/android/scoped_java_ref.h" #include "base/macros.h" #include "device/vr/vr_device_base.h" +#include "mojo/public/cpp/bindings/receiver.h" #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h" namespace device { @@ -63,7 +64,8 @@ class DEVICE_VR_EXPORT GvrDevice : public VRDeviceBase, bool paused_ = true; - mojo::Binding<mojom::XRSessionController> exclusive_controller_binding_; + mojo::Receiver<mojom::XRSessionController> exclusive_controller_receiver_{ + this}; mojom::XRRuntime::RequestSessionCallback pending_request_session_callback_; diff --git a/chromium/device/vr/android/gvr/gvr_device_provider.cc b/chromium/device/vr/android/gvr/gvr_device_provider.cc index 33856d1f842..9b007593a09 100644 --- a/chromium/device/vr/android/gvr/gvr_device_provider.cc +++ b/chromium/device/vr/android/gvr/gvr_device_provider.cc @@ -16,7 +16,8 @@ GvrDeviceProvider::~GvrDeviceProvider() = default; void GvrDeviceProvider::Initialize( base::RepeatingCallback<void(mojom::XRDeviceId, mojom::VRDisplayInfoPtr, - mojom::XRRuntimePtr)> add_device_callback, + mojo::PendingRemote<mojom::XRRuntime>)> + add_device_callback, base::RepeatingCallback<void(mojom::XRDeviceId)> remove_device_callback, base::OnceClosure initialization_complete) { // Version check should match MIN_SDK_VERSION in VrCoreVersionChecker.java. @@ -30,7 +31,7 @@ void GvrDeviceProvider::Initialize( } if (vr_device_) { add_device_callback.Run(vr_device_->GetId(), vr_device_->GetVRDisplayInfo(), - vr_device_->BindXRRuntimePtr()); + vr_device_->BindXRRuntime()); } 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 527b84715cb..52209459da5 100644 --- a/chromium/device/vr/android/gvr/gvr_device_provider.h +++ b/chromium/device/vr/android/gvr/gvr_device_provider.h @@ -10,6 +10,7 @@ #include "base/macros.h" #include "device/vr/vr_device_provider.h" #include "device/vr/vr_export.h" +#include "mojo/public/cpp/bindings/pending_remote.h" namespace device { @@ -23,7 +24,8 @@ class DEVICE_VR_EXPORT GvrDeviceProvider : public VRDeviceProvider { void Initialize( base::RepeatingCallback<void(mojom::XRDeviceId, mojom::VRDisplayInfoPtr, - mojom::XRRuntimePtr)> add_device_callback, + mojo::PendingRemote<mojom::XRRuntime>)> + add_device_callback, base::RepeatingCallback<void(mojom::XRDeviceId)> remove_device_callback, base::OnceClosure initialization_complete) override; diff --git a/chromium/device/vr/android/java/src/org/chromium/device/vr/NonPresentingGvrContext.java b/chromium/device/vr/android/java/src/org/chromium/device/vr/NonPresentingGvrContext.java index d7ba07e62b0..20acaecf050 100644 --- a/chromium/device/vr/android/java/src/org/chromium/device/vr/NonPresentingGvrContext.java +++ b/chromium/device/vr/android/java/src/org/chromium/device/vr/NonPresentingGvrContext.java @@ -15,6 +15,7 @@ import com.google.vr.ndk.base.GvrApi; import org.chromium.base.ContextUtils; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.NativeMethods; /** * Creates an active GvrContext from a GvrApi created from the Application Context. This GvrContext @@ -90,8 +91,14 @@ public class NonPresentingGvrContext { public void onDisplayConfigurationChanged() { mGvrApi.refreshDisplayMetrics(); - if (mNativeGvrDevice != 0) nativeOnDisplayConfigurationChanged(mNativeGvrDevice); + if (mNativeGvrDevice != 0) { + NonPresentingGvrContextJni.get().onDisplayConfigurationChanged( + mNativeGvrDevice, NonPresentingGvrContext.this); + } } - private native void nativeOnDisplayConfigurationChanged(long nativeGvrDevice); + @NativeMethods + interface Natives { + void onDisplayConfigurationChanged(long nativeGvrDevice, NonPresentingGvrContext caller); + } } diff --git a/chromium/device/vr/buildflags/buildflags.gni b/chromium/device/vr/buildflags/buildflags.gni index dfe975ea1ee..b95861404df 100644 --- a/chromium/device/vr/buildflags/buildflags.gni +++ b/chromium/device/vr/buildflags/buildflags.gni @@ -47,13 +47,4 @@ declare_args() { # TODO(crbug.com/843374): AR should not depend on |enable_vr|. enable_arcore = enable_vr && is_android && !is_chromecast && (current_cpu == "arm" || current_cpu == "arm64") - - # When true, portions of VR's native code are included in the VR DFM. - modularize_vr_native = true - - # Whether to create AR module as an asynchronous DFM. - async_ar = false - - # Whether to create VR module as an asynchronous DFM. - async_vr = false } diff --git a/chromium/device/vr/isolated_gamepad_data_fetcher.cc b/chromium/device/vr/isolated_gamepad_data_fetcher.cc index bd27e4299c1..d58d3189f7f 100644 --- a/chromium/device/vr/isolated_gamepad_data_fetcher.cc +++ b/chromium/device/vr/isolated_gamepad_data_fetcher.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/strings/utf_string_conversions.h" +#include "base/trace_event/trace_event.h" #include "build/buildflag.h" #include "device/vr/buildflags/buildflags.h" #include "device/vr/vr_device.h" @@ -69,15 +70,17 @@ GamepadSource GamepadSourceFromDeviceId(device::mojom::XRDeviceId id) { IsolatedGamepadDataFetcher::Factory::Factory( device::mojom::XRDeviceId display_id, - device::mojom::IsolatedXRGamepadProviderFactoryPtr factory) + mojo::PendingRemote<device::mojom::IsolatedXRGamepadProviderFactory> + factory) : display_id_(display_id), factory_(std::move(factory)) {} IsolatedGamepadDataFetcher::Factory::~Factory() {} std::unique_ptr<GamepadDataFetcher> IsolatedGamepadDataFetcher::Factory::CreateDataFetcher() { - device::mojom::IsolatedXRGamepadProviderPtr provider; - factory_->GetIsolatedXRGamepadProvider(mojo::MakeRequest(&provider)); + mojo::PendingRemote<device::mojom::IsolatedXRGamepadProvider> provider; + factory_->GetIsolatedXRGamepadProvider( + provider.InitWithNewPipeAndPassReceiver()); return std::make_unique<IsolatedGamepadDataFetcher>(display_id_, std::move(provider)); } @@ -88,11 +91,11 @@ GamepadSource IsolatedGamepadDataFetcher::Factory::source() { IsolatedGamepadDataFetcher::IsolatedGamepadDataFetcher( device::mojom::XRDeviceId display_id, - device::mojom::IsolatedXRGamepadProviderPtr provider) + mojo::PendingRemote<device::mojom::IsolatedXRGamepadProvider> provider) : display_id_(display_id) { // We bind provider_ on the poling thread, but we're created on the main UI // thread. - provider_info_ = provider.PassInterface(); + pending_provider_ = std::move(provider); } IsolatedGamepadDataFetcher::~IsolatedGamepadDataFetcher() = default; @@ -161,8 +164,8 @@ GamepadPose GamepadPoseFromXRPose(device::mojom::VRPose* pose) { } void IsolatedGamepadDataFetcher::GetGamepadData(bool devices_changed_hint) { - if (!provider_ && provider_info_) { - provider_.Bind(std::move(provider_info_)); + if (!provider_ && pending_provider_) { + provider_.Bind(std::move(pending_provider_)); } // If we don't have a provider, we can't give out data. @@ -289,7 +292,8 @@ void IsolatedGamepadDataFetcher::Factory::RemoveGamepad( void IsolatedGamepadDataFetcher::Factory::AddGamepad( device::mojom::XRDeviceId device_id, - device::mojom::IsolatedXRGamepadProviderFactoryPtr gamepad_factory) { + mojo::PendingRemote<device::mojom::IsolatedXRGamepadProviderFactory> + gamepad_factory) { if (!IsValidDeviceId(device_id)) return; diff --git a/chromium/device/vr/isolated_gamepad_data_fetcher.h b/chromium/device/vr/isolated_gamepad_data_fetcher.h index 39511405498..cf5e1af92e6 100644 --- a/chromium/device/vr/isolated_gamepad_data_fetcher.h +++ b/chromium/device/vr/isolated_gamepad_data_fetcher.h @@ -8,6 +8,8 @@ #include "device/gamepad/gamepad_data_fetcher.h" #include "device/vr/public/mojom/isolated_xr_service.mojom.h" #include "device/vr/vr_device.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/remote.h" namespace device { @@ -16,24 +18,26 @@ class IsolatedGamepadDataFetcher : public GamepadDataFetcher { class DEVICE_VR_EXPORT Factory : public GamepadDataFetcherFactory { public: Factory(device::mojom::XRDeviceId display_id, - device::mojom::IsolatedXRGamepadProviderFactoryPtr factory); + mojo::PendingRemote<device::mojom::IsolatedXRGamepadProviderFactory> + factory); ~Factory() override; std::unique_ptr<GamepadDataFetcher> CreateDataFetcher() override; GamepadSource source() override; static void AddGamepad( device::mojom::XRDeviceId device_id, - device::mojom::IsolatedXRGamepadProviderFactoryPtr gamepad_factory); + mojo::PendingRemote<device::mojom::IsolatedXRGamepadProviderFactory> + gamepad_factory); static void RemoveGamepad(device::mojom::XRDeviceId device_id); private: device::mojom::XRDeviceId display_id_; - device::mojom::IsolatedXRGamepadProviderFactoryPtr factory_; + mojo::Remote<device::mojom::IsolatedXRGamepadProviderFactory> factory_; }; IsolatedGamepadDataFetcher( device::mojom::XRDeviceId display_id, - device::mojom::IsolatedXRGamepadProviderPtr provider); + mojo::PendingRemote<device::mojom::IsolatedXRGamepadProvider> provider); ~IsolatedGamepadDataFetcher() override; GamepadSource source() override; @@ -49,10 +53,10 @@ class IsolatedGamepadDataFetcher : public GamepadDataFetcher { bool have_outstanding_request_ = false; std::set<unsigned int> active_gamepads_; device::mojom::XRGamepadDataPtr data_; - device::mojom::IsolatedXRGamepadProviderPtr + mojo::Remote<device::mojom::IsolatedXRGamepadProvider> provider_; // Bound on the polling thread. - device::mojom::IsolatedXRGamepadProviderPtrInfo - provider_info_; // Received on the UI thread, bound when polled. + mojo::PendingRemote<device::mojom::IsolatedXRGamepadProvider> + pending_provider_; // Received on the UI thread, bound when polled. DISALLOW_COPY_AND_ASSIGN(IsolatedGamepadDataFetcher); }; diff --git a/chromium/device/vr/oculus/oculus_device.cc b/chromium/device/vr/oculus/oculus_device.cc index 9f15d2e9356..cc3f0a5cd51 100644 --- a/chromium/device/vr/oculus/oculus_device.cc +++ b/chromium/device/vr/oculus/oculus_device.cc @@ -16,6 +16,7 @@ #include "device/vr/oculus/oculus_render_loop.h" #include "device/vr/oculus/oculus_type_converters.h" #include "device/vr/util/transform_utils.h" +#include "mojo/public/cpp/bindings/pending_remote.h" #include "third_party/libovr/src/Include/OVR_CAPI.h" #include "third_party/libovr/src/Include/OVR_CAPI_D3D.h" #include "ui/gfx/geometry/angle_conversions.h" @@ -95,9 +96,6 @@ mojom::VRDisplayInfoPtr CreateVRDisplayInfo(mojom::XRDeviceId id, OculusDevice::OculusDevice() : VRDeviceBase(mojom::XRDeviceId::OCULUS_DEVICE_ID), main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), - exclusive_controller_binding_(this), - gamepad_provider_factory_binding_(this), - compositor_host_binding_(this), weak_ptr_factory_(this) { render_loop_ = std::make_unique<OculusRenderLoop>(); } @@ -112,16 +110,14 @@ bool OculusDevice::IsApiAvailable() { return result.IsOculusServiceRunning; } -mojom::IsolatedXRGamepadProviderFactoryPtr OculusDevice::BindGamepadFactory() { - mojom::IsolatedXRGamepadProviderFactoryPtr ret; - gamepad_provider_factory_binding_.Bind(mojo::MakeRequest(&ret)); - return ret; +mojo::PendingRemote<mojom::IsolatedXRGamepadProviderFactory> +OculusDevice::BindGamepadFactory() { + return gamepad_provider_factory_receiver_.BindNewPipeAndPassRemote(); } -mojom::XRCompositorHostPtr OculusDevice::BindCompositorHost() { - mojom::XRCompositorHostPtr ret; - compositor_host_binding_.Bind(mojo::MakeRequest(&ret)); - return ret; +mojo::PendingRemote<mojom::XRCompositorHost> +OculusDevice::BindCompositorHost() { + return compositor_host_receiver_.BindNewPipeAndPassRemote(); } OculusDevice::~OculusDevice() { @@ -137,7 +133,7 @@ void OculusDevice::RequestSession( mojom::XRRuntimeSessionOptionsPtr options, mojom::XRRuntime::RequestSessionCallback callback) { if (!EnsureValidDisplayInfo()) { - std::move(callback).Run(nullptr, nullptr); + std::move(callback).Run(nullptr, mojo::NullRemote()); return; } @@ -149,25 +145,25 @@ void OculusDevice::RequestSession( render_loop_->Start(); if (!render_loop_->IsRunning()) { - std::move(callback).Run(nullptr, nullptr); + std::move(callback).Run(nullptr, mojo::NullRemote()); StartOvrSession(); return; } - // If we have a pending gamepad provider request when starting the render - // loop, post the request over to the render loop to be bound. - if (provider_request_) { + // If we have a pending gamepad provider receiver when starting the render + // loop, post the receiver over to the render loop to be bound. + if (provider_receiver_) { render_loop_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestGamepadProvider, base::Unretained(render_loop_.get()), - std::move(provider_request_))); + std::move(provider_receiver_))); } - if (overlay_request_) { + if (overlay_receiver_) { render_loop_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestOverlay, base::Unretained(render_loop_.get()), - std::move(overlay_request_))); + std::move(overlay_receiver_))); } } @@ -182,8 +178,9 @@ void OculusDevice::RequestSession( FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestSession, base::Unretained(render_loop_.get()), - std::move(on_presentation_ended), std::move(options), - std::move(on_request_present_result))); + std::move(on_presentation_ended), + base::DoNothing::Repeatedly<mojom::XRVisibilityState>(), + std::move(options), std::move(on_request_present_result))); outstanding_session_requests_count_++; } @@ -213,7 +210,7 @@ void OculusDevice::OnRequestSessionResult( mojom::XRSessionPtr session) { outstanding_session_requests_count_--; if (!result) { - std::move(callback).Run(nullptr, nullptr); + std::move(callback).Run(nullptr, mojo::NullRemote()); // Start magic window again. if (outstanding_session_requests_count_ == 0) @@ -223,18 +220,17 @@ void OculusDevice::OnRequestSessionResult( OnStartPresenting(); - mojom::XRSessionControllerPtr session_controller; - exclusive_controller_binding_.Bind(mojo::MakeRequest(&session_controller)); + session->display_info = display_info_.Clone(); + + std::move(callback).Run( + std::move(session), + exclusive_controller_receiver_.BindNewPipeAndPassRemote()); // Unretained is safe because the error handler won't be called after the // binding has been destroyed. - exclusive_controller_binding_.set_connection_error_handler( + exclusive_controller_receiver_.set_disconnect_handler( base::BindOnce(&OculusDevice::OnPresentingControllerMojoConnectionError, base::Unretained(this))); - - session->display_info = display_info_.Clone(); - - std::move(callback).Run(std::move(session), std::move(session_controller)); } bool OculusDevice::IsAvailable() { @@ -253,7 +249,7 @@ void OculusDevice::OnPresentingControllerMojoConnectionError() { FROM_HERE, base::BindOnce(&XRCompositorCommon::ExitPresent, base::Unretained(render_loop_.get()))); OnExitPresent(); - exclusive_controller_binding_.Close(); + exclusive_controller_receiver_.reset(); } void OculusDevice::OnPresentationEnded() { @@ -289,30 +285,30 @@ void OculusDevice::StopOvrSession() { } void OculusDevice::GetIsolatedXRGamepadProvider( - mojom::IsolatedXRGamepadProviderRequest provider_request) { - // We bind the provider_request on the render loop thread, so gamepad data is + mojo::PendingReceiver<mojom::IsolatedXRGamepadProvider> provider_receiver) { + // We bind the provider_receiver on the render loop thread, so gamepad data is // updated at the rendering rate. - // If we haven't started the render loop yet, postpone binding the request + // If we haven't started the render loop yet, postpone binding the receiver // until we do. if (render_loop_->IsRunning()) { render_loop_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestGamepadProvider, base::Unretained(render_loop_.get()), - std::move(provider_request))); + std::move(provider_receiver))); } else { - provider_request_ = std::move(provider_request); + provider_receiver_ = std::move(provider_receiver); } } void OculusDevice::CreateImmersiveOverlay( - mojom::ImmersiveOverlayRequest overlay_request) { + mojo::PendingReceiver<mojom::ImmersiveOverlay> overlay_receiver) { if (render_loop_->IsRunning()) { render_loop_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestOverlay, base::Unretained(render_loop_.get()), - std::move(overlay_request))); + std::move(overlay_receiver))); } else { - overlay_request_ = std::move(overlay_request); + overlay_receiver_ = std::move(overlay_receiver); } } diff --git a/chromium/device/vr/oculus/oculus_device.h b/chromium/device/vr/oculus/oculus_device.h index a44754a177c..63670cf4dcd 100644 --- a/chromium/device/vr/oculus/oculus_device.h +++ b/chromium/device/vr/oculus/oculus_device.h @@ -11,7 +11,9 @@ #include "base/single_thread_task_runner.h" #include "device/vr/public/mojom/vr_service.mojom.h" #include "device/vr/vr_device_base.h" -#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver.h" #include "third_party/libovr/src/Include/OVR_CAPI.h" namespace device { @@ -41,8 +43,9 @@ class DEVICE_VR_EXPORT OculusDevice bool IsAvailable(); - mojom::IsolatedXRGamepadProviderFactoryPtr BindGamepadFactory(); - mojom::XRCompositorHostPtr BindCompositorHost(); + mojo::PendingRemote<mojom::IsolatedXRGamepadProviderFactory> + BindGamepadFactory(); + mojo::PendingRemote<mojom::XRCompositorHost> BindCompositorHost(); private: // XRSessionController @@ -52,11 +55,12 @@ class DEVICE_VR_EXPORT OculusDevice // mojom::IsolatedXRGamepadProviderFactory void GetIsolatedXRGamepadProvider( - mojom::IsolatedXRGamepadProviderRequest provider_request) override; + mojo::PendingReceiver<mojom::IsolatedXRGamepadProvider> provider_receiver) + override; // XRCompositorHost void CreateImmersiveOverlay( - mojom::ImmersiveOverlayRequest overlay_request) override; + mojo::PendingReceiver<mojom::ImmersiveOverlay> overlay_receiver) override; void OnPresentationEnded(); bool EnsureValidDisplayInfo(); @@ -69,13 +73,14 @@ class DEVICE_VR_EXPORT OculusDevice ovrSession session_ = nullptr; scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_; - mojo::Binding<mojom::XRSessionController> exclusive_controller_binding_; - mojo::Binding<mojom::IsolatedXRGamepadProviderFactory> - gamepad_provider_factory_binding_; - mojom::IsolatedXRGamepadProviderRequest provider_request_; + mojo::Receiver<mojom::XRSessionController> exclusive_controller_receiver_{ + this}; + mojo::Receiver<mojom::IsolatedXRGamepadProviderFactory> + gamepad_provider_factory_receiver_{this}; + mojo::PendingReceiver<mojom::IsolatedXRGamepadProvider> provider_receiver_; - mojo::Binding<mojom::XRCompositorHost> compositor_host_binding_; - mojom::ImmersiveOverlayRequest overlay_request_; + mojo::Receiver<mojom::XRCompositorHost> compositor_host_receiver_{this}; + mojo::PendingReceiver<mojom::ImmersiveOverlay> overlay_receiver_; base::WeakPtrFactory<OculusDevice> weak_ptr_factory_; diff --git a/chromium/device/vr/oculus/oculus_render_loop.cc b/chromium/device/vr/oculus/oculus_render_loop.cc index 12449248b41..dda325d132b 100644 --- a/chromium/device/vr/oculus/oculus_render_loop.cc +++ b/chromium/device/vr/oculus/oculus_render_loop.cc @@ -347,8 +347,8 @@ device::mojom::XRInputSourceStatePtr OculusRenderLoop::GetTouchData( break; } - // Touch controller are fully 6DoF. - desc->emulated_position = false; + // Touch controllers are fully 6DoF. + state->emulated_position = false; // The grip pose will be rotated and translated back a bit from the pointer // pose, which is what the Oculus API returns. @@ -368,7 +368,7 @@ device::mojom::XRInputSourceStatePtr OculusRenderLoop::GetTouchData( // The absence of "touchpad" in this string indicates that the slots in the // button and axes arrays are placeholders required by the xr-standard mapping // but not actually updated with any input. - desc->profiles.push_back("grip-thumbstick-controller"); + desc->profiles.push_back("generic-trigger-squeeze-thumbstick"); state->description = std::move(desc); diff --git a/chromium/device/vr/openvr/openvr_device.cc b/chromium/device/vr/openvr/openvr_device.cc index 8110ff38df0..98e630c7b62 100644 --- a/chromium/device/vr/openvr/openvr_device.cc +++ b/chromium/device/vr/openvr/openvr_device.cc @@ -16,6 +16,7 @@ #include "build/build_config.h" #include "device/vr/openvr/openvr_render_loop.h" #include "device/vr/openvr/openvr_type_converters.h" +#include "mojo/public/cpp/bindings/pending_remote.h" #include "third_party/openvr/src/headers/openvr.h" #include "ui/gfx/geometry/angle_conversions.h" @@ -122,10 +123,7 @@ mojom::VRDisplayInfoPtr CreateVRDisplayInfo(vr::IVRSystem* vr_system, OpenVRDevice::OpenVRDevice() : VRDeviceBase(device::mojom::XRDeviceId::OPENVR_DEVICE_ID), - main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), - exclusive_controller_binding_(this), - gamepad_provider_factory_binding_(this), - compositor_host_binding_(this) { + main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()) { render_loop_ = std::make_unique<OpenVRRenderLoop>(); OnPollingEvents(); @@ -139,16 +137,14 @@ bool OpenVRDevice::IsApiAvailable() { return vr::VR_IsRuntimeInstalled(); } -mojom::IsolatedXRGamepadProviderFactoryPtr OpenVRDevice::BindGamepadFactory() { - mojom::IsolatedXRGamepadProviderFactoryPtr ret; - gamepad_provider_factory_binding_.Bind(mojo::MakeRequest(&ret)); - return ret; +mojo::PendingRemote<mojom::IsolatedXRGamepadProviderFactory> +OpenVRDevice::BindGamepadFactory() { + return gamepad_provider_factory_receiver_.BindNewPipeAndPassRemote(); } -mojom::XRCompositorHostPtr OpenVRDevice::BindCompositorHost() { - mojom::XRCompositorHostPtr ret; - compositor_host_binding_.Bind(mojo::MakeRequest(&ret)); - return ret; +mojo::PendingRemote<mojom::XRCompositorHost> +OpenVRDevice::BindCompositorHost() { + return compositor_host_receiver_.BindNewPipeAndPassRemote(); } OpenVRDevice::~OpenVRDevice() { @@ -167,7 +163,7 @@ void OpenVRDevice::RequestSession( mojom::XRRuntimeSessionOptionsPtr options, mojom::XRRuntime::RequestSessionCallback callback) { if (!EnsureValidDisplayInfo()) { - std::move(callback).Run(nullptr, nullptr); + std::move(callback).Run(nullptr, mojo::NullRemote()); return; } @@ -177,22 +173,22 @@ void OpenVRDevice::RequestSession( render_loop_->Start(); if (!render_loop_->IsRunning()) { - std::move(callback).Run(nullptr, nullptr); + std::move(callback).Run(nullptr, mojo::NullRemote()); return; } - if (provider_request_) { + if (provider_receiver_) { render_loop_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestGamepadProvider, base::Unretained(render_loop_.get()), - std::move(provider_request_))); + std::move(provider_receiver_))); } - if (overlay_request_) { + if (overlay_receiver_) { render_loop_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestOverlay, base::Unretained(render_loop_.get()), - std::move(overlay_request_))); + std::move(overlay_receiver_))); } } @@ -207,10 +203,12 @@ void OpenVRDevice::RequestSession( &OpenVRDevice::OnPresentationEnded, weak_ptr_factory_.GetWeakPtr()); render_loop_->task_runner()->PostTask( - FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestSession, - base::Unretained(render_loop_.get()), - std::move(on_presentation_ended), - std::move(options), std::move(my_callback))); + FROM_HERE, + base::BindOnce(&XRCompositorCommon::RequestSession, + base::Unretained(render_loop_.get()), + std::move(on_presentation_ended), + base::DoNothing::Repeatedly<mojom::XRVisibilityState>(), + std::move(options), std::move(my_callback))); outstanding_session_requests_count_++; } @@ -253,24 +251,23 @@ void OpenVRDevice::OnRequestSessionResult( outstanding_session_requests_count_--; if (!result) { OnPresentationEnded(); - std::move(callback).Run(nullptr, nullptr); + std::move(callback).Run(nullptr, mojo::NullRemote()); return; } OnStartPresenting(); - mojom::XRSessionControllerPtr session_controller; - exclusive_controller_binding_.Bind(mojo::MakeRequest(&session_controller)); + session->display_info = display_info_.Clone(); + + std::move(callback).Run( + std::move(session), + exclusive_controller_receiver_.BindNewPipeAndPassRemote()); // Use of Unretained is safe because the callback will only occur if the // binding is not destroyed. - exclusive_controller_binding_.set_connection_error_handler( + exclusive_controller_receiver_.set_disconnect_handler( base::BindOnce(&OpenVRDevice::OnPresentingControllerMojoConnectionError, base::Unretained(this))); - - session->display_info = display_info_.Clone(); - - std::move(callback).Run(std::move(session), std::move(session_controller)); } bool OpenVRDevice::IsAvailable() { @@ -278,26 +275,26 @@ bool OpenVRDevice::IsAvailable() { } void OpenVRDevice::GetIsolatedXRGamepadProvider( - mojom::IsolatedXRGamepadProviderRequest provider_request) { + mojo::PendingReceiver<mojom::IsolatedXRGamepadProvider> provider_receiver) { if (render_loop_->IsRunning()) { render_loop_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestGamepadProvider, base::Unretained(render_loop_.get()), - std::move(provider_request))); + std::move(provider_receiver))); } else { - provider_request_ = std::move(provider_request); + provider_receiver_ = std::move(provider_receiver); } } void OpenVRDevice::CreateImmersiveOverlay( - mojom::ImmersiveOverlayRequest overlay_request) { + mojo::PendingReceiver<mojom::ImmersiveOverlay> overlay_receiver) { if (render_loop_->IsRunning()) { render_loop_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestOverlay, base::Unretained(render_loop_.get()), - std::move(overlay_request))); + std::move(overlay_receiver))); } else { - overlay_request_ = std::move(overlay_request); + overlay_receiver_ = std::move(overlay_receiver); } } @@ -316,7 +313,7 @@ void OpenVRDevice::OnPresentingControllerMojoConnectionError() { // TODO(https://crbug.com/875187): Alternatively, we could recreate the // provider on the next session, or look into why the callback gets lost. OnExitPresent(); - exclusive_controller_binding_.Close(); + exclusive_controller_receiver_.reset(); } // Only deal with events that will cause displayInfo changes for now. diff --git a/chromium/device/vr/openvr/openvr_device.h b/chromium/device/vr/openvr/openvr_device.h index d852eea191a..3d02da8e939 100644 --- a/chromium/device/vr/openvr/openvr_device.h +++ b/chromium/device/vr/openvr/openvr_device.h @@ -12,7 +12,9 @@ #include "device/vr/openvr/openvr_api_wrapper.h" #include "device/vr/public/mojom/vr_service.mojom.h" #include "device/vr/vr_device_base.h" -#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver.h" namespace device { @@ -47,8 +49,9 @@ class DEVICE_VR_EXPORT OpenVRDevice bool IsAvailable(); - mojom::IsolatedXRGamepadProviderFactoryPtr BindGamepadFactory(); - mojom::XRCompositorHostPtr BindCompositorHost(); + mojo::PendingRemote<mojom::IsolatedXRGamepadProviderFactory> + BindGamepadFactory(); + mojo::PendingRemote<mojom::XRCompositorHost> BindCompositorHost(); private: // XRSessionController @@ -56,11 +59,12 @@ class DEVICE_VR_EXPORT OpenVRDevice // mojom::IsolatedXRGamepadProviderFactory void GetIsolatedXRGamepadProvider( - mojom::IsolatedXRGamepadProviderRequest provider_request) override; + mojo::PendingReceiver<mojom::IsolatedXRGamepadProvider> provider_receiver) + override; // XRCompositorHost void CreateImmersiveOverlay( - mojom::ImmersiveOverlayRequest overlay_request) override; + mojo::PendingReceiver<mojom::ImmersiveOverlay> overlay_receiver) override; void OnPresentingControllerMojoConnectionError(); void OnPresentationEnded(); @@ -72,14 +76,15 @@ class DEVICE_VR_EXPORT OpenVRDevice std::unique_ptr<OpenVRWrapper> openvr_; scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_; - mojo::Binding<mojom::XRSessionController> exclusive_controller_binding_; + mojo::Receiver<mojom::XRSessionController> exclusive_controller_receiver_{ + this}; - mojo::Binding<mojom::IsolatedXRGamepadProviderFactory> - gamepad_provider_factory_binding_; - mojom::IsolatedXRGamepadProviderRequest provider_request_; + mojo::Receiver<mojom::IsolatedXRGamepadProviderFactory> + gamepad_provider_factory_receiver_{this}; + mojo::PendingReceiver<mojom::IsolatedXRGamepadProvider> provider_receiver_; - mojo::Binding<mojom::XRCompositorHost> compositor_host_binding_; - mojom::ImmersiveOverlayRequest overlay_request_; + mojo::Receiver<mojom::XRCompositorHost> compositor_host_receiver_{this}; + mojo::PendingReceiver<mojom::ImmersiveOverlay> overlay_receiver_; base::WeakPtrFactory<OpenVRDevice> weak_ptr_factory_{this}; diff --git a/chromium/device/vr/openvr/openvr_gamepad_helper.cc b/chromium/device/vr/openvr/openvr_gamepad_helper.cc index 7f7c6682802..313c0706a9d 100644 --- a/chromium/device/vr/openvr/openvr_gamepad_helper.cc +++ b/chromium/device/vr/openvr/openvr_gamepad_helper.cc @@ -351,18 +351,18 @@ class OpenVRGamepadBuilder : public XRStandardGamepadBuilder { profiles_.push_back(name); // Also record information about what this controller actually does in a - // more general sense. - std::string capabilities = ""; + // more general sense. The controller is guaranteed to at least have a + // trigger if we get here. + std::string capabilities = "generic-trigger"; if (HasSecondaryButton()) { - capabilities += "grip-"; + capabilities += "-squeeze"; } if (HasTouchpad()) { - capabilities += "touchpad-"; + capabilities += "-touchpad"; } if (HasThumbstick()) { - capabilities += "thumbstick-"; + capabilities += "-thumbstick"; } - capabilities += "controller"; profiles_.push_back(capabilities); } diff --git a/chromium/device/vr/openvr/openvr_render_loop.cc b/chromium/device/vr/openvr/openvr_render_loop.cc index e3df40e994b..8b13108c747 100644 --- a/chromium/device/vr/openvr/openvr_render_loop.cc +++ b/chromium/device/vr/openvr/openvr_render_loop.cc @@ -4,6 +4,7 @@ #include "device/vr/openvr/openvr_render_loop.h" +#include "base/trace_event/trace_event.h" #include "device/vr/openvr/openvr_api_wrapper.h" #include "device/vr/openvr/openvr_gamepad_helper.h" #include "device/vr/openvr/openvr_type_converters.h" @@ -290,6 +291,9 @@ std::vector<mojom::XRInputSourceStatePtr> OpenVRRenderLoop::GetInputState( controller_state, handedness); state->gamepad = input_source_data.gamepad; + // OpenVR controller are fully 6DoF. + state->emulated_position = false; + // Re-send the controller's description if it's newly active or if the // handedness or profile strings have changed. if (newly_active || @@ -304,9 +308,6 @@ std::vector<mojom::XRInputSourceStatePtr> OpenVRRenderLoop::GetInputState( desc->handedness = handedness; input_active_state.controller_role = controller_role; - // OpenVR controller are fully 6DoF. - desc->emulated_position = false; - // Tweak the pointer transform so that it's angled down from the // grip. This should be a bit more ergonomic. desc->pointer_offset = gfx::Transform(); diff --git a/chromium/device/vr/openxr/openxr_api_wrapper.cc b/chromium/device/vr/openxr/openxr_api_wrapper.cc index 68e8af15e2c..c3ad215b4d9 100644 --- a/chromium/device/vr/openxr/openxr_api_wrapper.cc +++ b/chromium/device/vr/openxr/openxr_api_wrapper.cc @@ -4,13 +4,12 @@ #include "device/vr/openxr/openxr_api_wrapper.h" -#include <directxmath.h> #include <stdint.h> #include <algorithm> #include <array> #include "base/logging.h" -#include "device/vr/openxr/openxr_gamepad_helper.h" +#include "device/vr/openxr/openxr_input_helper.h" #include "device/vr/openxr/openxr_util.h" #include "device/vr/test/test_hook.h" #include "ui/gfx/geometry/point3_f.h" @@ -33,6 +32,7 @@ constexpr uint32_t kNumViews = 2; XrResult CreateInstance(XrInstance* instance) { XrInstanceCreateInfo instance_create_info = {XR_TYPE_INSTANCE_CREATE_INFO}; strcpy_s(instance_create_info.applicationInfo.applicationName, "Chromium"); + instance_create_info.applicationInfo.apiVersion = XR_CURRENT_API_VERSION; // xrCreateInstance validates the list of extensions and returns // XR_ERROR_EXTENSION_NOT_PRESENT if an extension is not supported, @@ -116,7 +116,8 @@ void OpenXrApiWrapper::Reset() { view_configs_.clear(); color_swapchain_images_.clear(); frame_state_ = {}; - views_.clear(); + origin_from_eye_views_.clear(); + head_from_eye_views_.clear(); layer_projection_views_.clear(); } @@ -278,7 +279,7 @@ XrResult OpenXrApiWrapper::PickEnvironmentBlendMode(XrSystemId system) { // objects that may have been created before the failure. XrResult OpenXrApiWrapper::InitSession( const Microsoft::WRL::ComPtr<ID3D11Device>& d3d_device, - std::unique_ptr<OpenXrGamepadHelper>* gamepad_helper) { + std::unique_ptr<OpenXRInputHelper>* input_helper) { DCHECK(d3d_device.Get()); DCHECK(IsInitialized()); @@ -289,7 +290,7 @@ XrResult OpenXrApiWrapper::InitSession( RETURN_IF_XR_FAILED( CreateSpace(XR_REFERENCE_SPACE_TYPE_LOCAL, &local_space_)); RETURN_IF_XR_FAILED(CreateSpace(XR_REFERENCE_SPACE_TYPE_VIEW, &view_space_)); - RETURN_IF_XR_FAILED(CreateGamepadHelper(gamepad_helper)); + RETURN_IF_XR_FAILED(CreateGamepadHelper(input_helper)); // It's ok if stage_space_ fails since not all OpenXR devices are required to // support this reference space. @@ -298,15 +299,16 @@ XrResult OpenXrApiWrapper::InitSession( // Since the objects in these arrays are used on every frame, // we don't want to create and destroy these objects every frame, // so create the number of objects we need and reuse them. - views_.resize(view_configs_.size()); - layer_projection_views_.resize(view_configs_.size()); + origin_from_eye_views_.resize(kNumViews); + head_from_eye_views_.resize(kNumViews); + layer_projection_views_.resize(kNumViews); // Make sure all of the objects we initialized are there. DCHECK(HasSession()); DCHECK(HasColorSwapChain()); DCHECK(HasSpace(XR_REFERENCE_SPACE_TYPE_LOCAL)); DCHECK(HasSpace(XR_REFERENCE_SPACE_TYPE_VIEW)); - DCHECK(gamepad_helper); + DCHECK(input_helper); return xr_result; } @@ -387,12 +389,12 @@ XrResult OpenXrApiWrapper::CreateSpace(XrReferenceSpaceType type, } XrResult OpenXrApiWrapper::CreateGamepadHelper( - std::unique_ptr<OpenXrGamepadHelper>* gamepad_helper) { + std::unique_ptr<OpenXRInputHelper>* input_helper) { DCHECK(HasSession()); DCHECK(HasSpace(XR_REFERENCE_SPACE_TYPE_LOCAL)); - return OpenXrGamepadHelper::CreateOpenXrGamepadHelper( - instance_, session_, local_space_, gamepad_helper); + return OpenXRInputHelper::CreateOpenXRInputHelper(instance_, session_, + local_space_, input_helper); } XrResult OpenXrApiWrapper::BeginSession() { @@ -456,7 +458,7 @@ XrResult OpenXrApiWrapper::EndFrame() { XrCompositionLayerProjection* multi_projection_layer_ptr = &multi_projection_layer; multi_projection_layer.space = local_space_; - multi_projection_layer.viewCount = views_.size(); + multi_projection_layer.viewCount = origin_from_eye_views_.size(); multi_projection_layer.views = layer_projection_views_.data(); XrFrameEndInfo end_frame_info = {XR_TYPE_FRAME_END_INFO}; @@ -475,25 +477,15 @@ XrResult OpenXrApiWrapper::EndFrame() { XrResult OpenXrApiWrapper::UpdateProjectionLayers() { XrResult xr_result; - XrViewState view_state = {XR_TYPE_VIEW_STATE}; - - XrViewLocateInfo view_locate_info = {XR_TYPE_VIEW_LOCATE_INFO}; - - view_locate_info.viewConfigurationType = kSupportedViewConfiguration; - view_locate_info.displayTime = frame_state_.predictedDisplayTime; - view_locate_info.space = local_space_; - - uint32_t view_count = 0; - RETURN_IF_XR_FAILED(xrLocateViews(session_, &view_locate_info, &view_state, - views_.size(), &view_count, views_.data())); + RETURN_IF_XR_FAILED( + LocateViews(XR_REFERENCE_SPACE_TYPE_LOCAL, &origin_from_eye_views_)); + RETURN_IF_XR_FAILED( + LocateViews(XR_REFERENCE_SPACE_TYPE_VIEW, &head_from_eye_views_)); gfx::Size view_size = GetViewSize(); - - DCHECK(view_count <= views_.size()); - DCHECK(view_count <= layer_projection_views_.size()); - - for (uint32_t view_index = 0; view_index < view_count; view_index++) { - const XrView& view = views_[view_index]; + for (uint32_t view_index = 0; view_index < origin_from_eye_views_.size(); + view_index++) { + const XrView& view = origin_from_eye_views_[view_index]; XrCompositionLayerProjectionView& layer_projection_view = layer_projection_views_[view_index]; @@ -516,6 +508,47 @@ XrResult OpenXrApiWrapper::UpdateProjectionLayers() { return xr_result; } +XrResult OpenXrApiWrapper::LocateViews(XrReferenceSpaceType type, + std::vector<XrView>* views) const { + DCHECK(HasSession()); + + XrResult xr_result; + + XrViewState view_state = {XR_TYPE_VIEW_STATE}; + XrViewLocateInfo view_locate_info = {XR_TYPE_VIEW_LOCATE_INFO}; + view_locate_info.viewConfigurationType = kSupportedViewConfiguration; + view_locate_info.displayTime = frame_state_.predictedDisplayTime; + + switch (type) { + case XR_REFERENCE_SPACE_TYPE_LOCAL: + view_locate_info.space = local_space_; + break; + case XR_REFERENCE_SPACE_TYPE_VIEW: + view_locate_info.space = view_space_; + break; + case XR_REFERENCE_SPACE_TYPE_STAGE: + case XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT: + case XR_REFERENCE_SPACE_TYPE_MAX_ENUM: + NOTREACHED(); + } + + std::vector<XrView> new_views(kNumViews); + uint32_t view_count; + RETURN_IF_XR_FAILED(xrLocateViews(session_, &view_locate_info, &view_state, + new_views.size(), &view_count, + new_views.data())); + DCHECK(view_count == kNumViews); + + // If the position or orientation is not valid, don't update the views so that + // the previous valid views are used instead. + if ((view_state.viewStateFlags & XR_VIEW_STATE_POSITION_VALID_BIT) && + (view_state.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT)) { + *views = std::move(new_views); + } + + return xr_result; +} + bool OpenXrApiWrapper::HasPosition() const { DCHECK(IsInitialized()); @@ -544,21 +577,23 @@ XrResult OpenXrApiWrapper::GetHeadPose( XrResult xr_result; - XrSpaceLocation location = {XR_TYPE_SPACE_LOCATION}; - RETURN_IF_XR_FAILED(xrLocateSpace( - view_space_, local_space_, frame_state_.predictedDisplayTime, &location)); + XrSpaceLocation view_from_local = {XR_TYPE_SPACE_LOCATION}; + RETURN_IF_XR_FAILED(xrLocateSpace(view_space_, local_space_, + frame_state_.predictedDisplayTime, + &view_from_local)); - if (location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) { + if (view_from_local.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) { *orientation = gfx::Quaternion( - location.pose.orientation.x, location.pose.orientation.y, - location.pose.orientation.z, location.pose.orientation.w); + view_from_local.pose.orientation.x, view_from_local.pose.orientation.y, + view_from_local.pose.orientation.z, view_from_local.pose.orientation.w); } else { *orientation = base::nullopt; } - if (location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) { - *position = gfx::Point3F(location.pose.position.x, location.pose.position.y, - location.pose.position.z); + if (view_from_local.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) { + *position = gfx::Point3F(view_from_local.pose.position.x, + view_from_local.pose.position.y, + view_from_local.pose.position.z); } else { *position = base::nullopt; } @@ -566,6 +601,13 @@ XrResult OpenXrApiWrapper::GetHeadPose( return xr_result; } +void OpenXrApiWrapper::GetHeadFromEyes(XrView* left, XrView* right) const { + DCHECK(HasSession()); + + *left = head_from_eye_views_[0]; + *right = head_from_eye_views_[1]; +} + XrResult OpenXrApiWrapper::GetLuid(LUID* luid) const { DCHECK(IsInitialized()); @@ -648,17 +690,6 @@ std::string OpenXrApiWrapper::GetRuntimeName() const { } } -const XrView& OpenXrApiWrapper::GetView(uint32_t index) const { - DCHECK(HasSession()); - - // XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO so the OpenXR runtime must have - // returned two view configurations. - DCHECK(views_.size() == kNumViews); - DCHECK(index < kNumViews); - - return views_[index]; -} - XrResult OpenXrApiWrapper::GetStageBounds(XrExtent2Df* stage_bounds) const { DCHECK(stage_bounds); DCHECK(HasSession()); @@ -667,10 +698,11 @@ XrResult OpenXrApiWrapper::GetStageBounds(XrExtent2Df* stage_bounds) const { stage_bounds); } -bool OpenXrApiWrapper::GetStageParameters(XrExtent2Df* stage_bounds, - gfx::Transform* transform) const { +bool OpenXrApiWrapper::GetStageParameters( + XrExtent2Df* stage_bounds, + gfx::Transform* local_from_stage) const { DCHECK(stage_bounds); - DCHECK(transform); + DCHECK(local_from_stage); DCHECK(HasSession()); if (!HasSpace(XR_REFERENCE_SPACE_TYPE_LOCAL)) @@ -682,25 +714,33 @@ bool OpenXrApiWrapper::GetStageParameters(XrExtent2Df* stage_bounds, if (XR_FAILED(GetStageBounds(stage_bounds))) return false; - XrSpaceLocation location = {XR_TYPE_SPACE_LOCATION}; - if (FAILED(xrLocateSpace(stage_space_, local_space_, - frame_state_.predictedDisplayTime, &location)) || - !(location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) || - !(location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT)) { + XrSpaceLocation local_from_stage_location = {XR_TYPE_SPACE_LOCATION}; + if (FAILED(xrLocateSpace(local_space_, stage_space_, + frame_state_.predictedDisplayTime, + &local_from_stage_location)) || + !(local_from_stage_location.locationFlags & + XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) || + !(local_from_stage_location.locationFlags & + XR_SPACE_LOCATION_POSITION_VALID_BIT)) { return false; } // Convert the orientation and translation given by runtime into a // transformation matrix. - gfx::DecomposedTransform seat_to_standing_decomp; - seat_to_standing_decomp.quaternion = - gfx::Quaternion(location.pose.orientation.x, location.pose.orientation.y, - location.pose.orientation.z, location.pose.orientation.w); - seat_to_standing_decomp.translate[0] = location.pose.position.x; - seat_to_standing_decomp.translate[1] = location.pose.position.y; - seat_to_standing_decomp.translate[2] = location.pose.position.z; - - *transform = gfx::ComposeTransform(seat_to_standing_decomp); + gfx::DecomposedTransform local_from_stage_decomp; + local_from_stage_decomp.quaternion = + gfx::Quaternion(local_from_stage_location.pose.orientation.x, + local_from_stage_location.pose.orientation.y, + local_from_stage_location.pose.orientation.z, + local_from_stage_location.pose.orientation.w); + local_from_stage_decomp.translate[0] = + local_from_stage_location.pose.position.x; + local_from_stage_decomp.translate[1] = + local_from_stage_location.pose.position.y; + local_from_stage_decomp.translate[2] = + local_from_stage_location.pose.position.z; + + *local_from_stage = gfx::ComposeTransform(local_from_stage_decomp); return true; } diff --git a/chromium/device/vr/openxr/openxr_api_wrapper.h b/chromium/device/vr/openxr/openxr_api_wrapper.h index 3c64f5bbcab..24ed4616b3a 100644 --- a/chromium/device/vr/openxr/openxr_api_wrapper.h +++ b/chromium/device/vr/openxr/openxr_api_wrapper.h @@ -26,7 +26,7 @@ class Transform; namespace device { -class OpenXrGamepadHelper; +class OpenXRInputHelper; class VRTestHook; class ServiceTestHook; @@ -46,22 +46,22 @@ class OpenXrApiWrapper { bool session_ended() const { return session_ended_; } XrResult InitSession(const Microsoft::WRL::ComPtr<ID3D11Device>& d3d_device, - std::unique_ptr<OpenXrGamepadHelper>* gamepad_helper); + std::unique_ptr<OpenXRInputHelper>* input_helper); XrResult BeginFrame(Microsoft::WRL::ComPtr<ID3D11Texture2D>* texture); XrResult EndFrame(); XrResult GetHeadPose(base::Optional<gfx::Quaternion>* orientation, base::Optional<gfx::Point3F>* position) const; + void GetHeadFromEyes(XrView* left, XrView* right) const; bool HasPosition() const; gfx::Size GetViewSize() const; - const XrView& GetView(uint32_t index) const; XrTime GetPredictedDisplayTime() const; XrResult GetLuid(LUID* luid) const; std::string GetRuntimeName() const; bool GetStageParameters(XrExtent2Df* stage_bounds, - gfx::Transform* standing_transform) const; + gfx::Transform* local_from_stage) const; static void DEVICE_VR_EXPORT SetTestHook(VRTestHook* hook); @@ -79,10 +79,12 @@ class OpenXrApiWrapper { XrResult CreateSwapchain(); XrResult CreateSpace(XrReferenceSpaceType type, XrSpace* space); XrResult CreateGamepadHelper( - std::unique_ptr<OpenXrGamepadHelper>* gamepad_helper); + std::unique_ptr<OpenXRInputHelper>* input_helper); XrResult BeginSession(); XrResult UpdateProjectionLayers(); + XrResult LocateViews(XrReferenceSpaceType type, + std::vector<XrView>* views) const; bool HasInstance() const; bool HasSystem() const; @@ -121,7 +123,8 @@ class OpenXrApiWrapper { // These objects store information about the current frame. They're // valid only while a session is active, and they are updated each frame. XrFrameState frame_state_; - std::vector<XrView> views_; + std::vector<XrView> origin_from_eye_views_; + std::vector<XrView> head_from_eye_views_; std::vector<XrCompositionLayerProjectionView> layer_projection_views_; DISALLOW_COPY_AND_ASSIGN(OpenXrApiWrapper); diff --git a/chromium/device/vr/openxr/openxr_controller.cc b/chromium/device/vr/openxr/openxr_controller.cc index 4fae1658b5c..f8e64e1c0f2 100644 --- a/chromium/device/vr/openxr/openxr_controller.cc +++ b/chromium/device/vr/openxr/openxr_controller.cc @@ -7,8 +7,12 @@ #include <stdint.h> #include "base/logging.h" +#include "device/gamepad/public/cpp/gamepads.h" #include "device/vr/openxr/openxr_util.h" #include "device/vr/public/mojom/vr_service.mojom.h" +#include "device/vr/util/xr_standard_gamepad_builder.h" +#include "ui/gfx/geometry/quaternion.h" +#include "ui/gfx/transform_util.h" namespace device { @@ -17,22 +21,32 @@ namespace { constexpr char kMicrosoftInteractionProfileName[] = "/interaction_profiles/microsoft/motion_controller"; -const char* GetStringFromType(OpenXrControllerType type) { +const char* GetStringFromType(OpenXrHandednessType type) { switch (type) { - case OpenXrControllerType::kLeft: + case OpenXrHandednessType::kLeft: return "left"; - case OpenXrControllerType::kRight: + case OpenXrHandednessType::kRight: return "right"; - default: + case OpenXrHandednessType::kCount: NOTREACHED(); return ""; } } +mojom::XRGamepadButtonPtr GetXRGamepadButtonPtr( + base::Optional<GamepadButton> button) { + mojom::XRGamepadButtonPtr ret = mojom::XRGamepadButton::New(); + ret->pressed = button->pressed; + ret->touched = button->touched; + ret->value = button->value; + return ret; +} + } // namespace OpenXrController::OpenXrController() - : type_(OpenXrControllerType::kCount), // COUNT refers to invalid. + : description_(nullptr), + type_(OpenXrHandednessType::kCount), // COUNT refers to invalid. session_(XR_NULL_HANDLE), action_set_(XR_NULL_HANDLE), grip_pose_action_{XR_NULL_HANDLE}, @@ -51,15 +65,15 @@ OpenXrController::~OpenXrController() { } XrResult OpenXrController::Initialize( - OpenXrControllerType type, + OpenXrHandednessType type, XrInstance instance, XrSession session, std::map<XrPath, std::vector<XrActionSuggestedBinding>>* bindings) { type_ = type; session_ = session; - std::string type_string = GetStringFromType(type_); + std::string handedness_string = GetStringFromType(type_); - std::string action_set_name = type_string + "_action_set"; + std::string action_set_name = handedness_string + "_action_set"; XrActionSetCreateInfo action_set_create_info = { XR_TYPE_ACTION_SET_CREATE_INFO}; @@ -75,20 +89,15 @@ XrResult OpenXrController::Initialize( RETURN_IF_XR_FAILED( xrCreateActionSet(instance, &action_set_create_info, &action_set_)); - RETURN_IF_XR_FAILED( - InitializeMicrosoftMotionControllers(instance, type_string, bindings)); + RETURN_IF_XR_FAILED(InitializeMicrosoftMotionControllerActions( + instance, handedness_string, bindings)); + + RETURN_IF_XR_FAILED(InitializeMicrosoftMotionControllerSpaces()); - XrActionSpaceCreateInfo action_space_create_info = {}; - action_space_create_info.type = XR_TYPE_ACTION_SPACE_CREATE_INFO; - action_space_create_info.action = grip_pose_action_; - action_space_create_info.subactionPath = XR_NULL_PATH; - action_space_create_info.poseInActionSpace = PoseIdentity(); - RETURN_IF_XR_FAILED(xrCreateActionSpace(session_, &action_space_create_info, - &grip_pose_space_)); return xr_result; } -XrResult OpenXrController::InitializeMicrosoftMotionControllers( +XrResult OpenXrController::InitializeMicrosoftMotionControllerActions( XrInstance instance, const std::string& type_string, std::map<XrPath, std::vector<XrActionSuggestedBinding>>* bindings) { @@ -99,28 +108,40 @@ XrResult OpenXrController::InitializeMicrosoftMotionControllers( RETURN_IF_XR_FAILED(CreateAction( instance, XR_ACTION_TYPE_BOOLEAN_INPUT, kMicrosoftInteractionProfileName, - binding_prefix + "trackpad/click", name_prefix + "trackpad_button_press", - &(button_action_map_[OpenXrButtonType::kTrackpad].press_action), + binding_prefix + "trigger/value", name_prefix + "trigger_button_press", + &(button_action_map_[OpenXrButtonType::kTrigger].press_action), + bindings)); + + RETURN_IF_XR_FAILED(CreateAction( + instance, XR_ACTION_TYPE_FLOAT_INPUT, kMicrosoftInteractionProfileName, + binding_prefix + "trigger/value", name_prefix + "trigger_button_value", + &(button_action_map_[OpenXrButtonType::kTrigger].value_action), bindings)); RETURN_IF_XR_FAILED(CreateAction( instance, XR_ACTION_TYPE_BOOLEAN_INPUT, kMicrosoftInteractionProfileName, - binding_prefix + "trigger/value", name_prefix + "trigger_button_press", - &(button_action_map_[OpenXrButtonType::kTrigger].press_action), + binding_prefix + "thumbstick/click", + name_prefix + "thumbstick_button_press", + &(button_action_map_[OpenXrButtonType::kThumbstick].press_action), bindings)); - // In OpenXR, this input is called 'squeeze'. However, the rest of Chromium - // uses the term 'grip' for this button, so the OpenXrButtonType is named - // kGrip for consistency. RETURN_IF_XR_FAILED(CreateAction( instance, XR_ACTION_TYPE_BOOLEAN_INPUT, kMicrosoftInteractionProfileName, binding_prefix + "squeeze/click", name_prefix + "squeeze_button_press", - &(button_action_map_[OpenXrButtonType::kGrip].press_action), bindings)); + &(button_action_map_[OpenXrButtonType::kSqueeze].press_action), + bindings)); + + RETURN_IF_XR_FAILED(CreateAction( + instance, XR_ACTION_TYPE_BOOLEAN_INPUT, kMicrosoftInteractionProfileName, + binding_prefix + "trackpad/click", name_prefix + "trackpad_button_press", + &(button_action_map_[OpenXrButtonType::kTrackpad].press_action), + bindings)); RETURN_IF_XR_FAILED(CreateAction( instance, XR_ACTION_TYPE_BOOLEAN_INPUT, kMicrosoftInteractionProfileName, - binding_prefix + "menu/click", name_prefix + "menu_button_press", - &(button_action_map_[OpenXrButtonType::kMenu].press_action), bindings)); + binding_prefix + "trackpad/touch", name_prefix + "trackpad_button_touch", + &(button_action_map_[OpenXrButtonType::kTrackpad].touch_action), + bindings)); RETURN_IF_XR_FAILED(CreateAction( instance, XR_ACTION_TYPE_VECTOR2F_INPUT, kMicrosoftInteractionProfileName, @@ -137,20 +158,36 @@ XrResult OpenXrController::InitializeMicrosoftMotionControllers( kMicrosoftInteractionProfileName, binding_prefix + "grip", name_prefix + "grip_pose", &grip_pose_action_, bindings)); + RETURN_IF_XR_FAILED( + CreateAction(instance, XR_ACTION_TYPE_POSE_INPUT, + kMicrosoftInteractionProfileName, binding_prefix + "aim", + name_prefix + "aim_pose", &pointer_pose_action_, bindings)); + + return xr_result; +} + +XrResult OpenXrController::InitializeMicrosoftMotionControllerSpaces() { + XrResult xr_result; + + RETURN_IF_XR_FAILED(CreateActionSpace(grip_pose_action_, &grip_pose_space_)); + + RETURN_IF_XR_FAILED( + CreateActionSpace(pointer_pose_action_, &pointer_pose_space_)); + return xr_result; } -uint32_t OpenXrController::GetID() const { +uint32_t OpenXrController::GetId() const { return static_cast<uint32_t>(type_); } device::mojom::XRHandedness OpenXrController::GetHandness() const { switch (type_) { - case OpenXrControllerType::kLeft: + case OpenXrHandednessType::kLeft: return device::mojom::XRHandedness::LEFT; - case OpenXrControllerType::kRight: + case OpenXrHandednessType::kRight: return device::mojom::XRHandedness::RIGHT; - default: + case OpenXrHandednessType::kCount: // LEFT controller and RIGHT controller are currently the only supported // controllers. In the future, other controllers such as sound (which // does not have a handedness) will be added here. @@ -159,6 +196,32 @@ device::mojom::XRHandedness OpenXrController::GetHandness() const { } } +device::mojom::XRInputSourceDescriptionPtr OpenXrController::GetDescription( + XrTime predicted_display_time) { + if (!description_) { + description_ = device::mojom::XRInputSourceDescription::New(); + description_->handedness = GetHandness(); + description_->target_ray_mode = device::mojom::XRTargetRayMode::POINTING; + // TODO(crbug.com/1006072): + // Query USB vendor and product ID From OpenXR. + description_->profiles.push_back("windows-mixed-reality"); + // This makes it clear that the controller actually has a squeeze button, + // trigger button, a touchpad and a thumbstick. Otherwise, it's ambiguous + // whether slots like the touchpad buttons + axes are hooked up vs just + // placeholders. + description_->profiles.push_back( + "generic-trigger-squeeze-touchpad-thumbstick"); + } + // pointer_offset only need to be set once unless something changed about + // controller. + if (!description_->pointer_offset) { + description_->pointer_offset = + GetPointerFromGripTransform(predicted_display_time); + } + + return description_.Clone(); +} + std::vector<mojom::XRGamepadButtonPtr> OpenXrController::GetWebVrButtons() const { std::vector<mojom::XRGamepadButtonPtr> buttons; @@ -166,37 +229,51 @@ std::vector<mojom::XRGamepadButtonPtr> OpenXrController::GetWebVrButtons() constexpr uint32_t kNumButtons = static_cast<uint32_t>(OpenXrButtonType::kMaxValue) + 1; for (uint32_t i = 0; i < kNumButtons; i++) { - mojom::XRGamepadButtonPtr mojo_button_ptr = + base::Optional<GamepadButton> button = GetButton(static_cast<OpenXrButtonType>(i)); - if (!mojo_button_ptr) { + if (button) { + buttons.push_back(GetXRGamepadButtonPtr(button.value())); + } else { return {}; } - - buttons.push_back(std::move(mojo_button_ptr)); } return buttons; } -mojom::XRGamepadButtonPtr OpenXrController::GetButton( +base::Optional<GamepadButton> OpenXrController::GetButton( OpenXrButtonType type) const { + GamepadButton ret; + + DCHECK(button_action_map_.count(type) == 1); + XrActionStateBoolean press_state_bool = {XR_TYPE_ACTION_STATE_BOOLEAN}; if (XR_FAILED(QueryState(button_action_map_.at(type).press_action, &press_state_bool)) || !press_state_bool.isActive) { - return nullptr; + return base::nullopt; + } + ret.pressed = press_state_bool.currentState; + + XrActionStateBoolean touch_state_bool = {XR_TYPE_ACTION_STATE_BOOLEAN}; + if (button_action_map_.at(type).touch_action != XR_NULL_HANDLE && + XR_SUCCEEDED(QueryState(button_action_map_.at(type).touch_action, + &touch_state_bool)) && + touch_state_bool.isActive) { + ret.touched = touch_state_bool.currentState; + } else { + ret.touched = ret.pressed; } - return GetGamepadButton(press_state_bool); -} - -mojom::XRGamepadButtonPtr OpenXrController::GetGamepadButton( - const XrActionStateBoolean& action_state) const { - mojom::XRGamepadButtonPtr ret = mojom::XRGamepadButton::New(); - bool button_pressed = action_state.currentState; - ret->touched = button_pressed; - ret->pressed = button_pressed; - ret->value = button_pressed ? 1.0 : 0.0; + XrActionStateFloat value_state_float = {XR_TYPE_ACTION_STATE_FLOAT}; + if (button_action_map_.at(type).value_action != XR_NULL_HANDLE && + XR_SUCCEEDED(QueryState(button_action_map_.at(type).value_action, + &value_state_float)) && + value_state_float.isActive) { + ret.value = value_state_float.currentState; + } else { + ret.value = ret.pressed ? 1.0 : 0.0; + } return ret; } @@ -237,43 +314,85 @@ mojom::VRPosePtr OpenXrController::GetPose(XrTime predicted_display_time, return nullptr; } - XrSpaceVelocity velocity = {XR_TYPE_SPACE_VELOCITY}; - XrSpaceLocation space_location = {XR_TYPE_SPACE_LOCATION}; - space_location.next = &velocity; + XrSpaceVelocity local_from_grip_speed = {XR_TYPE_SPACE_VELOCITY}; + XrSpaceLocation local_from_grip = {XR_TYPE_SPACE_LOCATION}; + local_from_grip.next = &local_from_grip_speed; if (XR_FAILED(xrLocateSpace(grip_pose_space_, local_space, - predicted_display_time, &space_location))) { + predicted_display_time, &local_from_grip))) { return nullptr; } mojom::VRPosePtr pose = mojom::VRPose::New(); - if (space_location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) { - pose->position = gfx::Point3F(space_location.pose.position.x, - space_location.pose.position.y, - space_location.pose.position.z); + if (local_from_grip.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) { + pose->position = gfx::Point3F(local_from_grip.pose.position.x, + local_from_grip.pose.position.y, + local_from_grip.pose.position.z); } - if (space_location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) { + if (local_from_grip.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) { pose->orientation = gfx::Quaternion( - space_location.pose.orientation.x, space_location.pose.orientation.y, - space_location.pose.orientation.z, space_location.pose.orientation.w); + local_from_grip.pose.orientation.x, local_from_grip.pose.orientation.y, + local_from_grip.pose.orientation.z, local_from_grip.pose.orientation.w); } - if (velocity.velocityFlags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) { + if (local_from_grip_speed.velocityFlags & + XR_SPACE_VELOCITY_LINEAR_VALID_BIT) { pose->linear_velocity = - gfx::Vector3dF(velocity.linearVelocity.x, velocity.linearVelocity.y, - velocity.linearVelocity.z); + gfx::Vector3dF(local_from_grip_speed.linearVelocity.x, + local_from_grip_speed.linearVelocity.y, + local_from_grip_speed.linearVelocity.z); } - if (velocity.velocityFlags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) { + if (local_from_grip_speed.velocityFlags & + XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) { pose->angular_velocity = - gfx::Vector3dF(velocity.angularVelocity.x, velocity.angularVelocity.y, - velocity.angularVelocity.z); + gfx::Vector3dF(local_from_grip_speed.angularVelocity.x, + local_from_grip_speed.angularVelocity.y, + local_from_grip_speed.angularVelocity.z); } return pose; } +base::Optional<gfx::Transform> OpenXrController::GetMojoFromGripTransform( + XrTime predicted_display_time, + XrSpace local_space) const { + return GetTransformFromSpaces(predicted_display_time, grip_pose_space_, + local_space); +} + +base::Optional<gfx::Transform> OpenXrController::GetPointerFromGripTransform( + XrTime predicted_display_time) const { + return GetTransformFromSpaces(predicted_display_time, pointer_pose_space_, + grip_pose_space_); +} + +base::Optional<gfx::Transform> OpenXrController::GetTransformFromSpaces( + XrTime predicted_display_time, + XrSpace target, + XrSpace origin) const { + XrSpaceLocation location = {XR_TYPE_SPACE_LOCATION}; + if (FAILED( + xrLocateSpace(target, origin, predicted_display_time, &location)) || + !(location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) || + !(location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT)) { + return base::nullopt; + } + + // Convert the orientation and translation given by runtime into a + // transformation matrix. + gfx::DecomposedTransform decomp; + decomp.quaternion = + gfx::Quaternion(location.pose.orientation.x, location.pose.orientation.y, + location.pose.orientation.z, location.pose.orientation.w); + decomp.translate[0] = location.pose.position.x; + decomp.translate[1] = location.pose.position.y; + decomp.translate[2] = location.pose.position.z; + + return gfx::ComposeTransform(decomp); +} + XrActionSet OpenXrController::GetActionSet() const { return action_set_; } @@ -308,4 +427,13 @@ XrResult OpenXrController::CreateAction( return xr_result; } +XrResult OpenXrController::CreateActionSpace(XrAction action, XrSpace* space) { + XrActionSpaceCreateInfo action_space_create_info = {}; + action_space_create_info.type = XR_TYPE_ACTION_SPACE_CREATE_INFO; + action_space_create_info.action = action; + action_space_create_info.subactionPath = XR_NULL_PATH; + action_space_create_info.poseInActionSpace = PoseIdentity(); + return xrCreateActionSpace(session_, &action_space_create_info, space); +} + } // namespace device diff --git a/chromium/device/vr/openxr/openxr_controller.h b/chromium/device/vr/openxr/openxr_controller.h index 7b430173111..8d836b41ce4 100644 --- a/chromium/device/vr/openxr/openxr_controller.h +++ b/chromium/device/vr/openxr/openxr_controller.h @@ -8,15 +8,16 @@ #include <stdint.h> #include <unordered_map> +#include "base/optional.h" #include "device/vr/public/mojom/isolated_xr_service.mojom.h" -#include "device/vr/util/gamepad_builder.h" #include "third_party/openxr/src/include/openxr/openxr.h" +#include "ui/gfx/transform.h" namespace device { constexpr uint32_t kAxisDimensions = 2; -enum class OpenXrControllerType { +enum class OpenXrHandednessType { kLeft = 0, kRight = 1, kCount = 2, @@ -24,9 +25,9 @@ enum class OpenXrControllerType { enum class OpenXrButtonType { kTrigger = 0, - kTrackpad = 1, - kGrip = 2, - kMenu = 3, + kSqueeze = 1, + kTrackpad = 2, + kThumbstick = 3, kMaxValue = 3, }; @@ -42,34 +43,50 @@ class OpenXrController { ~OpenXrController(); XrResult Initialize( - OpenXrControllerType type, + OpenXrHandednessType type, XrInstance instance, XrSession session, std::map<XrPath, std::vector<XrActionSuggestedBinding>>* bindings); XrActionSet GetActionSet() const; - uint32_t GetID() const; + uint32_t GetId() const; device::mojom::XRHandedness GetHandness() const; + device::mojom::XRInputSourceDescriptionPtr GetDescription( + XrTime predicted_display_time); + + base::Optional<GamepadButton> GetButton(OpenXrButtonType type) const; std::vector<mojom::XRGamepadButtonPtr> GetWebVrButtons() const; + std::vector<double> GetAxis(OpenXrAxisType type) const; std::vector<double> GetWebVrAxes() const; mojom::VRPosePtr GetPose(XrTime predicted_display_time, XrSpace local_space) const; + base::Optional<gfx::Transform> GetMojoFromGripTransform( + XrTime predicted_display_time, + XrSpace local_space) const; + private: // ActionButton struct is used to store all XrAction that is related to the // button. For example, we may need to query the analog value for button press // which require a seperate XrAction than the current boolean XrAction. struct ActionButton { XrAction press_action; - ActionButton() : press_action(XR_NULL_HANDLE) {} + XrAction touch_action; + XrAction value_action; + ActionButton() + : press_action(XR_NULL_HANDLE), + touch_action(XR_NULL_HANDLE), + value_action(XR_NULL_HANDLE) {} }; - XrResult InitializeMicrosoftMotionControllers( + XrResult InitializeMicrosoftMotionControllerActions( XrInstance instance, const std::string& type_string, std::map<XrPath, std::vector<XrActionSuggestedBinding>>* bindings); + XrResult InitializeMicrosoftMotionControllerSpaces(); + XrResult CreateAction( XrInstance instance, XrActionType type, @@ -79,6 +96,16 @@ class OpenXrController { XrAction* action, std::map<XrPath, std::vector<XrActionSuggestedBinding>>* bindings); + XrResult CreateActionSpace(XrAction action, XrSpace* space); + + base::Optional<gfx::Transform> GetPointerFromGripTransform( + XrTime predicted_display_time) const; + + base::Optional<gfx::Transform> GetTransformFromSpaces( + XrTime predicted_display_time, + XrSpace target, + XrSpace origin) const; + template <typename T> XrResult QueryState(XrAction action, T* action_state) const { // this function should never be called because each valid XrActionState @@ -88,6 +115,16 @@ class OpenXrController { } template <> + XrResult QueryState<XrActionStateFloat>( + XrAction action, + XrActionStateFloat* action_state) const { + action_state->type = XR_TYPE_ACTION_STATE_FLOAT; + XrActionStateGetInfo get_info = {XR_TYPE_ACTION_STATE_GET_INFO}; + get_info.action = action; + return xrGetActionStateFloat(session_, &get_info, action_state); + } + + template <> XrResult QueryState<XrActionStateBoolean>( XrAction action, XrActionStateBoolean* action_state) const { @@ -117,16 +154,15 @@ class OpenXrController { return xrGetActionStatePose(session_, &get_info, action_state); } - std::vector<double> GetAxis(OpenXrAxisType type) const; - mojom::XRGamepadButtonPtr GetButton(OpenXrButtonType type) const; - mojom::XRGamepadButtonPtr GetGamepadButton( - const XrActionStateBoolean& action_state) const; + device::mojom::XRInputSourceDescriptionPtr description_; - OpenXrControllerType type_; + OpenXrHandednessType type_; XrSession session_; XrActionSet action_set_; XrAction grip_pose_action_; XrSpace grip_pose_space_; + XrAction pointer_pose_action_; + XrSpace pointer_pose_space_; std::unordered_map<OpenXrButtonType, ActionButton> button_action_map_; std::unordered_map<OpenXrAxisType, XrAction> axis_action_map_; diff --git a/chromium/device/vr/openxr/openxr_device.cc b/chromium/device/vr/openxr/openxr_device.cc index 2983f9ce7ca..edfa4dc9097 100644 --- a/chromium/device/vr/openxr/openxr_device.cc +++ b/chromium/device/vr/openxr/openxr_device.cc @@ -10,6 +10,7 @@ #include "device/vr/openxr/openxr_api_wrapper.h" #include "device/vr/openxr/openxr_render_loop.h" #include "device/vr/util/transform_utils.h" +#include "mojo/public/cpp/bindings/pending_remote.h" namespace device { @@ -85,9 +86,6 @@ bool OpenXrDevice::IsApiAvailable() { OpenXrDevice::OpenXrDevice() : VRDeviceBase(device::mojom::XRDeviceId::OPENXR_DEVICE_ID), - exclusive_controller_binding_(this), - gamepad_provider_factory_binding_(this), - compositor_host_binding_(this), weak_ptr_factory_(this) { SetVRDisplayInfo(CreateFakeVRDisplayInfo(GetId())); } @@ -101,16 +99,14 @@ OpenXrDevice::~OpenXrDevice() { } } -mojom::IsolatedXRGamepadProviderFactoryPtr OpenXrDevice::BindGamepadFactory() { - mojom::IsolatedXRGamepadProviderFactoryPtr ret; - gamepad_provider_factory_binding_.Bind(mojo::MakeRequest(&ret)); - return ret; +mojo::PendingRemote<mojom::IsolatedXRGamepadProviderFactory> +OpenXrDevice::BindGamepadFactory() { + return gamepad_provider_factory_receiver_.BindNewPipeAndPassRemote(); } -mojom::XRCompositorHostPtr OpenXrDevice::BindCompositorHost() { - mojom::XRCompositorHostPtr ret; - compositor_host_binding_.Bind(mojo::MakeRequest(&ret)); - return ret; +mojo::PendingRemote<mojom::XRCompositorHost> +OpenXrDevice::BindCompositorHost() { + return compositor_host_receiver_.BindNewPipeAndPassRemote(); } void OpenXrDevice::EnsureRenderLoop() { @@ -132,22 +128,22 @@ void OpenXrDevice::RequestSession( render_loop_->Start(); if (!render_loop_->IsRunning()) { - std::move(callback).Run(nullptr, nullptr); + std::move(callback).Run(nullptr, mojo::NullRemote()); return; } - if (provider_request_) { + if (provider_receiver_) { render_loop_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestGamepadProvider, base::Unretained(render_loop_.get()), - std::move(provider_request_))); + std::move(provider_receiver_))); } - if (overlay_request_) { + if (overlay_receiver_) { render_loop_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestOverlay, base::Unretained(render_loop_.get()), - std::move(overlay_request_))); + std::move(overlay_receiver_))); } } @@ -160,10 +156,12 @@ void OpenXrDevice::RequestSession( // a method and cannot take nullptr, so passing in base::DoNothing::Once() // for on_presentation_ended render_loop_->task_runner()->PostTask( - FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestSession, - base::Unretained(render_loop_.get()), - base::DoNothing::Once(), std::move(options), - std::move(my_callback))); + FROM_HERE, + base::BindOnce(&XRCompositorCommon::RequestSession, + base::Unretained(render_loop_.get()), + base::DoNothing::Once(), + base::DoNothing::Repeatedly<mojom::XRVisibilityState>(), + std::move(options), std::move(my_callback))); } void OpenXrDevice::OnRequestSessionResult( @@ -171,21 +169,12 @@ void OpenXrDevice::OnRequestSessionResult( bool result, mojom::XRSessionPtr session) { if (!result) { - std::move(callback).Run(nullptr, nullptr); + std::move(callback).Run(nullptr, mojo::NullRemote()); return; } OnStartPresenting(); - 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(&OpenXrDevice::OnPresentingControllerMojoConnectionError, - base::Unretained(this))); - EnsureRenderLoop(); gfx::Size view_size = render_loop_->GetViewSize(); display_info_->left_eye->render_width = view_size.width(); @@ -194,7 +183,15 @@ void OpenXrDevice::OnRequestSessionResult( display_info_->right_eye->render_height = view_size.height(); session->display_info = display_info_.Clone(); - std::move(callback).Run(std::move(session), std::move(session_controller)); + std::move(callback).Run( + std::move(session), + exclusive_controller_receiver_.BindNewPipeAndPassRemote()); + + // Use of Unretained is safe because the callback will only occur if the + // binding is not destroyed. + exclusive_controller_receiver_.set_disconnect_handler( + base::BindOnce(&OpenXrDevice::OnPresentingControllerMojoConnectionError, + base::Unretained(this))); } void OpenXrDevice::OnPresentingControllerMojoConnectionError() { @@ -206,7 +203,7 @@ void OpenXrDevice::OnPresentingControllerMojoConnectionError() { base::Unretained(render_loop_.get()))); } OnExitPresent(); - exclusive_controller_binding_.Close(); + exclusive_controller_receiver_.reset(); } void OpenXrDevice::SetFrameDataRestricted(bool restricted) { @@ -215,28 +212,28 @@ void OpenXrDevice::SetFrameDataRestricted(bool restricted) { } void OpenXrDevice::GetIsolatedXRGamepadProvider( - mojom::IsolatedXRGamepadProviderRequest provider_request) { + mojo::PendingReceiver<mojom::IsolatedXRGamepadProvider> provider_receiver) { EnsureRenderLoop(); if (render_loop_->IsRunning()) { render_loop_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestGamepadProvider, base::Unretained(render_loop_.get()), - std::move(provider_request))); + std::move(provider_receiver))); } else { - provider_request_ = std::move(provider_request); + provider_receiver_ = std::move(provider_receiver); } } void OpenXrDevice::CreateImmersiveOverlay( - mojom::ImmersiveOverlayRequest overlay_request) { + mojo::PendingReceiver<mojom::ImmersiveOverlay> overlay_receiver) { EnsureRenderLoop(); if (render_loop_->IsRunning()) { render_loop_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestOverlay, base::Unretained(render_loop_.get()), - std::move(overlay_request))); + std::move(overlay_receiver))); } else { - overlay_request_ = std::move(overlay_request); + overlay_receiver_ = std::move(overlay_receiver); } } diff --git a/chromium/device/vr/openxr/openxr_device.h b/chromium/device/vr/openxr/openxr_device.h index 3f4706c19a3..c9fa1dd4174 100644 --- a/chromium/device/vr/openxr/openxr_device.h +++ b/chromium/device/vr/openxr/openxr_device.h @@ -10,6 +10,9 @@ #include "base/macros.h" #include "device/vr/public/mojom/vr_service.mojom.h" #include "device/vr/vr_device_base.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver.h" namespace device { @@ -32,8 +35,9 @@ class DEVICE_VR_EXPORT OpenXrDevice mojom::XRRuntimeSessionOptionsPtr options, mojom::XRRuntime::RequestSessionCallback callback) override; - mojom::IsolatedXRGamepadProviderFactoryPtr BindGamepadFactory(); - mojom::XRCompositorHostPtr BindCompositorHost(); + mojo::PendingRemote<mojom::IsolatedXRGamepadProviderFactory> + BindGamepadFactory(); + mojo::PendingRemote<mojom::XRCompositorHost> BindCompositorHost(); private: // XRSessionController @@ -41,11 +45,12 @@ class DEVICE_VR_EXPORT OpenXrDevice // mojom::IsolatedXRGamepadProviderFactory void GetIsolatedXRGamepadProvider( - mojom::IsolatedXRGamepadProviderRequest provider_request) override; + mojo::PendingReceiver<mojom::IsolatedXRGamepadProvider> provider_receiver) + override; // XRCompositorHost void CreateImmersiveOverlay( - mojom::ImmersiveOverlayRequest overlay_request) override; + mojo::PendingReceiver<mojom::ImmersiveOverlay> overlay_receiver) override; void EnsureRenderLoop(); @@ -56,14 +61,15 @@ class DEVICE_VR_EXPORT OpenXrDevice std::unique_ptr<OpenXrRenderLoop> render_loop_; - mojo::Binding<mojom::XRSessionController> exclusive_controller_binding_; + mojo::Receiver<mojom::XRSessionController> exclusive_controller_receiver_{ + this}; - mojo::Binding<mojom::IsolatedXRGamepadProviderFactory> - gamepad_provider_factory_binding_; - mojom::IsolatedXRGamepadProviderRequest provider_request_; + mojo::Receiver<mojom::IsolatedXRGamepadProviderFactory> + gamepad_provider_factory_receiver_{this}; + mojo::PendingReceiver<mojom::IsolatedXRGamepadProvider> provider_receiver_; - mojo::Binding<mojom::XRCompositorHost> compositor_host_binding_; - mojom::ImmersiveOverlayRequest overlay_request_; + mojo::Receiver<mojom::XRCompositorHost> compositor_host_receiver_{this}; + mojo::PendingReceiver<mojom::ImmersiveOverlay> overlay_receiver_; base::WeakPtrFactory<OpenXrDevice> weak_ptr_factory_; diff --git a/chromium/device/vr/openxr/openxr_gamepad_helper.cc b/chromium/device/vr/openxr/openxr_gamepad_helper.cc deleted file mode 100644 index 418d9f779d1..00000000000 --- a/chromium/device/vr/openxr/openxr_gamepad_helper.cc +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2019 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/openxr/openxr_gamepad_helper.h" - -#include "device/vr/openxr/openxr_util.h" - -namespace device { - -XrResult OpenXrGamepadHelper::CreateOpenXrGamepadHelper( - XrInstance instance, - XrSession session, - XrSpace local_space, - std::unique_ptr<OpenXrGamepadHelper>* helper) { - XrResult xr_result; - - std::unique_ptr<OpenXrGamepadHelper> new_helper = - std::make_unique<OpenXrGamepadHelper>(session, local_space); - - // This map is used to store bindings for different kinds of interaction - // profiles. This allows the runtime to choose a different input sources based - // on availability. - std::map<XrPath, std::vector<XrActionSuggestedBinding>> bindings; - DCHECK(new_helper->controllers_.size() == - new_helper->active_action_sets_.size()); - for (size_t i = 0; i < new_helper->controllers_.size(); i++) { - RETURN_IF_XR_FAILED(new_helper->controllers_[i].Initialize( - static_cast<OpenXrControllerType>(i), instance, session, &bindings)); - - new_helper->active_action_sets_[i].actionSet = - new_helper->controllers_[i].GetActionSet(); - new_helper->active_action_sets_[i].subactionPath = XR_NULL_PATH; - } - - for (auto it = bindings.begin(); it != bindings.end(); it++) { - XrInteractionProfileSuggestedBinding profile_suggested_bindings = { - XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING}; - profile_suggested_bindings.interactionProfile = it->first; - profile_suggested_bindings.suggestedBindings = it->second.data(); - profile_suggested_bindings.countSuggestedBindings = it->second.size(); - - RETURN_IF_XR_FAILED(xrSuggestInteractionProfileBindings( - instance, &profile_suggested_bindings)); - } - - std::vector<XrActionSet> action_sets(new_helper->controllers_.size()); - for (size_t i = 0; i < new_helper->controllers_.size(); i++) { - action_sets[i] = new_helper->controllers_[i].GetActionSet(); - } - - XrSessionActionSetsAttachInfo attach_info = { - XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO}; - attach_info.countActionSets = action_sets.size(); - attach_info.actionSets = action_sets.data(); - RETURN_IF_XR_FAILED(xrAttachSessionActionSets(session, &attach_info)); - - *helper = std::move(new_helper); - return xr_result; -} - -OpenXrGamepadHelper::OpenXrGamepadHelper(XrSession session, XrSpace local_space) - : session_(session), local_space_(local_space) {} - -OpenXrGamepadHelper::~OpenXrGamepadHelper() = default; - -mojom::XRGamepadDataPtr OpenXrGamepadHelper::GetGamepadData( - XrTime predicted_display_time) { - XrActionsSyncInfo sync_info = {XR_TYPE_ACTIONS_SYNC_INFO}; - sync_info.countActiveActionSets = active_action_sets_.size(); - sync_info.activeActionSets = active_action_sets_.data(); - if (XR_FAILED(xrSyncActions(session_, &sync_info))) - return nullptr; - - mojom::XRGamepadDataPtr gamepad_data_ptr = mojom::XRGamepadData::New(); - for (const OpenXrController& controller : controllers_) { - mojom::XRGamepadPtr gamepad_ptr = mojom::XRGamepad::New(); - gamepad_ptr->controller_id = controller.GetID(); - gamepad_ptr->timestamp = base::TimeTicks::Now(); - gamepad_ptr->hand = controller.GetHandness(); - - std::vector<mojom::XRGamepadButtonPtr> buttons = - controller.GetWebVrButtons(); - if (buttons.empty()) - continue; - gamepad_ptr->buttons = std::move(buttons); - - std::vector<double> axes = controller.GetWebVrAxes(); - if (axes.empty()) - continue; - gamepad_ptr->axes = std::move(axes); - - gamepad_ptr->pose = - controller.GetPose(predicted_display_time, local_space_); - if (!gamepad_ptr->pose) - continue; - gamepad_ptr->can_provide_position = gamepad_ptr->pose->position.has_value(); - gamepad_ptr->can_provide_orientation = - gamepad_ptr->pose->orientation.has_value(); - - gamepad_data_ptr->gamepads.push_back(std::move(gamepad_ptr)); - } - - return gamepad_data_ptr; -} - -} // namespace device diff --git a/chromium/device/vr/openxr/openxr_gamepad_helper.h b/chromium/device/vr/openxr/openxr_gamepad_helper.h deleted file mode 100644 index f5e6763d57b..00000000000 --- a/chromium/device/vr/openxr/openxr_gamepad_helper.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2019 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_OPENXR_OPENXR_GAMEPAD_HELPER_H_ -#define DEVICE_VR_OPENXR_OPENXR_GAMEPAD_HELPER_H_ - -#include "device/vr/openxr/openxr_controller.h" -#include "device/vr/public/mojom/isolated_xr_service.mojom.h" - -namespace device { - -class OpenXrGamepadHelper { - public: - static XrResult CreateOpenXrGamepadHelper( - XrInstance instance, - XrSession session, - XrSpace local_space, - std::unique_ptr<OpenXrGamepadHelper>* helper); - - OpenXrGamepadHelper(XrSession session, XrSpace local_space); - - ~OpenXrGamepadHelper(); - - mojom::XRGamepadDataPtr GetGamepadData(XrTime predicted_display_time); - - private: - XrSession session_; - XrSpace local_space_; - - std::array<OpenXrController, - static_cast<size_t>(OpenXrControllerType::kCount)> - controllers_; - std::array<XrActiveActionSet, - static_cast<size_t>(OpenXrControllerType::kCount)> - active_action_sets_; - - DISALLOW_COPY_AND_ASSIGN(OpenXrGamepadHelper); -}; - -} // namespace device - -#endif // DEVICE_VR_OPENXR_OPENXR_GAMEPAD_HELPER_H_ diff --git a/chromium/device/vr/openxr/openxr_input_helper.cc b/chromium/device/vr/openxr/openxr_input_helper.cc new file mode 100644 index 00000000000..6a8cfa26ab6 --- /dev/null +++ b/chromium/device/vr/openxr/openxr_input_helper.cc @@ -0,0 +1,235 @@ +// Copyright 2019 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/openxr/openxr_input_helper.h" + +#include "device/vr/openxr/openxr_util.h" +#include "device/vr/util/xr_standard_gamepad_builder.h" + +namespace device { + +namespace { +base::Optional<GamepadBuilder::ButtonData> GetAxisButtonData( + OpenXrAxisType openxr_button_type, + base::Optional<GamepadButton> button_data, + std::vector<double> axis) { + GamepadBuilder::ButtonData data; + if (!button_data || axis.size() != 2) { + return base::nullopt; + } + + switch (openxr_button_type) { + case OpenXrAxisType::kThumbstick: + data.type = GamepadBuilder::ButtonData::Type::kThumbstick; + break; + case OpenXrAxisType::kTrackpad: + data.type = GamepadBuilder::ButtonData::Type::kTouchpad; + break; + } + data.touched = button_data->touched; + data.pressed = button_data->pressed; + data.value = button_data->value; + // Invert the y axis because -1 is up in the Gamepad API, but down in + // OpenXR. + data.x_axis = axis.at(0); + data.y_axis = -axis.at(1); + return data; +} +} // namespace + +XrResult OpenXRInputHelper::CreateOpenXRInputHelper( + XrInstance instance, + XrSession session, + XrSpace local_space, + std::unique_ptr<OpenXRInputHelper>* helper) { + XrResult xr_result; + + std::unique_ptr<OpenXRInputHelper> new_helper = + std::make_unique<OpenXRInputHelper>(session, local_space); + + // This map is used to store bindings for different kinds of interaction + // profiles. This allows the runtime to choose a different input sources based + // on availability. + std::map<XrPath, std::vector<XrActionSuggestedBinding>> bindings; + + for (size_t i = 0; i < new_helper->controller_states_.size(); i++) { + RETURN_IF_XR_FAILED(new_helper->controller_states_[i].controller.Initialize( + static_cast<OpenXrHandednessType>(i), instance, session, &bindings)); + new_helper->controller_states_[i].primary_button_pressed = false; + } + + for (auto it = bindings.begin(); it != bindings.end(); it++) { + XrInteractionProfileSuggestedBinding profile_suggested_bindings = { + XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING}; + profile_suggested_bindings.interactionProfile = it->first; + profile_suggested_bindings.suggestedBindings = it->second.data(); + profile_suggested_bindings.countSuggestedBindings = it->second.size(); + + RETURN_IF_XR_FAILED(xrSuggestInteractionProfileBindings( + instance, &profile_suggested_bindings)); + } + + std::vector<XrActionSet> action_sets(new_helper->controller_states_.size()); + for (size_t i = 0; i < new_helper->controller_states_.size(); i++) { + action_sets[i] = + new_helper->controller_states_[i].controller.GetActionSet(); + } + + XrSessionActionSetsAttachInfo attach_info = { + XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO}; + attach_info.countActionSets = action_sets.size(); + attach_info.actionSets = action_sets.data(); + RETURN_IF_XR_FAILED(xrAttachSessionActionSets(session, &attach_info)); + + *helper = std::move(new_helper); + return xr_result; +} + +OpenXRInputHelper::OpenXRInputHelper(XrSession session, XrSpace local_space) + : session_(session), local_space_(local_space) {} + +OpenXRInputHelper::~OpenXRInputHelper() = default; + +mojom::XRGamepadDataPtr OpenXRInputHelper::GetGamepadData( + XrTime predicted_display_time) { + if (XR_FAILED(SyncActions(predicted_display_time))) + return nullptr; + + mojom::XRGamepadDataPtr gamepad_data_ptr = mojom::XRGamepadData::New(); + for (const OpenXrControllerState& controller_state : controller_states_) { + const OpenXrController& controller = controller_state.controller; + mojom::XRGamepadPtr gamepad_ptr = mojom::XRGamepad::New(); + gamepad_ptr->controller_id = controller.GetId(); + gamepad_ptr->timestamp = base::TimeTicks::Now(); + gamepad_ptr->hand = controller.GetHandness(); + + std::vector<mojom::XRGamepadButtonPtr> buttons = + controller.GetWebVrButtons(); + if (buttons.empty()) + continue; + gamepad_ptr->buttons = std::move(buttons); + + std::vector<double> axes = controller.GetWebVrAxes(); + if (axes.empty()) + continue; + gamepad_ptr->axes = std::move(axes); + + gamepad_ptr->pose = + controller.GetPose(predicted_display_time, local_space_); + if (!gamepad_ptr->pose) + continue; + gamepad_ptr->can_provide_position = gamepad_ptr->pose->position.has_value(); + gamepad_ptr->can_provide_orientation = + gamepad_ptr->pose->orientation.has_value(); + + gamepad_data_ptr->gamepads.push_back(std::move(gamepad_ptr)); + } + + return gamepad_data_ptr; +} + +std::vector<mojom::XRInputSourceStatePtr> OpenXRInputHelper::GetInputState( + XrTime predicted_display_time) { + std::vector<mojom::XRInputSourceStatePtr> input_states; + if (XR_FAILED(SyncActions(predicted_display_time))) { + for (OpenXrControllerState& state : controller_states_) { + state.primary_button_pressed = false; + } + return input_states; + } + + for (uint32_t i = 0; i < controller_states_.size(); i++) { + device::OpenXrController* controller = &controller_states_[i].controller; + + base::Optional<GamepadButton> trigger_button = + controller->GetButton(OpenXrButtonType::kTrigger); + + // Having a trigger button is the minimum for an webxr input. + // No trigger button indicates input is not connected. + if (!trigger_button) { + continue; + } + + device::mojom::XRInputSourceStatePtr state = + device::mojom::XRInputSourceState::New(); + + // ID 0 will cause a DCHECK in the hash table used on the blink side. + // To ensure that we don't have any collisions with other ids, increment + // all of the ids by one. + state->source_id = i + 1; + + state->description = controller->GetDescription(predicted_display_time); + + state->grip = controller->GetMojoFromGripTransform(predicted_display_time, + local_space_); + state->emulated_position = false; + state->primary_input_pressed = trigger_button.value().pressed; + state->primary_input_clicked = + controller_states_[i].primary_button_pressed && + !state->primary_input_pressed; + controller_states_[i].primary_button_pressed = state->primary_input_pressed; + state->gamepad = GetWebXRGamepad(*controller); + input_states.push_back(std::move(state)); + } + + return input_states; +} + +base::Optional<Gamepad> OpenXRInputHelper::GetWebXRGamepad( + const OpenXrController& controller) const { + XRStandardGamepadBuilder builder(controller.GetHandness()); + + base::Optional<GamepadButton> trigger_button = + controller.GetButton(OpenXrButtonType::kTrigger); + if (!trigger_button) + return base::nullopt; + + builder.SetPrimaryButton(trigger_button.value()); + + base::Optional<GamepadButton> squeeze_button = + controller.GetButton(OpenXrButtonType::kSqueeze); + if (squeeze_button) + builder.SetSecondaryButton(squeeze_button.value()); + + base::Optional<GamepadButton> trackpad_button = + controller.GetButton(OpenXrButtonType::kTrackpad); + std::vector<double> trackpad_axis = + controller.GetAxis(OpenXrAxisType::kTrackpad); + base::Optional<GamepadBuilder::ButtonData> trackpad_button_data = + GetAxisButtonData(OpenXrAxisType::kTrackpad, trackpad_button, + trackpad_axis); + + if (trackpad_button_data) + builder.SetTouchpadData(trackpad_button_data.value()); + + base::Optional<GamepadButton> thumbstick_button = + controller.GetButton(OpenXrButtonType::kThumbstick); + std::vector<double> thumbstick_axis = + controller.GetAxis(OpenXrAxisType::kThumbstick); + base::Optional<GamepadBuilder::ButtonData> thumbstick_button_data = + GetAxisButtonData(OpenXrAxisType::kThumbstick, thumbstick_button, + thumbstick_axis); + + if (thumbstick_button_data) + builder.SetThumbstickData(thumbstick_button_data.value()); + + return builder.GetGamepad(); +} + +XrResult OpenXRInputHelper::SyncActions(XrTime predicted_display_time) { + std::vector<XrActiveActionSet> active_action_sets(controller_states_.size()); + + for (size_t i = 0; i < controller_states_.size(); i++) { + active_action_sets[i].actionSet = + controller_states_[i].controller.GetActionSet(); + active_action_sets[i].subactionPath = XR_NULL_PATH; + } + + XrActionsSyncInfo sync_info = {XR_TYPE_ACTIONS_SYNC_INFO}; + sync_info.countActiveActionSets = active_action_sets.size(); + sync_info.activeActionSets = active_action_sets.data(); + return xrSyncActions(session_, &sync_info); +} + +} // namespace device diff --git a/chromium/device/vr/openxr/openxr_input_helper.h b/chromium/device/vr/openxr/openxr_input_helper.h new file mode 100644 index 00000000000..4e3909e271e --- /dev/null +++ b/chromium/device/vr/openxr/openxr_input_helper.h @@ -0,0 +1,58 @@ +// Copyright 2019 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_OPENXR_OPENXR_INPUT_HELPER_H_ +#define DEVICE_VR_OPENXR_OPENXR_INPUT_HELPER_H_ + +#include <array> +#include <memory> +#include <vector> + +#include "base/optional.h" + +#include "device/vr/openxr/openxr_controller.h" +#include "device/vr/public/mojom/isolated_xr_service.mojom.h" + +namespace device { + +class OpenXRInputHelper { + public: + static XrResult CreateOpenXRInputHelper( + XrInstance instance, + XrSession session, + XrSpace local_space, + std::unique_ptr<OpenXRInputHelper>* helper); + + OpenXRInputHelper(XrSession session, XrSpace local_space); + + ~OpenXRInputHelper(); + + mojom::XRGamepadDataPtr GetGamepadData(XrTime predicted_display_time); + + std::vector<mojom::XRInputSourceStatePtr> GetInputState( + XrTime predicted_display_time); + + private: + base::Optional<Gamepad> GetWebXRGamepad( + const OpenXrController& controller) const; + + XrResult SyncActions(XrTime predicted_display_time); + + XrSession session_; + XrSpace local_space_; + + struct OpenXrControllerState { + OpenXrController controller; + bool primary_button_pressed; + }; + std::array<OpenXrControllerState, + static_cast<size_t>(OpenXrHandednessType::kCount)> + controller_states_; + + DISALLOW_COPY_AND_ASSIGN(OpenXRInputHelper); +}; + +} // namespace device + +#endif // DEVICE_VR_OPENXR_OPENXR_INPUT_HELPER_H_ diff --git a/chromium/device/vr/openxr/openxr_render_loop.cc b/chromium/device/vr/openxr/openxr_render_loop.cc index 442e5cb825c..4e01032274c 100644 --- a/chromium/device/vr/openxr/openxr_render_loop.cc +++ b/chromium/device/vr/openxr/openxr_render_loop.cc @@ -4,11 +4,12 @@ #include "device/vr/openxr/openxr_render_loop.h" -#include <directxmath.h> - #include "device/vr/openxr/openxr_api_wrapper.h" -#include "device/vr/openxr/openxr_gamepad_helper.h" +#include "device/vr/openxr/openxr_input_helper.h" #include "device/vr/util/transform_utils.h" +#include "ui/gfx/geometry/angle_conversions.h" +#include "ui/gfx/transform.h" +#include "ui/gfx/transform_util.h" namespace device { @@ -40,10 +41,13 @@ mojom::XRFrameDataPtr OpenXrRenderLoop::GetNextFrameData() { frame_data->time_delta = base::TimeDelta::FromNanoseconds(openxr_->GetPredictedDisplayTime()); + frame_data->pose = mojom::VRPose::New(); + frame_data->pose->input_state = + input_helper_->GetInputState(openxr_->GetPredictedDisplayTime()); + base::Optional<gfx::Quaternion> orientation; base::Optional<gfx::Point3F> position; if (XR_SUCCEEDED(openxr_->GetHeadPose(&orientation, &position))) { - frame_data->pose = mojom::VRPose::New(); if (orientation.has_value()) frame_data->pose->orientation = orientation; @@ -78,12 +82,12 @@ mojom::XRFrameDataPtr OpenXrRenderLoop::GetNextFrameData() { } mojom::XRGamepadDataPtr OpenXrRenderLoop::GetNextGamepadData() { - return gamepad_helper_->GetGamepadData(openxr_->GetPredictedDisplayTime()); + return input_helper_->GetGamepadData(openxr_->GetPredictedDisplayTime()); } bool OpenXrRenderLoop::StartRuntime() { DCHECK(!openxr_); - DCHECK(!gamepad_helper_); + DCHECK(!input_helper_); DCHECK(!current_display_info_); // The new wrapper object is stored in a temporary variable instead of @@ -100,7 +104,7 @@ bool OpenXrRenderLoop::StartRuntime() { !texture_helper_.SetAdapterLUID(luid) || !texture_helper_.EnsureInitialized() || XR_FAILED( - openxr->InitSession(texture_helper_.GetDevice(), &gamepad_helper_))) { + openxr->InitSession(texture_helper_.GetDevice(), &input_helper_))) { texture_helper_.Reset(); return false; } @@ -111,16 +115,16 @@ bool OpenXrRenderLoop::StartRuntime() { texture_helper_.SetDefaultSize(GetViewSize()); DCHECK(openxr_); - DCHECK(gamepad_helper_); + DCHECK(input_helper_); return true; } void OpenXrRenderLoop::StopRuntime() { - // Has to reset gamepad_helper_ before reset openxr_. If we destroy openxr_ - // first, gamepad_helper_destructor will try to call the actual openxr runtime + // Has to reset input_helper_ before reset openxr_. If we destroy openxr_ + // first, input_helper_destructor will try to call the actual openxr runtime // rather than the mock in tests. - gamepad_helper_.reset(); + input_helper_.reset(); openxr_ = nullptr; current_display_info_ = nullptr; texture_helper_.Reset(); @@ -202,27 +206,19 @@ bool OpenXrRenderLoop::UpdateEyeParameters() { changed = true; } - const XrView left = openxr_->GetView(0); - const XrView right = openxr_->GetView(1); - - gfx::Point3F center = - gfx::Point3F((left.pose.position.x + right.pose.position.x) / 2, - (left.pose.position.y + right.pose.position.y) / 2, - (left.pose.position.z + right.pose.position.z) / 2); - + XrView left; + XrView right; + openxr_->GetHeadFromEyes(&left, &right); gfx::Size view_size = GetViewSize(); - changed |= - UpdateEye(left, center, view_size, ¤t_display_info_->left_eye); + changed |= UpdateEye(left, view_size, ¤t_display_info_->left_eye); - changed |= - UpdateEye(right, center, view_size, ¤t_display_info_->right_eye); + changed |= UpdateEye(right, view_size, ¤t_display_info_->right_eye); return changed; } -bool OpenXrRenderLoop::UpdateEye(const XrView& view, - const gfx::Point3F& center, +bool OpenXrRenderLoop::UpdateEye(const XrView& view_head, const gfx::Size& view_size, mojom::VREyeParametersPtr* eye) const { bool changed = false; @@ -231,8 +227,8 @@ bool OpenXrRenderLoop::UpdateEye(const XrView& view, // that instead of just building a transformation matrix from the translation // component. gfx::Transform head_from_eye = vr_utils::MakeTranslationTransform( - view.pose.position.x - center.x(), view.pose.position.y - center.y(), - view.pose.position.z - center.z()); + view_head.pose.position.x, view_head.pose.position.y, + view_head.pose.position.z); if ((*eye)->head_from_eye != head_from_eye) { (*eye)->head_from_eye = head_from_eye; @@ -249,11 +245,11 @@ bool OpenXrRenderLoop::UpdateEye(const XrView& view, changed = true; } - mojom::VRFieldOfViewPtr fov = mojom::VRFieldOfView::New( - DirectX::XMConvertToDegrees(view.fov.angleUp), - -DirectX::XMConvertToDegrees(view.fov.angleDown), - -DirectX::XMConvertToDegrees(view.fov.angleLeft), - DirectX::XMConvertToDegrees(view.fov.angleRight)); + mojom::VRFieldOfViewPtr fov = + mojom::VRFieldOfView::New(gfx::RadToDeg(view_head.fov.angleUp), + gfx::RadToDeg(-view_head.fov.angleDown), + gfx::RadToDeg(-view_head.fov.angleLeft), + gfx::RadToDeg(view_head.fov.angleRight)); if (!(*eye)->field_of_view || !fov->Equals(*(*eye)->field_of_view)) { (*eye)->field_of_view = std::move(fov); changed = true; @@ -265,8 +261,8 @@ bool OpenXrRenderLoop::UpdateEye(const XrView& view, bool OpenXrRenderLoop::UpdateStageParameters() { bool changed = false; XrExtent2Df stage_bounds; - gfx::Transform transform; - if (openxr_->GetStageParameters(&stage_bounds, &transform)) { + gfx::Transform local_from_stage; + if (openxr_->GetStageParameters(&stage_bounds, &local_from_stage)) { if (!current_display_info_->stage_parameters) { current_display_info_->stage_parameters = mojom::VRStageParameters::New(); changed = true; @@ -281,8 +277,9 @@ bool OpenXrRenderLoop::UpdateStageParameters() { } if (current_display_info_->stage_parameters->standing_transform != - transform) { - current_display_info_->stage_parameters->standing_transform = transform; + local_from_stage) { + current_display_info_->stage_parameters->standing_transform = + local_from_stage; changed = true; } } else if (current_display_info_->stage_parameters) { diff --git a/chromium/device/vr/openxr/openxr_render_loop.h b/chromium/device/vr/openxr/openxr_render_loop.h index 6c1cb17e1b6..4a79f69bba6 100644 --- a/chromium/device/vr/openxr/openxr_render_loop.h +++ b/chromium/device/vr/openxr/openxr_render_loop.h @@ -16,7 +16,7 @@ struct XrView; namespace device { class OpenXrApiWrapper; -class OpenXrGamepadHelper; +class OpenXRInputHelper; class OpenXrRenderLoop : public XRCompositorCommon { public: @@ -39,14 +39,13 @@ class OpenXrRenderLoop : public XRCompositorCommon { bool UpdateDisplayInfo(); bool UpdateEyeParameters(); - bool UpdateEye(const XrView& view, - const gfx::Point3F& center, + bool UpdateEye(const XrView& view_head, const gfx::Size& view_size, mojom::VREyeParametersPtr* eye) const; bool UpdateStageParameters(); std::unique_ptr<OpenXrApiWrapper> openxr_; - std::unique_ptr<OpenXrGamepadHelper> gamepad_helper_; + std::unique_ptr<OpenXRInputHelper> input_helper_; base::RepeatingCallback<void(mojom::VRDisplayInfoPtr)> on_display_info_changed_; diff --git a/chromium/device/vr/orientation/orientation_device.cc b/chromium/device/vr/orientation/orientation_device.cc index fe7b0a9346a..d6e1a98601f 100644 --- a/chromium/device/vr/orientation/orientation_device.cc +++ b/chromium/device/vr/orientation/orientation_device.cc @@ -10,6 +10,7 @@ #include "base/time/time.h" #include "device/vr/orientation/orientation_device.h" #include "device/vr/orientation/orientation_session.h" +#include "mojo/public/cpp/bindings/pending_remote.h" #include "services/device/public/cpp/generic_sensor/sensor_reading.h" #include "services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.h" #include "services/device/public/mojom/sensor_provider.mojom.h" @@ -52,16 +53,13 @@ display::Display::Rotation GetRotation() { } // namespace -VROrientationDevice::VROrientationDevice( - mojom::SensorProviderPtr* sensor_provider, - base::OnceClosure ready_callback) +VROrientationDevice::VROrientationDevice(mojom::SensorProvider* sensor_provider, + base::OnceClosure ready_callback) : VRDeviceBase(mojom::XRDeviceId::ORIENTATION_DEVICE_ID), - ready_callback_(std::move(ready_callback)), - binding_(this) { - (*sensor_provider) - ->GetSensor(kOrientationSensorType, - base::BindOnce(&VROrientationDevice::SensorReady, - base::Unretained(this))); + ready_callback_(std::move(ready_callback)) { + sensor_provider->GetSensor(kOrientationSensorType, + base::BindOnce(&VROrientationDevice::SensorReady, + base::Unretained(this))); SetVRDisplayInfo(CreateVRDisplayInfo(GetId())); } @@ -87,7 +85,7 @@ void VROrientationDevice::SensorReady( sensor_.Bind(std::move(params->sensor)); - binding_.Bind(std::move(params->client_request)); + receiver_.Bind(std::move(params->client_receiver)); shared_buffer_reader_ = device::SensorReadingSharedBufferReader::Create( std::move(params->memory), params->buffer_offset); @@ -99,7 +97,7 @@ void VROrientationDevice::SensorReady( } default_config.set_frequency(kDefaultPumpFrequencyHz); - sensor_.set_connection_error_handler(base::BindOnce( + sensor_.set_disconnect_handler(base::BindOnce( &VROrientationDevice::HandleSensorError, base::Unretained(this))); sensor_->ConfigureReadingChangeNotifications(false /* disabled */); sensor_->AddConfiguration( @@ -128,7 +126,7 @@ void VROrientationDevice::RaiseError() { void VROrientationDevice::HandleSensorError() { sensor_.reset(); shared_buffer_reader_.reset(); - binding_.Close(); + receiver_.reset(); } void VROrientationDevice::RequestSession( @@ -139,18 +137,22 @@ void VROrientationDevice::RequestSession( // TODO(http://crbug.com/695937): Perform a check to see if sensors are // available when RequestSession is called for non-immersive sessions. - mojom::XRFrameDataProviderPtr data_provider; - mojom::XRSessionControllerPtr controller; + mojo::PendingRemote<mojom::XRFrameDataProvider> data_provider; + mojo::PendingRemote<mojom::XRSessionController> controller; magic_window_sessions_.push_back(std::make_unique<VROrientationSession>( - this, mojo::MakeRequest(&data_provider), mojo::MakeRequest(&controller))); + this, data_provider.InitWithNewPipeAndPassReceiver(), + controller.InitWithNewPipeAndPassReceiver())); auto session = mojom::XRSession::New(); - session->data_provider = data_provider.PassInterface(); + session->data_provider = std::move(data_provider); if (display_info_) { session->display_info = display_info_.Clone(); } std::move(callback).Run(std::move(session), std::move(controller)); + + // The sensor may have been suspended, so resume it now. + sensor_->Resume(); } void VROrientationDevice::EndMagicWindowSession(VROrientationSession* session) { @@ -158,10 +160,18 @@ void VROrientationDevice::EndMagicWindowSession(VROrientationSession* session) { [session](const std::unique_ptr<VROrientationSession>& item) { return item.get() == session; }); + + // If there are no more magic window sessions, suspend the sensor until we get + // a new one. + if (magic_window_sessions_.empty()) { + sensor_->Suspend(); + } } void VROrientationDevice::GetInlineFrameData( mojom::XRFrameDataProvider::GetFrameDataCallback callback) { + // Orientation sessions should never be exclusive or presenting. + DCHECK(!HasExclusiveSession()); if (!inline_poses_enabled_) { std::move(callback).Run(nullptr); return; diff --git a/chromium/device/vr/orientation/orientation_device.h b/chromium/device/vr/orientation/orientation_device.h index eff93f7a041..420ead8ca8f 100644 --- a/chromium/device/vr/orientation/orientation_device.h +++ b/chromium/device/vr/orientation/orientation_device.h @@ -13,7 +13,8 @@ #include "build/build_config.h" #include "device/vr/public/mojom/vr_service.mojom.h" #include "device/vr/vr_device_base.h" -#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" #include "services/device/public/mojom/sensor_provider.mojom.h" #include "ui/gfx/geometry/quaternion.h" @@ -39,7 +40,7 @@ static constexpr mojom::SensorType kOrientationSensorType = class DEVICE_VR_EXPORT VROrientationDevice : public VRDeviceBase, public mojom::SensorClient { public: - VROrientationDevice(mojom::SensorProviderPtr* sensor_provider, + VROrientationDevice(mojom::SensorProvider* sensor_provider, base::OnceClosure ready_callback); ~VROrientationDevice() override; @@ -76,9 +77,9 @@ class DEVICE_VR_EXPORT VROrientationDevice : public VRDeviceBase, base::Optional<gfx::Quaternion> base_pose_; gfx::Quaternion latest_pose_; - mojom::SensorPtr sensor_; + mojo::Remote<mojom::Sensor> sensor_; std::unique_ptr<SensorReadingSharedBufferReader> shared_buffer_reader_; - mojo::Binding<mojom::SensorClient> binding_; + mojo::Receiver<mojom::SensorClient> receiver_{this}; std::vector<std::unique_ptr<VROrientationSession>> magic_window_sessions_; }; diff --git a/chromium/device/vr/orientation/orientation_device_provider.cc b/chromium/device/vr/orientation/orientation_device_provider.cc index 505ae2c496a..4fd6fffd15f 100644 --- a/chromium/device/vr/orientation/orientation_device_provider.cc +++ b/chromium/device/vr/orientation/orientation_device_provider.cc @@ -17,8 +17,8 @@ namespace device { VROrientationDeviceProvider::VROrientationDeviceProvider( service_manager::Connector* connector) { - connector->BindInterface(device::mojom::kServiceName, - mojo::MakeRequest(&sensor_provider_)); + connector->Connect(device::mojom::kServiceName, + sensor_provider_.BindNewPipeAndPassReceiver()); } VROrientationDeviceProvider::~VROrientationDeviceProvider() = default; @@ -26,7 +26,8 @@ VROrientationDeviceProvider::~VROrientationDeviceProvider() = default; void VROrientationDeviceProvider::Initialize( base::RepeatingCallback<void(mojom::XRDeviceId, mojom::VRDisplayInfoPtr, - mojom::XRRuntimePtr)> add_device_callback, + mojo::PendingRemote<mojom::XRRuntime>)> + add_device_callback, base::RepeatingCallback<void(mojom::XRDeviceId)> remove_device_callback, base::OnceClosure initialization_complete) { if (!base::FeatureList::IsEnabled(device::kWebXrOrientationSensorDevice)) { @@ -39,13 +40,13 @@ void VROrientationDeviceProvider::Initialize( if (device_ && device_->IsAvailable()) { add_device_callback.Run(device_->GetId(), device_->GetVRDisplayInfo(), - device_->BindXRRuntimePtr()); + device_->BindXRRuntime()); return; } if (!device_) { device_ = std::make_unique<VROrientationDevice>( - &sensor_provider_, + sensor_provider_.get(), base::BindOnce(&VROrientationDeviceProvider::DeviceInitialized, base::Unretained(this))); add_device_callback_ = add_device_callback; @@ -66,7 +67,7 @@ void VROrientationDeviceProvider::DeviceInitialized() { // If the device successfully connected to the orientation APIs, provide it. if (device_->IsAvailable()) { add_device_callback_.Run(device_->GetId(), device_->GetVRDisplayInfo(), - device_->BindXRRuntimePtr()); + device_->BindXRRuntime()); } initialized_ = true; diff --git a/chromium/device/vr/orientation/orientation_device_provider.h b/chromium/device/vr/orientation/orientation_device_provider.h index a260c5ba38c..72e8a0e8656 100644 --- a/chromium/device/vr/orientation/orientation_device_provider.h +++ b/chromium/device/vr/orientation/orientation_device_provider.h @@ -12,6 +12,8 @@ #include "device/vr/orientation/orientation_device.h" #include "device/vr/vr_device_provider.h" #include "device/vr/vr_export.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/remote.h" #include "services/device/public/mojom/constants.mojom.h" #include "services/device/public/mojom/sensor_provider.mojom.h" #include "services/service_manager/public/cpp/connector.h" @@ -26,7 +28,8 @@ class DEVICE_VR_EXPORT VROrientationDeviceProvider : public VRDeviceProvider { void Initialize( base::RepeatingCallback<void(mojom::XRDeviceId, mojom::VRDisplayInfoPtr, - mojom::XRRuntimePtr)> add_device_callback, + mojo::PendingRemote<mojom::XRRuntime>)> + add_device_callback, base::RepeatingCallback<void(mojom::XRDeviceId)> remove_device_callback, base::OnceClosure initialization_complete) override; @@ -37,12 +40,13 @@ class DEVICE_VR_EXPORT VROrientationDeviceProvider : public VRDeviceProvider { bool initialized_ = false; - device::mojom::SensorProviderPtr sensor_provider_; + mojo::Remote<device::mojom::SensorProvider> sensor_provider_; std::unique_ptr<VROrientationDevice> device_; - base::RepeatingCallback< - void(mojom::XRDeviceId, mojom::VRDisplayInfoPtr, mojom::XRRuntimePtr)> + base::RepeatingCallback<void(mojom::XRDeviceId, + mojom::VRDisplayInfoPtr, + mojo::PendingRemote<mojom::XRRuntime>)> add_device_callback_; base::OnceClosure initialized_callback_; diff --git a/chromium/device/vr/orientation/orientation_device_provider_unittest.cc b/chromium/device/vr/orientation/orientation_device_provider_unittest.cc index 1e2a294940e..3874089d4e4 100644 --- a/chromium/device/vr/orientation/orientation_device_provider_unittest.cc +++ b/chromium/device/vr/orientation/orientation_device_provider_unittest.cc @@ -18,6 +18,8 @@ #include "device/vr/orientation/orientation_device_provider.h" #include "device/vr/test/fake_orientation_provider.h" #include "device/vr/test/fake_sensor_provider.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/remote.h" #include "services/device/public/cpp/generic_sensor/sensor_reading.h" #include "services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.h" #include "services/device/public/cpp/generic_sensor/sensor_traits.h" @@ -38,7 +40,7 @@ class VROrientationDeviceProviderTest : public testing::Test { fake_sensor_provider_ = std::make_unique<FakeSensorProvider>(); fake_sensor_ = std::make_unique<FakeOrientationSensor>( - mojo::MakeRequest(&sensor_ptr_)); + sensor_.InitWithNewPipeAndPassReceiver()); shared_buffer_handle_ = mojo::SharedBufferHandle::Create( sizeof(SensorReadingSharedBuffer) * (static_cast<uint64_t>(mojom::SensorType::kMaxValue) + 1)); @@ -70,11 +72,11 @@ class VROrientationDeviceProviderTest : public testing::Test { mojom::SensorInitParamsPtr FakeInitParams() { auto init_params = mojom::SensorInitParams::New(); - init_params->sensor = std::move(sensor_ptr_); + init_params->sensor = std::move(sensor_); init_params->default_configuration = PlatformSensorConfiguration( SensorTraits<kOrientationSensorType>::kDefaultFrequency); - init_params->client_request = mojo::MakeRequest(&sensor_client_ptr_); + init_params->client_receiver = sensor_client_.BindNewPipeAndPassReceiver(); init_params->memory = shared_buffer_handle_->Clone( mojo::SharedBufferHandle::AccessMode::READ_ONLY); @@ -87,11 +89,11 @@ class VROrientationDeviceProviderTest : public testing::Test { base::RepeatingCallback<void(device::mojom::XRDeviceId, mojom::VRDisplayInfoPtr, - mojom::XRRuntimePtr device)> + mojo::PendingRemote<mojom::XRRuntime> device)> DeviceAndIdCallbackFailIfCalled() { - return base::BindRepeating([](device::mojom::XRDeviceId id, - mojom::VRDisplayInfoPtr, - mojom::XRRuntimePtr device) { FAIL(); }); + return base::BindRepeating( + [](device::mojom::XRDeviceId id, mojom::VRDisplayInfoPtr, + mojo::PendingRemote<mojom::XRRuntime> device) { FAIL(); }); } base::RepeatingCallback<void(device::mojom::XRDeviceId)> @@ -101,11 +103,12 @@ class VROrientationDeviceProviderTest : public testing::Test { base::RepeatingCallback<void(device::mojom::XRDeviceId, mojom::VRDisplayInfoPtr, - mojom::XRRuntimePtr device)> + mojo::PendingRemote<mojom::XRRuntime> device)> DeviceAndIdCallbackMustBeCalled(base::RunLoop* loop) { return base::BindRepeating( [](base::OnceClosure quit_closure, device::mojom::XRDeviceId id, - mojom::VRDisplayInfoPtr info, mojom::XRRuntimePtr device) { + mojom::VRDisplayInfoPtr info, + mojo::PendingRemote<mojom::XRRuntime> device) { ASSERT_TRUE(device); ASSERT_TRUE(info); std::move(quit_closure).Run(); @@ -138,13 +141,13 @@ class VROrientationDeviceProviderTest : public testing::Test { std::unique_ptr<VROrientationDeviceProvider> provider_; std::unique_ptr<FakeSensorProvider> fake_sensor_provider_; - mojom::SensorProviderPtr sensor_provider_ptr_; + mojo::Remote<mojom::SensorProvider> sensor_provider_; // Fake Sensor Init params objects std::unique_ptr<FakeOrientationSensor> fake_sensor_; - mojom::SensorPtrInfo sensor_ptr_; + mojo::PendingRemote<mojom::Sensor> sensor_; mojo::ScopedSharedBufferHandle shared_buffer_handle_; - mojom::SensorClientPtr sensor_client_ptr_; + mojo::Remote<mojom::SensorClient> sensor_client_; std::unique_ptr<service_manager::Connector> connector_; diff --git a/chromium/device/vr/orientation/orientation_device_unittest.cc b/chromium/device/vr/orientation/orientation_device_unittest.cc index be550164ee6..9f8ce7aa889 100644 --- a/chromium/device/vr/orientation/orientation_device_unittest.cc +++ b/chromium/device/vr/orientation/orientation_device_unittest.cc @@ -16,6 +16,8 @@ #include "device/vr/orientation/orientation_session.h" #include "device/vr/test/fake_orientation_provider.h" #include "device/vr/test/fake_sensor_provider.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/remote.h" #include "services/device/public/cpp/generic_sensor/sensor_reading.h" #include "services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.h" #include "services/device/public/cpp/generic_sensor/sensor_traits.h" @@ -79,10 +81,10 @@ class VROrientationDeviceTest : public testing::Test { ~VROrientationDeviceTest() override = default; void SetUp() override { fake_sensor_provider_ = std::make_unique<FakeSensorProvider>( - mojo::MakeRequest(&sensor_provider_ptr_)); + sensor_provider_.BindNewPipeAndPassReceiver()); fake_sensor_ = std::make_unique<FakeOrientationSensor>( - mojo::MakeRequest(&sensor_ptr_)); + sensor_.InitWithNewPipeAndPassReceiver()); shared_buffer_handle_ = mojo::SharedBufferHandle::Create( sizeof(SensorReadingSharedBuffer) * @@ -108,13 +110,8 @@ class VROrientationDeviceTest : public testing::Test { void InitializeDevice(mojom::SensorInitParamsPtr params) { base::RunLoop loop; - device_ = std::make_unique<VROrientationDevice>( - &sensor_provider_ptr_, base::BindOnce( - [](base::OnceClosure quit_closure) { - // The callback was called. - std::move(quit_closure).Run(); - }, - loop.QuitClosure())); + device_ = std::make_unique<VROrientationDevice>(sensor_provider_.get(), + loop.QuitClosure()); // Complete the creation of device_ by letting the GetSensor function go // through. @@ -166,11 +163,11 @@ class VROrientationDeviceTest : public testing::Test { } std::unique_ptr<VROrientationSession> MakeDisplay() { - mojom::XRFrameDataProviderPtr data_provider; - mojom::XRSessionControllerPtr controller; + mojo::PendingRemote<mojom::XRFrameDataProvider> data_provider; + mojo::PendingRemote<mojom::XRSessionController> controller; return std::make_unique<VROrientationSession>( - device_.get(), mojo::MakeRequest(&data_provider), - mojo::MakeRequest(&controller)); + device_.get(), data_provider.InitWithNewPipeAndPassReceiver(), + controller.InitWithNewPipeAndPassReceiver()); } void TryGetFrameData(VROrientationSession* display, bool expect_null) { @@ -190,11 +187,11 @@ class VROrientationDeviceTest : public testing::Test { mojom::SensorInitParamsPtr FakeInitParams() { auto init_params = mojom::SensorInitParams::New(); - init_params->sensor = std::move(sensor_ptr_); + init_params->sensor = std::move(sensor_); init_params->default_configuration = PlatformSensorConfiguration( SensorTraits<kOrientationSensorType>::kDefaultFrequency); - init_params->client_request = mojo::MakeRequest(&sensor_client_ptr_); + init_params->client_receiver = sensor_client_.BindNewPipeAndPassReceiver(); init_params->memory = shared_buffer_handle_->Clone( mojo::SharedBufferHandle::AccessMode::READ_ONLY); @@ -229,14 +226,14 @@ class VROrientationDeviceTest : public testing::Test { std::unique_ptr<VROrientationDevice> device_; std::unique_ptr<FakeSensorProvider> fake_sensor_provider_; - mojom::SensorProviderPtr sensor_provider_ptr_; + mojo::Remote<mojom::SensorProvider> sensor_provider_; // Fake Sensor Init params objects std::unique_ptr<FakeOrientationSensor> fake_sensor_; - mojom::SensorPtrInfo sensor_ptr_; + mojo::PendingRemote<mojom::Sensor> sensor_; mojo::ScopedSharedBufferHandle shared_buffer_handle_; mojo::ScopedSharedBufferMapping shared_buffer_mapping_; - mojom::SensorClientPtr sensor_client_ptr_; + mojo::Remote<mojom::SensorClient> sensor_client_; std::unique_ptr<FakeScreen> fake_screen_; std::unique_ptr<ScopedScreenOverride> scoped_screen_override_; @@ -248,8 +245,8 @@ TEST_F(VROrientationDeviceTest, InitializationTest) { // Check that without running anything, the device will return not available, // without crashing. - device_ = std::make_unique<VROrientationDevice>(&sensor_provider_ptr_, - base::BindOnce([]() {})); + device_ = std::make_unique<VROrientationDevice>(sensor_provider_.get(), + base::DoNothing()); task_environment_.RunUntilIdle(); EXPECT_FALSE(device_->IsAvailable()); diff --git a/chromium/device/vr/orientation/orientation_session.cc b/chromium/device/vr/orientation/orientation_session.cc index 3a937ed332e..35207e4eaa5 100644 --- a/chromium/device/vr/orientation/orientation_session.cc +++ b/chromium/device/vr/orientation/orientation_session.cc @@ -13,15 +13,17 @@ namespace device { VROrientationSession::VROrientationSession( VROrientationDevice* device, - mojom::XRFrameDataProviderRequest magic_window_request, - mojom::XRSessionControllerRequest session_request) - : magic_window_binding_(this, std::move(magic_window_request)), - session_controller_binding_(this, std::move(session_request)), + mojo::PendingReceiver<mojom::XRFrameDataProvider> magic_window_receiver, + mojo::PendingReceiver<mojom::XRSessionController> session_receiver) + : magic_window_receiver_(this, std::move(magic_window_receiver)), + session_controller_receiver_(this, std::move(session_receiver)), device_(device) { - // 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( - &VROrientationSession::OnMojoConnectionError, base::Unretained(this))); + magic_window_receiver_.set_disconnect_handler( + base::BindOnce(&VROrientationSession::OnMojoConnectionError, + weak_ptr_factory_.GetWeakPtr())); + session_controller_receiver_.set_disconnect_handler( + base::BindOnce(&VROrientationSession::OnMojoConnectionError, + weak_ptr_factory_.GetWeakPtr())); } VROrientationSession::~VROrientationSession() = default; @@ -42,15 +44,15 @@ void VROrientationSession::GetFrameData( } void VROrientationSession::GetEnvironmentIntegrationProvider( - mojom::XREnvironmentIntegrationProviderAssociatedRequest - environment_request) { + mojo::PendingAssociatedReceiver<mojom::XREnvironmentIntegrationProvider> + environment_provider) { // Environment integration is not supported. This call should not // be made on this device. mojo::ReportBadMessage("Environment integration is not supported."); } void VROrientationSession::SetInputSourceButtonListener( - device::mojom::XRInputSourceButtonListenerAssociatedPtrInfo) { + mojo::PendingAssociatedRemote<device::mojom::XRInputSourceButtonListener>) { // Input eventing is not supported. This call should not // be made on this device. mojo::ReportBadMessage("Input eventing is not supported."); @@ -62,8 +64,8 @@ void VROrientationSession::SetFrameDataRestricted(bool frame_data_restricted) { } void VROrientationSession::OnMojoConnectionError() { - magic_window_binding_.Close(); - session_controller_binding_.Close(); + magic_window_receiver_.reset(); + session_controller_receiver_.reset(); device_->EndMagicWindowSession(this); // This call will destroy us. } diff --git a/chromium/device/vr/orientation/orientation_session.h b/chromium/device/vr/orientation/orientation_session.h index 50c28288591..ab8a7af6a44 100644 --- a/chromium/device/vr/orientation/orientation_session.h +++ b/chromium/device/vr/orientation/orientation_session.h @@ -13,8 +13,10 @@ #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/associated_binding.h" -#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/pending_associated_receiver.h" +#include "mojo/public/cpp/bindings/pending_associated_remote.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/receiver.h" #include "ui/display/display.h" namespace device { @@ -30,15 +32,16 @@ class DEVICE_VR_EXPORT VROrientationSession public mojom::XRSessionController { public: VROrientationSession(VROrientationDevice* device, - mojom::XRFrameDataProviderRequest, - mojom::XRSessionControllerRequest); + mojo::PendingReceiver<mojom::XRFrameDataProvider>, + mojo::PendingReceiver<mojom::XRSessionController>); ~VROrientationSession() override; void GetEnvironmentIntegrationProvider( - mojom::XREnvironmentIntegrationProviderAssociatedRequest + mojo::PendingAssociatedReceiver<mojom::XREnvironmentIntegrationProvider> environment_provider) override; void SetInputSourceButtonListener( - device::mojom::XRInputSourceButtonListenerAssociatedPtrInfo) override; + mojo::PendingAssociatedRemote<device::mojom::XRInputSourceButtonListener>) + override; // Accessible to tests. protected: @@ -51,10 +54,13 @@ class DEVICE_VR_EXPORT VROrientationSession void OnMojoConnectionError(); - mojo::Binding<mojom::XRFrameDataProvider> magic_window_binding_; - mojo::Binding<mojom::XRSessionController> session_controller_binding_; + mojo::Receiver<mojom::XRFrameDataProvider> magic_window_receiver_; + mojo::Receiver<mojom::XRSessionController> session_controller_receiver_; device::VROrientationDevice* device_; bool restrict_frame_data_ = true; + + // This must be the last member + base::WeakPtrFactory<VROrientationSession> weak_ptr_factory_{this}; }; } // namespace device diff --git a/chromium/device/vr/public/mojom/browser_test_interfaces.mojom b/chromium/device/vr/public/mojom/browser_test_interfaces.mojom index bbf864023c1..a9e321fc0fd 100644 --- a/chromium/device/vr/public/mojom/browser_test_interfaces.mojom +++ b/chromium/device/vr/public/mojom/browser_test_interfaces.mojom @@ -128,7 +128,7 @@ interface XRTestHook { // It is always hosted in the XRDevice process, but only has effects while // running in browser tests with mock implementations of runtimes. interface XRServiceTestHook { - [Sync] SetTestHook(XRTestHook? hook) => (); + [Sync] SetTestHook(pending_remote<XRTestHook>? hook) => (); // Called by tests to trigger a termination of the Device Service Process // To test that the product can properly handle the service either crashing diff --git a/chromium/device/vr/public/mojom/isolated_xr_service.mojom b/chromium/device/vr/public/mojom/isolated_xr_service.mojom index e16e645b86d..5bb301d542c 100644 --- a/chromium/device/vr/public/mojom/isolated_xr_service.mojom +++ b/chromium/device/vr/public/mojom/isolated_xr_service.mojom @@ -34,6 +34,10 @@ interface XRRuntimeEventListener { // A device has indicated that it is idle. OnDeviceIdle(device.mojom.VRDisplayEventReason reason); + // A device has indicated that the visibility of the content it's displaying + // has changed. + OnVisibilityStateChanged(device.mojom.XRVisibilityState visibility_state); + // Called when the device exits presentation. OnExitPresent(); }; @@ -42,6 +46,10 @@ struct XRRuntimeSessionOptions { bool immersive; bool environment_integration; + // The enabled features combine the active required and optional features as + // resolved by BrowserXRRuntime and VRServiceImpl. + array<XRSessionFeature> enabled_features; + // The following options are used for permission requests and showing module // install UI. // TODO(crbug.com/854655): Remove these fields, and do permission checks, show @@ -65,12 +73,13 @@ interface XRRuntime { // the browser process. RequestSession(XRRuntimeSessionOptions options) => ( XRSession? session, - XRSessionController? controller); + pending_remote<XRSessionController>? controller); // The browser may register for changes to a device. Initial VRDisplayInfo // will immediately be returned to the listener to prevent races. - ListenToDeviceChanges(associated XRRuntimeEventListener listener) => - (VRDisplayInfo? display_info); + ListenToDeviceChanges( + pending_associated_remote<XRRuntimeEventListener> listener) => + (VRDisplayInfo? display_info); // Ensure that the runtime has installed most prerequisites, and is ready to // start. May result in updated display info being sent to registered @@ -144,7 +153,8 @@ interface IsolatedXRGamepadProviderFactory { // Get the IsolatedXRGamepadProvider for a specific XR runtime API (Oculus, or // OpenVR, which are currently the only two that are hosted outside of the // browser process). - GetIsolatedXRGamepadProvider(IsolatedXRGamepadProvider& provider); + GetIsolatedXRGamepadProvider( + pending_receiver<IsolatedXRGamepadProvider> provider); }; // Represents an overlay that the browser may show on top of or instead of WebXR @@ -178,7 +188,7 @@ interface ImmersiveOverlay { // obtain an ImmersiveOverlay to show overlay UI. The obtained overlay will // disconnect when presentation ends. interface XRCompositorHost { - CreateImmersiveOverlay(ImmersiveOverlay& overlay); + CreateImmersiveOverlay(pending_receiver<ImmersiveOverlay> overlay); }; // Notify the browser process about a set of runtimes. The browser process @@ -187,9 +197,9 @@ interface XRCompositorHost { interface IsolatedXRRuntimeProviderClient { // Called when runtimes are initially enumerated, or when devices are later // attached and become available. - OnDeviceAdded(XRRuntime runtime, - IsolatedXRGamepadProviderFactory gamepad_factory, - XRCompositorHost compositor_host, + OnDeviceAdded(pending_remote<XRRuntime> runtime, + pending_remote<IsolatedXRGamepadProviderFactory> gamepad_factory, + pending_remote<XRCompositorHost> compositor_host, device.mojom.XRDeviceId device_id); // Called when runtimes become unavailable - for example if the hardware is @@ -210,7 +220,7 @@ interface IsolatedXRRuntimeProvider { // Register a client, and triggers OnDeviceAdded for all available runtimes, // followed by OnDevicesEnumerated. // Should only be called once. - RequestDevices(IsolatedXRRuntimeProviderClient client); + RequestDevices(pending_remote<IsolatedXRRuntimeProviderClient> client); }; // The main interface for the XR Device Service. diff --git a/chromium/device/vr/public/mojom/vr_service.mojom b/chromium/device/vr/public/mojom/vr_service.mojom index fb00428808c..5e53c01301f 100644 --- a/chromium/device/vr/public/mojom/vr_service.mojom +++ b/chromium/device/vr/public/mojom/vr_service.mojom @@ -52,6 +52,9 @@ enum XRSessionFeature { REF_SPACE_LOCAL_FLOOR = 3, REF_SPACE_BOUNDED_FLOOR = 4, REF_SPACE_UNBOUNDED = 5, + + // Experimental or not-yet-standardized feature names. + DOM_OVERLAY_FOR_HANDHELD_AR = 6, }; struct XRSessionOptions { @@ -80,15 +83,15 @@ struct XRSessionOptions { // apropriate based on the session creation options. (for example, an immersive // session ought to have a XRPresentationConnection to submit the frames to the // immersive environment). -// The XRSessionClient request must be fulfilled for the session to get +// The XRSessionClient receiver must be fulfilled for the session to get // information about the device it is connected to, such as focus and blur // events, changes to view or stage parameters, or exit present calls initiated // by the device. struct XRSession { - XRFrameDataProvider data_provider; - // TODO(http://crbug.com/842025) Move where the client_request gets set to the + pending_remote<XRFrameDataProvider> data_provider; + // TODO(http://crbug.com/842025) Move where the client_receiver gets set to the // device process then mark this as non-optional. - XRSessionClient&? client_request; + pending_receiver<XRSessionClient>? client_receiver; // TODO(http://crbug.com/842025) Move the information that is sent in display // info to more sensible places so that this doesn't need to be sent here. VRDisplayInfo display_info; @@ -107,15 +110,14 @@ struct XRSession { // way connection between the renderer and a device to synchronize and submit // frames to a sink outside of Chrome. struct XRPresentationConnection { - XRPresentationProvider provider; - XRPresentationClient& client_request; + pending_remote<XRPresentationProvider> provider; + pending_receiver<XRPresentationClient> client_receiver; XRPresentationTransportOptions transport_options; }; struct XRInputSourceDescription { XRTargetRayMode target_ray_mode; XRHandedness handedness; - bool emulated_position; // Transform from the grip matrix to the pointer's origin and orientation. gfx.mojom.Transform? pointer_offset; @@ -137,6 +139,10 @@ struct XRInputSourceState { // Transform to the controllers grip (users palm) from start space origin. gfx.mojom.Transform? grip; + // True when position is estimated by something like a neck or arm model. + // False when position is based on sensors tracking a 6DoF pose. + bool emulated_position; + // Describes the current state of the primary input. bool primary_input_pressed; @@ -174,6 +180,10 @@ struct VRPose { gfx.mojom.Quaternion? orientation; gfx.mojom.Point3F? position; + // True when position is estimated by something like a neck or arm model. + // False when position is based on sensors tracking a 6DoF pose. + bool emulated_position; + // Velocity/Acceleration is in global coordinates, as rad/s. gfx.mojom.Vector3dF? angular_velocity; gfx.mojom.Vector3dF? linear_velocity; @@ -279,6 +289,31 @@ struct XRPresentationTransportOptions { bool wait_for_gpu_fence; }; +// Native origins that are reference spaces are identified by their category. +// Currently, the device needs to know about 3 reference space categories. +enum XRReferenceSpaceCategory { + LOCAL, + LOCAL_FLOOR, + VIEWER, + BOUNDED_FLOOR, + UNBOUNDED +}; + +// Native origin represents a reference space that is known to the device and +// whose position can be tracked over time. There are multiple different types +// of native origins (planes, anchors, reference spaces, input sources), each of +// them roughly corresponds to something that either is an XRSpace (for example +// XRReferenceSpaceType, XRBoundedReferenceSpace) or returns an XRSpace (for +// example XRAnchor, XRPlane, XRInputSource). Native origin can be identified, +// depending on its type, by the id of the entity (this is the case for planes, +// anchors and input sources), or by reference space category. +union XRNativeOriginInformation { + uint32 input_source_id; + uint32 plane_id; + uint32 anchor_id; + XRReferenceSpaceCategory reference_space_category; +}; + enum XRPlaneOrientation { UNKNOWN = 0, HORIZONTAL = 1, @@ -294,7 +329,7 @@ struct XRPlanePointData { // detected in the real world by the underlying system. struct XRPlaneData { // Unique (per session) identifier of the plane. - int32 id; + uint32 id; // Plane orientation relative to gravity. XRPlaneOrientation orientation; @@ -314,7 +349,7 @@ struct XRPlaneData { // present in updated_planes_data. struct XRPlaneDetectionData { // Array with ids of all tracked planes. - array<int32> all_planes_ids; + array<uint32> all_planes_ids; // Array with plane data for all updated planes. Plane is considered updated // if its position or polygon has changed. Updated planes are always a subset @@ -329,7 +364,7 @@ struct XRPlaneDetectionData { // evolves. struct XRAnchorData { // Unique (per session) identifier of the anchor. - int32 id; + uint32 id; // Pose of the anchor. VRPose pose; @@ -341,7 +376,7 @@ struct XRAnchorData { // will not be present in updated_anchors_data. struct XRAnchorsData { // Array with ids of all tracked anchors. - array<int32> all_anchors_ids; + array<uint32> all_anchors_ids; // Array with anchor data for all updated anchors. Updated anchors are always // a subset of all anchors (i.e. for each anchor found in @@ -349,6 +384,18 @@ struct XRAnchorsData { array<XRAnchorData> updated_anchors_data; }; +// Information about results for a single subscription for the current frame. +struct XRHitTestSubscriptionResultData { + uint32 subscription_id; + array<XRHitResult> hit_test_results; +}; + +// Struct containing data about results of all hit test subscriptions for the +// current frame. +struct XRHitTestSubscriptionResultsData { + array<XRHitTestSubscriptionResultData> results; +}; + // The data needed for each animation frame of an XRSession. struct XRFrameData { // General XRSession value @@ -398,6 +445,10 @@ struct XRFrameData { // Tracked anchors information. XRAnchorsData? anchors_data; + + // Hit test subscription results. Only present if the session supports + // environment integration. + XRHitTestSubscriptionResultsData? hit_test_subscription_results; }; enum VRDisplayEventReason { @@ -445,6 +496,15 @@ interface VRService { GetImmersiveVRDisplayInfo() => (VRDisplayInfo? info); ExitPresent(); + + // Used for the renderer process to indicate that it is throttling frame + // requests from the page, and thus it (not the page) is responsible for any + // drop in frame rate or delay in posting frames. This is mainly meant to be + // informational for the browser process, so that it can decide if or what UI + // to show if it seems like the frame rate is too low or has stalled. The + // renderer can (and may) still decide to submit frames and that should not be + // treated as illegal or clear the throttled state. + SetFramesThrottled(bool throttled); }; // The interface for the renderer to listen to top level XR events, events that @@ -454,9 +514,22 @@ interface VRServiceClient { OnDeviceChanged(); }; +enum CreateAnchorResult { + SUCCESS = 0, + FAILURE = 1, +}; + +enum SubscribeToHitTestResult { + SUCCESS = 0, + FAILURE_GENERIC = 1, +}; + // Provides functionality for integrating environment information into an // XRSession. For example, some AR sessions would implement hit test to allow // developers to get the information about the world that its sensors supply. +// On XRSession end, the message pipe will be destroyed - we need to clean up +// all outstanding JavaScript promises at that time. The XRsession might end for +// example due to a call to XRSession::end() method (exposed to JavaScript). interface XREnvironmentIntegrationProvider { // Performs a raycast into the scene and returns a list of XRHitResults sorted // from closest to furthest hit from the ray. Each hit result contains a @@ -465,6 +538,45 @@ interface XREnvironmentIntegrationProvider { // An empty result list means there were no hits. If a nullopt is returned, // there was an error. RequestHitTest(XRRay ray) => (array<XRHitResult>? results); + + // Establishes a hit test subscription on the device. The subscription results + // will be computed taking into account latest state of the native origin + // identified by passed in |native_origin_information|. I.e., in each frame, + // the ray used to subscribe to hit test will be transformed to device + // coordinate system using the device's current knowledge of the state of the + // native origin. The returned |subscription_id| uniquely identifies the + // subscription within an immersive session. Lifetime of the subscription is + // tied to the lifetime of the immersive session. + SubscribeToHitTest( + XRNativeOriginInformation native_origin_information, XRRay ray) => + (SubscribeToHitTestResult result, uint32 subscription_id); + + // Notifies the device that subscription with the passed in |subscription_id| + // is no longer needed and should be removed. The |subscription_id| must be a + // valid id of a subscription returned by one of the SubscribeToHitTest calls, + // otherwise the call will be ignored. + UnsubscribeFromHitTest(uint32 subscription_id); + + // Issues a request to create an anchor attached to a session. + // |result| will contain status code of the request. |anchor_id| will be valid + // only if the |result| is SUCCESS. + CreateAnchor(VRPose anchor_pose) => (CreateAnchorResult result, + uint32 anchor_id); + // TODO(https://crbug.com/657632): Switch anchor_id to nullable integer once + // that's available. This will allow us to remove CreateAnchorResult if we're + // not interested in obtaining detailed error information from the device. + + // Issues a request to create an anchor attached to a plane. + // |result| will contain status code of the request. |anchor_id| will be valid + // only if the |result| is SUCCESS. + CreatePlaneAnchor(VRPose anchor_pose, uint32 plane_id) => + (CreateAnchorResult result, uint32 anchor_id); + // TODO(https://crbug.com/657632): Ditto - make anchor_id a nullable integer.. + + // Detaches an existing anchor. The |anchor_id| must be a valid id of an + // anchor created by one of the CreateAnchor calls, otherwise the call will be + // ignored. + DetachAnchor(uint32 anchor_id); }; // Provides a mechanism for a channel to plumb up any button click events @@ -490,9 +602,10 @@ interface XRFrameDataProvider { // for some reason, such as device disconnection. GetFrameData(XRFrameDataRequestOptions? options) => (XRFrameData? frame_data); GetEnvironmentIntegrationProvider( - associated XREnvironmentIntegrationProvider& environment_provider); + pending_associated_receiver<XREnvironmentIntegrationProvider> + environment_provider); SetInputSourceButtonListener( - associated XRInputSourceButtonListener? event_listener); + pending_associated_remote<XRInputSourceButtonListener>? event_listener); }; // Provides the necessary functionality for sending frames to a headset. @@ -561,12 +674,21 @@ interface XRPresentationClient { OnSubmitFrameGpuFence(gfx.mojom.GpuFenceHandle gpu_fence_handle); }; +enum XRVisibilityState { + // Indicates the WebXR content is visible to the user and receiving input. + VISIBLE = 1, + // Indicates the WebXR content is visible but not receiving input. + VISIBLE_BLURRED = 2, + // Indicates the WebXR content is not visible. + HIDDEN = 3 +}; + // Functions for pushing device information to the sessions. interface XRSessionClient { OnChanged(VRDisplayInfo display); OnExitPresent(); - OnBlur(); - OnFocus(); + // Indicates a change in the visibility of the WebXR content. + OnVisibilityStateChanged(XRVisibilityState visibility_state); }; // Backwards compatibility events for WebVR 1.1. These are expected to not be diff --git a/chromium/device/vr/vr_device_base.cc b/chromium/device/vr/vr_device_base.cc index 878dee57066..306b0aced64 100644 --- a/chromium/device/vr/vr_device_base.cc +++ b/chromium/device/vr/vr_device_base.cc @@ -11,8 +11,7 @@ namespace device { -VRDeviceBase::VRDeviceBase(mojom::XRDeviceId id) - : id_(id), runtime_binding_(this) {} +VRDeviceBase::VRDeviceBase(mojom::XRDeviceId id) : id_(id) {} VRDeviceBase::~VRDeviceBase() = default; @@ -43,7 +42,7 @@ bool VRDeviceBase::HasExclusiveSession() { } void VRDeviceBase::ListenToDeviceChanges( - mojom::XRRuntimeEventListenerAssociatedPtrInfo listener_info, + mojo::PendingAssociatedRemote<mojom::XRRuntimeEventListener> listener_info, mojom::XRRuntime::ListenToDeviceChangesCallback callback) { listener_.Bind(std::move(listener_info)); std::move(callback).Run(display_info_.Clone()); @@ -64,10 +63,14 @@ void VRDeviceBase::OnActivate(mojom::VRDisplayEventReason reason, listener_->OnDeviceActivated(reason, std::move(on_handled)); } -mojom::XRRuntimePtr VRDeviceBase::BindXRRuntimePtr() { - mojom::XRRuntimePtr runtime; - runtime_binding_.Bind(mojo::MakeRequest(&runtime)); - return runtime; +void VRDeviceBase::OnVisibilityStateChanged( + mojom::XRVisibilityState visibility_state) { + if (listener_) + listener_->OnVisibilityStateChanged(visibility_state); +} + +mojo::PendingRemote<mojom::XRRuntime> VRDeviceBase::BindXRRuntime() { + return runtime_receiver_.BindNewPipeAndPassRemote(); } void VRDeviceBase::OnListeningForActivate(bool listening) {} diff --git a/chromium/device/vr/vr_device_base.h b/chromium/device/vr/vr_device_base.h index 42efcad5d6c..75218d81554 100644 --- a/chromium/device/vr/vr_device_base.h +++ b/chromium/device/vr/vr_device_base.h @@ -13,7 +13,10 @@ #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 "mojo/public/cpp/bindings/associated_remote.h" +#include "mojo/public/cpp/bindings/pending_associated_remote.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver.h" #include "ui/display/display.h" namespace device { @@ -28,7 +31,7 @@ class DEVICE_VR_EXPORT VRDeviceBase : public mojom::XRRuntime { // VRDevice Implementation void ListenToDeviceChanges( - mojom::XRRuntimeEventListenerAssociatedPtrInfo listener, + mojo::PendingAssociatedRemote<mojom::XRRuntimeEventListener> listener, mojom::XRRuntime::ListenToDeviceChangesCallback callback) final; void SetListeningForActivate(bool is_listening) override; void EnsureInitialized(EnsureInitializedCallback callback) override; @@ -48,7 +51,7 @@ class DEVICE_VR_EXPORT VRDeviceBase : public mojom::XRRuntime { mojom::VRDisplayInfoPtr GetVRDisplayInfo(); // Used by providers to bind devices. - mojom::XRRuntimePtr BindXRRuntimePtr(); + mojo::PendingRemote<mojom::XRRuntime> BindXRRuntime(); // TODO(mthiesse): The browser should handle browser-side exiting of // presentation before device/ is even aware presentation is being exited. @@ -65,6 +68,7 @@ class DEVICE_VR_EXPORT VRDeviceBase : public mojom::XRRuntime { void SetVRDisplayInfo(mojom::VRDisplayInfoPtr display_info); void OnActivate(mojom::VRDisplayEventReason reason, base::Callback<void(bool)> on_handled); + void OnVisibilityStateChanged(mojom::XRVisibilityState visibility_state); mojom::VRDisplayInfoPtr display_info_; @@ -74,13 +78,13 @@ class DEVICE_VR_EXPORT VRDeviceBase : public mojom::XRRuntime { // TODO(https://crbug.com/842227): Rename methods to HandleOnXXX virtual void OnListeningForActivate(bool listening); - mojom::XRRuntimeEventListenerAssociatedPtr listener_; + mojo::AssociatedRemote<mojom::XRRuntimeEventListener> listener_; bool presenting_ = false; device::mojom::XRDeviceId id_; - mojo::Binding<mojom::XRRuntime> runtime_binding_; + mojo::Receiver<mojom::XRRuntime> runtime_receiver_{this}; 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 9c489a010e6..e3cc39788c5 100644 --- a/chromium/device/vr/vr_device_base_unittest.cc +++ b/chromium/device/vr/vr_device_base_unittest.cc @@ -13,7 +13,8 @@ #include "device/vr/public/mojom/vr_service.mojom.h" #include "device/vr/test/fake_vr_device.h" #include "device/vr/vr_device_base.h" -#include "mojo/public/cpp/bindings/associated_binding.h" +#include "mojo/public/cpp/bindings/associated_receiver.h" +#include "mojo/public/cpp/bindings/remote.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -52,8 +53,8 @@ class VRDeviceBaseForTesting : public VRDeviceBase { class StubVRDeviceEventListener : public mojom::XRRuntimeEventListener { public: - StubVRDeviceEventListener() : binding_(this) {} - ~StubVRDeviceEventListener() override {} + StubVRDeviceEventListener() = default; + ~StubVRDeviceEventListener() override = default; MOCK_METHOD1(DoOnChanged, void(mojom::VRDisplayInfo* vr_device_info)); void OnDisplayInfoChanged(mojom::VRDisplayInfoPtr vr_device_info) override { @@ -71,18 +72,16 @@ class StubVRDeviceEventListener : public mojom::XRRuntimeEventListener { } MOCK_METHOD0(OnExitPresent, void()); - MOCK_METHOD0(OnBlur, void()); - MOCK_METHOD0(OnFocus, void()); + MOCK_METHOD1(OnVisibilityStateChanged, void(mojom::XRVisibilityState)); MOCK_METHOD1(OnDeviceIdle, void(mojom::VRDisplayEventReason)); MOCK_METHOD0(OnInitialized, void()); - mojom::XRRuntimeEventListenerAssociatedPtrInfo BindPtrInfo() { - mojom::XRRuntimeEventListenerAssociatedPtrInfo ret; - binding_.Bind(mojo::MakeRequest(&ret)); - return ret; + mojo::PendingAssociatedRemote<mojom::XRRuntimeEventListener> + BindPendingRemote() { + return receiver_.BindNewEndpointAndPassRemote(); } - mojo::AssociatedBinding<mojom::XRRuntimeEventListener> binding_; + mojo::AssociatedReceiver<mojom::XRRuntimeEventListener> receiver_{this}; }; } // namespace @@ -117,10 +116,10 @@ class VRDeviceTest : public testing::Test { // will receive the "vrdevicechanged" event. TEST_F(VRDeviceTest, DeviceChangedDispatched) { auto device = MakeVRDevice(); - auto device_ptr = device->BindXRRuntimePtr(); + mojo::Remote<mojom::XRRuntime> device_remote(device->BindXRRuntime()); StubVRDeviceEventListener listener; - device_ptr->ListenToDeviceChanges( - listener.BindPtrInfo(), + device_remote->ListenToDeviceChanges( + listener.BindPendingRemote(), base::DoNothing()); // TODO: consider getting initial info base::RunLoop().RunUntilIdle(); EXPECT_CALL(listener, DoOnChanged(testing::_)).Times(1); @@ -132,10 +131,10 @@ TEST_F(VRDeviceTest, DisplayActivateRegsitered) { device::mojom::VRDisplayEventReason mounted = device::mojom::VRDisplayEventReason::MOUNTED; auto device = MakeVRDevice(); - auto device_ptr = device->BindXRRuntimePtr(); + mojo::Remote<mojom::XRRuntime> device_remote(device->BindXRRuntime()); StubVRDeviceEventListener listener; - device_ptr->ListenToDeviceChanges( - listener.BindPtrInfo(), + device_remote->ListenToDeviceChanges( + listener.BindPendingRemote(), base::DoNothing()); // TODO: consider getting initial data base::RunLoop().RunUntilIdle(); diff --git a/chromium/device/vr/vr_device_provider.h b/chromium/device/vr/vr_device_provider.h index 27616caa4f4..b40470766c7 100644 --- a/chromium/device/vr/vr_device_provider.h +++ b/chromium/device/vr/vr_device_provider.h @@ -9,6 +9,7 @@ #include "base/callback.h" #include "device/vr/public/mojom/isolated_xr_service.mojom.h" +#include "mojo/public/cpp/bindings/pending_remote.h" namespace device { @@ -21,7 +22,8 @@ class VRDeviceProvider { virtual void Initialize( base::RepeatingCallback<void(mojom::XRDeviceId id, mojom::VRDisplayInfoPtr, - mojom::XRRuntimePtr)> add_device_callback, + mojo::PendingRemote<mojom::XRRuntime>)> + add_device_callback, base::RepeatingCallback<void(mojom::XRDeviceId id)> remove_device_callback, base::OnceClosure initialization_complete) = 0; diff --git a/chromium/device/vr/windows/compositor_base.cc b/chromium/device/vr/windows/compositor_base.cc index 0c8d6d1a4d8..9ec0f3359a7 100644 --- a/chromium/device/vr/windows/compositor_base.cc +++ b/chromium/device/vr/windows/compositor_base.cc @@ -5,7 +5,7 @@ #include "device/vr/windows/compositor_base.h" #include "base/bind.h" -#include "base/trace_event/common/trace_event_common.h" +#include "base/trace_event/trace_event.h" #include "ui/gfx/geometry/angle_conversions.h" #include "ui/gfx/transform.h" @@ -44,11 +44,7 @@ XRCompositorCommon::XRCompositorCommon() : base::Thread("WindowsXRCompositor"), main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), webxr_js_time_(kSlidingAverageSize), - webxr_gpu_time_(kSlidingAverageSize), - presentation_binding_(this), - frame_data_binding_(this), - gamepad_provider_(this), - overlay_binding_(this) { + webxr_gpu_time_(kSlidingAverageSize) { DCHECK(main_thread_task_runner_); } @@ -136,26 +132,26 @@ void XRCompositorCommon::SubmitFrameWithTextureHandle( } void XRCompositorCommon::CleanUp() { - submit_client_ = nullptr; + submit_client_.reset(); webxr_has_pose_ = false; - presentation_binding_.Close(); - frame_data_binding_.Close(); - gamepad_provider_.Close(); - overlay_binding_.Close(); - input_event_listener_ = nullptr; + presentation_receiver_.reset(); + frame_data_receiver_.reset(); + gamepad_provider_receiver_.reset(); + overlay_receiver_.reset(); + input_event_listener_.reset(); StopRuntime(); } void XRCompositorCommon::RequestGamepadProvider( - mojom::IsolatedXRGamepadProviderRequest request) { - gamepad_provider_.Close(); - gamepad_provider_.Bind(std::move(request)); + mojo::PendingReceiver<mojom::IsolatedXRGamepadProvider> receiver) { + gamepad_provider_receiver_.reset(); + gamepad_provider_receiver_.Bind(std::move(receiver)); } void XRCompositorCommon::RequestOverlay( - mojom::ImmersiveOverlayRequest request) { - overlay_binding_.Close(); - overlay_binding_.Bind(std::move(request)); + mojo::PendingReceiver<mojom::ImmersiveOverlay> receiver) { + overlay_receiver_.reset(); + overlay_receiver_.Bind(std::move(receiver)); // WebXR is visible and overlay hidden by default until the overlay overrides // this. @@ -192,12 +188,14 @@ void XRCompositorCommon::UpdateLayerBounds(int16_t frame_id, void XRCompositorCommon::RequestSession( base::OnceCallback<void()> on_presentation_ended, + base::RepeatingCallback<void(mojom::XRVisibilityState)> + on_visibility_state_changed, mojom::XRRuntimeSessionOptionsPtr options, RequestSessionCallback callback) { DCHECK(options->immersive); webxr_has_pose_ = false; - presentation_binding_.Close(); - frame_data_binding_.Close(); + presentation_receiver_.reset(); + frame_data_receiver_.reset(); if (!StartRuntime()) { TRACE_EVENT_INSTANT0("xr", "Failed to start runtime", @@ -213,10 +211,15 @@ void XRCompositorCommon::RequestSession( // be called. on_presentation_ended_ = std::move(on_presentation_ended); - device::mojom::XRPresentationProviderPtr presentation_provider; - device::mojom::XRFrameDataProviderPtr frame_data_provider; - presentation_binding_.Bind(mojo::MakeRequest(&presentation_provider)); - frame_data_binding_.Bind(mojo::MakeRequest(&frame_data_provider)); + on_visibility_state_changed_ = std::move(on_visibility_state_changed); + + // Queue up a notification to the requester of the current visibility state, + // so that it can be initialized to the right value. + if (on_visibility_state_changed_) { + main_thread_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(on_visibility_state_changed_, visibility_state_)); + } device::mojom::XRPresentationTransportOptionsPtr transport_options = device::mojom::XRPresentationTransportOptions::New(); @@ -229,12 +232,14 @@ void XRCompositorCommon::RequestSession( OnSessionStart(); auto submit_frame_sink = device::mojom::XRPresentationConnection::New(); - submit_frame_sink->provider = presentation_provider.PassInterface(); - submit_frame_sink->client_request = mojo::MakeRequest(&submit_client_); + submit_frame_sink->provider = + presentation_receiver_.BindNewPipeAndPassRemote(); + submit_frame_sink->client_receiver = + submit_client_.BindNewPipeAndPassReceiver(); submit_frame_sink->transport_options = std::move(transport_options); auto session = device::mojom::XRSession::New(); - session->data_provider = frame_data_provider.PassInterface(); + session->data_provider = frame_data_receiver_.BindNewPipeAndPassRemote(); session->submit_frame_sink = std::move(submit_frame_sink); session->uses_input_eventing = UsesInputEventing(); @@ -249,9 +254,9 @@ void XRCompositorCommon::ExitPresent() { TRACE_EVENT_INSTANT0("xr", "ExitPresent", TRACE_EVENT_SCOPE_THREAD); is_presenting_ = false; webxr_has_pose_ = false; - presentation_binding_.Close(); - frame_data_binding_.Close(); - submit_client_ = nullptr; + presentation_receiver_.reset(); + frame_data_receiver_.reset(); + submit_client_.reset(); StopRuntime(); pending_frame_.reset(); @@ -265,7 +270,7 @@ void XRCompositorCommon::ExitPresent() { // Kill outstanding overlays: overlay_visible_ = false; - overlay_binding_.Close(); + overlay_receiver_.reset(); texture_helper_.SetSourceAndOverlayVisible(false, false); @@ -275,6 +280,18 @@ void XRCompositorCommon::ExitPresent() { } } +void XRCompositorCommon::SetVisibilityState( + mojom::XRVisibilityState visibility_state) { + if (visibility_state_ != visibility_state) { + visibility_state_ = visibility_state; + if (on_visibility_state_changed_) { + main_thread_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(on_visibility_state_changed_, visibility_state)); + } + } +} + void XRCompositorCommon::UpdateControllerState() { if (!gamepad_callback_) { // Nobody is listening to updates, so bail early. @@ -364,10 +381,11 @@ void XRCompositorCommon::GetFrameData( } void XRCompositorCommon::SetInputSourceButtonListener( - device::mojom::XRInputSourceButtonListenerAssociatedPtrInfo - input_listener_info) { + mojo::PendingAssociatedRemote<device::mojom::XRInputSourceButtonListener> + input_listener_remote) { DCHECK(UsesInputEventing()); - input_event_listener_.Bind(std::move(input_listener_info)); + input_event_listener_.reset(); + input_event_listener_.Bind(std::move(input_listener_remote)); } void XRCompositorCommon::GetControllerDataAndSendFrameData( @@ -377,17 +395,25 @@ void XRCompositorCommon::GetControllerDataAndSendFrameData( // Update gamepad controllers. UpdateControllerState(); + // This method represents a call from the renderer process. If our visibility + // state is hidden, we should avoid handing "sensitive" information, like the + // pose back up to the renderer. Note that this check is done here as other + // methods (RequestNextOverlayPose) represent a call from the browser process, + // which should receive the pose. + bool is_visible = + (visibility_state_ != device::mojom::XRVisibilityState::HIDDEN); + // We have posted a message to allow other calls to get through, and now state // may have changed. WebXR may not be presenting any more, or may be hidden. - std::move(callback).Run(is_presenting_ && + std::move(callback).Run(is_presenting_ && is_visible && (webxr_visible_ || on_webxr_submitted_) ? std::move(frame_data) : mojom::XRFrameData::New()); } void XRCompositorCommon::GetEnvironmentIntegrationProvider( - device::mojom::XREnvironmentIntegrationProviderAssociatedRequest - environment_provider) { + mojo::PendingAssociatedReceiver< + device::mojom::XREnvironmentIntegrationProvider> environment_provider) { // Environment integration is not supported. This call should not // be made on this device. mojo::ReportBadMessage("Environment integration is not supported."); diff --git a/chromium/device/vr/windows/compositor_base.h b/chromium/device/vr/windows/compositor_base.h index 6b924faf9a7..a8007193077 100644 --- a/chromium/device/vr/windows/compositor_base.h +++ b/chromium/device/vr/windows/compositor_base.h @@ -13,7 +13,12 @@ #include "device/vr/util/fps_meter.h" #include "device/vr/util/sliding_average.h" #include "device/vr/vr_device.h" -#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/associated_remote.h" +#include "mojo/public/cpp/bindings/pending_associated_receiver.h" +#include "mojo/public/cpp/bindings/pending_associated_remote.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/system/platform_handle.h" #include "ui/gfx/geometry/rect_f.h" @@ -55,6 +60,8 @@ class XRCompositorCommon : public base::Thread, // callback will be called (since we haven't yet stopped presenting to the // headset). void RequestSession(base::OnceCallback<void()> on_presentation_ended, + base::RepeatingCallback<void(mojom::XRVisibilityState)> + on_visibility_state_changed, mojom::XRRuntimeSessionOptionsPtr options, RequestSessionCallback callback); void ExitPresent(); @@ -62,21 +69,24 @@ class XRCompositorCommon : public base::Thread, void GetFrameData(mojom::XRFrameDataRequestOptionsPtr options, XRFrameDataProvider::GetFrameDataCallback callback) final; void SetInputSourceButtonListener( - device::mojom::XRInputSourceButtonListenerAssociatedPtrInfo - input_listener_info) override; + mojo::PendingAssociatedRemote<device::mojom::XRInputSourceButtonListener> + input_listener_remote) override; void GetControllerDataAndSendFrameData( XRFrameDataProvider::GetFrameDataCallback callback, mojom::XRFrameDataPtr frame_data); void GetEnvironmentIntegrationProvider( - device::mojom::XREnvironmentIntegrationProviderAssociatedRequest - environment_provider) final; + mojo::PendingAssociatedReceiver< + device::mojom::XREnvironmentIntegrationProvider> environment_provider) + final; - void RequestGamepadProvider(mojom::IsolatedXRGamepadProviderRequest request); - void RequestOverlay(mojom::ImmersiveOverlayRequest request); + void RequestGamepadProvider( + mojo::PendingReceiver<mojom::IsolatedXRGamepadProvider> receiver); + void RequestOverlay(mojo::PendingReceiver<mojom::ImmersiveOverlay> receiver); protected: virtual bool UsesInputEventing(); + void SetVisibilityState(mojom::XRVisibilityState visibility_state); #if defined(OS_WIN) D3D11TextureHelper texture_helper_; #endif @@ -84,7 +94,8 @@ class XRCompositorCommon : public base::Thread, // Allow derived classes to call methods on the main thread. scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_; - mojom::XRInputSourceButtonListenerAssociatedPtr input_event_listener_; + mojo::AssociatedRemote<mojom::XRInputSourceButtonListener> + input_event_listener_; private: // base::Thread overrides: @@ -166,16 +177,21 @@ class XRCompositorCommon : public base::Thread, gfx::RectF right_webxr_bounds_; gfx::Size source_size_; - mojom::XRPresentationClientPtr submit_client_; + mojo::Remote<mojom::XRPresentationClient> submit_client_; SubmitOverlayTextureCallback overlay_submit_callback_; RequestNotificationOnWebXrSubmittedCallback on_webxr_submitted_; bool webxr_has_pose_ = false; base::OnceCallback<void()> on_presentation_ended_; + base::RepeatingCallback<void(mojom::XRVisibilityState)> + on_visibility_state_changed_; mojom::IsolatedXRGamepadProvider::RequestUpdateCallback gamepad_callback_; - mojo::Binding<mojom::XRPresentationProvider> presentation_binding_; - mojo::Binding<mojom::XRFrameDataProvider> frame_data_binding_; - mojo::Binding<mojom::IsolatedXRGamepadProvider> gamepad_provider_; - mojo::Binding<mojom::ImmersiveOverlay> overlay_binding_; + mojo::Receiver<mojom::XRPresentationProvider> presentation_receiver_{this}; + mojo::Receiver<mojom::XRFrameDataProvider> frame_data_receiver_{this}; + mojo::Receiver<mojom::IsolatedXRGamepadProvider> gamepad_provider_receiver_{ + this}; + mojo::Receiver<mojom::ImmersiveOverlay> overlay_receiver_{this}; + mojom::XRVisibilityState visibility_state_ = + mojom::XRVisibilityState::VISIBLE; DISALLOW_COPY_AND_ASSIGN(XRCompositorCommon); }; diff --git a/chromium/device/vr/windows_mixed_reality/mixed_reality_device.cc b/chromium/device/vr/windows_mixed_reality/mixed_reality_device.cc index 0ef4c48be91..75a546dbdf8 100644 --- a/chromium/device/vr/windows_mixed_reality/mixed_reality_device.cc +++ b/chromium/device/vr/windows_mixed_reality/mixed_reality_device.cc @@ -15,6 +15,7 @@ #include "build/build_config.h" #include "device/vr/util/transform_utils.h" #include "device/vr/windows_mixed_reality/mixed_reality_renderloop.h" +#include "mojo/public/cpp/bindings/pending_remote.h" #include "ui/gfx/geometry/angle_conversions.h" namespace device { @@ -61,10 +62,7 @@ mojom::VRDisplayInfoPtr CreateFakeVRDisplayInfo(device::mojom::XRDeviceId id) { } // namespace MixedRealityDevice::MixedRealityDevice() - : VRDeviceBase(device::mojom::XRDeviceId::WINDOWS_MIXED_REALITY_ID), - gamepad_provider_factory_binding_(this), - compositor_host_binding_(this), - exclusive_controller_binding_(this) { + : VRDeviceBase(device::mojom::XRDeviceId::WINDOWS_MIXED_REALITY_ID) { SetVRDisplayInfo(CreateFakeVRDisplayInfo(GetId())); } @@ -72,17 +70,14 @@ MixedRealityDevice::~MixedRealityDevice() { Shutdown(); } -mojom::IsolatedXRGamepadProviderFactoryPtr +mojo::PendingRemote<mojom::IsolatedXRGamepadProviderFactory> MixedRealityDevice::BindGamepadFactory() { - mojom::IsolatedXRGamepadProviderFactoryPtr ret; - gamepad_provider_factory_binding_.Bind(mojo::MakeRequest(&ret)); - return ret; + return gamepad_provider_factory_receiver_.BindNewPipeAndPassRemote(); } -mojom::XRCompositorHostPtr MixedRealityDevice::BindCompositorHost() { - mojom::XRCompositorHostPtr ret; - compositor_host_binding_.Bind(mojo::MakeRequest(&ret)); - return ret; +mojo::PendingRemote<mojom::XRCompositorHost> +MixedRealityDevice::BindCompositorHost() { + return compositor_host_receiver_.BindNewPipeAndPassRemote(); } void MixedRealityDevice::RequestSession( @@ -104,22 +99,22 @@ void MixedRealityDevice::RequestSession( // memory exhaustion). If the thread fails to start, then we fail to create // a session. if (!render_loop_->IsRunning()) { - std::move(callback).Run(nullptr, nullptr); + std::move(callback).Run(nullptr, mojo::NullRemote()); return; } - if (provider_request_) { + if (provider_receiver_) { render_loop_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestGamepadProvider, base::Unretained(render_loop_.get()), - std::move(provider_request_))); + std::move(provider_receiver_))); } - if (overlay_request_) { + if (overlay_receiver_) { render_loop_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestOverlay, base::Unretained(render_loop_.get()), - std::move(overlay_request_))); + std::move(overlay_receiver_))); } } @@ -130,10 +125,15 @@ void MixedRealityDevice::RequestSession( auto on_presentation_ended = base::BindOnce( &MixedRealityDevice::OnPresentationEnded, weak_ptr_factory_.GetWeakPtr()); + auto on_visibility_state_changed = + base::BindRepeating(&MixedRealityDevice::OnVisibilityStateChanged, + weak_ptr_factory_.GetWeakPtr()); + render_loop_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestSession, base::Unretained(render_loop_.get()), std::move(on_presentation_ended), + std::move(on_visibility_state_changed), std::move(options), std::move(my_callback))); } @@ -158,51 +158,49 @@ void MixedRealityDevice::OnRequestSessionResult( mojom::XRSessionPtr session) { if (!result) { OnPresentationEnded(); - std::move(callback).Run(nullptr, nullptr); + std::move(callback).Run(nullptr, mojo::NullRemote()); return; } OnStartPresenting(); - mojom::XRSessionControllerPtr session_controller; - exclusive_controller_binding_.Bind(mojo::MakeRequest(&session_controller)); + session->display_info = display_info_.Clone(); + std::move(callback).Run( + std::move(session), + exclusive_controller_receiver_.BindNewPipeAndPassRemote()); // 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( + exclusive_controller_receiver_.set_disconnect_handler(base::BindOnce( &MixedRealityDevice::OnPresentingControllerMojoConnectionError, base::Unretained(this))); - - session->display_info = display_info_.Clone(); - - std::move(callback).Run(std::move(session), std::move(session_controller)); } void MixedRealityDevice::GetIsolatedXRGamepadProvider( - mojom::IsolatedXRGamepadProviderRequest provider_request) { + mojo::PendingReceiver<mojom::IsolatedXRGamepadProvider> provider_receiver) { if (!render_loop_) CreateRenderLoop(); if (render_loop_->IsRunning()) { render_loop_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestGamepadProvider, base::Unretained(render_loop_.get()), - std::move(provider_request))); + std::move(provider_receiver))); } else { - provider_request_ = std::move(provider_request); + provider_receiver_ = std::move(provider_receiver); } } void MixedRealityDevice::CreateImmersiveOverlay( - mojom::ImmersiveOverlayRequest overlay_request) { + mojo::PendingReceiver<mojom::ImmersiveOverlay> overlay_receiver) { if (!render_loop_) CreateRenderLoop(); if (render_loop_->IsRunning()) { render_loop_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&XRCompositorCommon::RequestOverlay, base::Unretained(render_loop_.get()), - std::move(overlay_request))); + std::move(overlay_receiver))); } else { - overlay_request_ = std::move(overlay_request); + overlay_receiver_ = std::move(overlay_receiver); } } @@ -223,7 +221,7 @@ void MixedRealityDevice::OnPresentingControllerMojoConnectionError() { // TODO(https://crbug.com/875187): Alternatively, we could recreate the // provider on the next session, or look into why the callback gets lost. OnExitPresent(); - exclusive_controller_binding_.Close(); + exclusive_controller_receiver_.reset(); } } // namespace device diff --git a/chromium/device/vr/windows_mixed_reality/mixed_reality_device.h b/chromium/device/vr/windows_mixed_reality/mixed_reality_device.h index 8b13a5406b2..234dc24d6de 100644 --- a/chromium/device/vr/windows_mixed_reality/mixed_reality_device.h +++ b/chromium/device/vr/windows_mixed_reality/mixed_reality_device.h @@ -13,7 +13,9 @@ #include "device/vr/public/mojom/vr_service.mojom.h" #include "device/vr/vr_device_base.h" #include "device/vr/windows/compositor_base.h" -#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver.h" namespace device { @@ -26,8 +28,9 @@ class DEVICE_VR_EXPORT MixedRealityDevice MixedRealityDevice(); ~MixedRealityDevice() override; - mojom::IsolatedXRGamepadProviderFactoryPtr BindGamepadFactory(); - mojom::XRCompositorHostPtr BindCompositorHost(); + mojo::PendingRemote<mojom::IsolatedXRGamepadProviderFactory> + BindGamepadFactory(); + mojo::PendingRemote<mojom::XRCompositorHost> BindCompositorHost(); private: // VRDeviceBase @@ -40,11 +43,12 @@ class DEVICE_VR_EXPORT MixedRealityDevice // mojom::IsolatedXRGamepadProviderFactory void GetIsolatedXRGamepadProvider( - mojom::IsolatedXRGamepadProviderRequest provider_request) override; + mojo::PendingReceiver<mojom::IsolatedXRGamepadProvider> provider_receiver) + override; // XRCompositorHost void CreateImmersiveOverlay( - mojom::ImmersiveOverlayRequest overlay_request) override; + mojo::PendingReceiver<mojom::ImmersiveOverlay> overlay_receiver) override; void CreateRenderLoop(); void Shutdown(); @@ -56,14 +60,15 @@ class DEVICE_VR_EXPORT MixedRealityDevice std::unique_ptr<XRCompositorCommon> render_loop_; - mojo::Binding<mojom::IsolatedXRGamepadProviderFactory> - gamepad_provider_factory_binding_; - mojom::IsolatedXRGamepadProviderRequest provider_request_; + mojo::Receiver<mojom::IsolatedXRGamepadProviderFactory> + gamepad_provider_factory_receiver_{this}; + mojo::PendingReceiver<mojom::IsolatedXRGamepadProvider> provider_receiver_; - mojo::Binding<mojom::XRCompositorHost> compositor_host_binding_; - mojom::ImmersiveOverlayRequest overlay_request_; + mojo::Receiver<mojom::XRCompositorHost> compositor_host_receiver_{this}; + mojo::PendingReceiver<mojom::ImmersiveOverlay> overlay_receiver_; - mojo::Binding<mojom::XRSessionController> exclusive_controller_binding_; + mojo::Receiver<mojom::XRSessionController> exclusive_controller_receiver_{ + this}; base::WeakPtrFactory<MixedRealityDevice> weak_ptr_factory_{this}; diff --git a/chromium/device/vr/windows_mixed_reality/mixed_reality_input_helper.cc b/chromium/device/vr/windows_mixed_reality/mixed_reality_input_helper.cc index 688c02cdb7c..5c3f02ef21a 100644 --- a/chromium/device/vr/windows_mixed_reality/mixed_reality_input_helper.cc +++ b/chromium/device/vr/windows_mixed_reality/mixed_reality_input_helper.cc @@ -43,6 +43,8 @@ using Handedness = using PressKind = ABI::Windows::UI::Input::Spatial::SpatialInteractionPressKind; using SourceKind = ABI::Windows::UI::Input::Spatial::SpatialInteractionSourceKind; +using PositionAccuracy = + ABI::Windows::UI::Input::Spatial::SpatialInteractionSourcePositionAccuracy; ParsedInputState::ParsedInputState() = default; ParsedInputState::~ParsedInputState() = default; @@ -503,6 +505,7 @@ ParsedInputState MixedRealityInputHelper::ParseWindowsSourceState( // Voice input is always untracked. gfx::Transform origin_from_grip; bool is_tracked = false; + bool emulated_position = false; if (is_controller) { input_state.button_data = ParseButtonState(state); std::unique_ptr<WMRInputLocation> location_in_origin = @@ -516,6 +519,15 @@ ParsedInputState MixedRealityInputHelper::ParseWindowsSourceState( is_tracked = true; } + PositionAccuracy position_accuracy; + if (location_in_origin->TryGetPositionAccuracy(&position_accuracy) && + (position_accuracy == + PositionAccuracy:: + SpatialInteractionSourcePositionAccuracy_Approximate)) { + // Controller lost precise tracking or has its position estimated. + emulated_position = true; + } + input_state.gamepad_pose = gamepad_pose; } @@ -552,8 +564,7 @@ ParsedInputState MixedRealityInputHelper::ParseWindowsSourceState( device::mojom::XRInputSourceDescriptionPtr description = device::mojom::XRInputSourceDescription::New(); - // If we've gotten this far we've gotten the real position. - description->emulated_position = false; + source_state->emulated_position = emulated_position; description->pointer_offset = grip_from_pointer; if (is_voice) { @@ -575,7 +586,8 @@ ParsedInputState MixedRealityInputHelper::ParseWindowsSourceState( // This makes it clear that the controller actually has a grip button and // touchpad and thumbstick input. Otherwise, it's ambiguous whether slots // like the touchpad buttons + axes are hooked up vs just placeholders. - description->profiles.push_back("grip-touchpad-thumbstick-controller"); + description->profiles.push_back( + "generic-trigger-squeeze-touchpad-thumbstick"); source_state->gamepad = GetWebXRGamepad(input_state); } else { diff --git a/chromium/device/vr/windows_mixed_reality/mixed_reality_renderloop.cc b/chromium/device/vr/windows_mixed_reality/mixed_reality_renderloop.cc index 747221d0a6b..5705d5160f6 100644 --- a/chromium/device/vr/windows_mixed_reality/mixed_reality_renderloop.cc +++ b/chromium/device/vr/windows_mixed_reality/mixed_reality_renderloop.cc @@ -14,7 +14,8 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" -#include "base/trace_event/common/trace_event_common.h" +#include "base/time/time.h" +#include "base/trace_event/trace_event.h" #include "base/win/com_init_util.h" #include "base/win/core_winrt_util.h" #include "base/win/scoped_co_mem.h" @@ -44,6 +45,8 @@ using SpatialMovementRange = using ABI::Windows::Foundation::DateTime; using ABI::Windows::Foundation::TimeSpan; using ABI::Windows::Foundation::Numerics::Matrix4x4; +using HolographicSpaceUserPresence = + ABI::Windows::Graphics::Holographic::HolographicSpaceUserPresence; using ABI::Windows::Graphics::Holographic::HolographicStereoTransform; using Microsoft::WRL::ComPtr; @@ -221,6 +224,15 @@ bool MixedRealityRenderLoop::StartRuntime() { if (!holographic_space_) return false; + // Since we explicitly null out both the holographic_space and the + // subscription during StopRuntime (which happens before destruction), + // base::Unretained is safe. + user_presence_changed_subscription_ = + holographic_space_->AddUserPresenceChangedCallback( + base::BindRepeating(&MixedRealityRenderLoop::OnUserPresenceChanged, + base::Unretained(this))); + UpdateVisibilityState(); + input_helper_ = std::make_unique<MixedRealityInputHelper>( window_->hwnd(), weak_ptr_factory_.GetWeakPtr()); @@ -252,7 +264,19 @@ bool MixedRealityRenderLoop::StartRuntime() { if (FAILED(hr)) return false; - return holographic_space_->TrySetDirect3D11Device(device); + if (!holographic_space_->TrySetDirect3D11Device(device)) + return false; + + // Go through one initial dummy frame to update the display info and notify + // the device of the correct values before it sends the initial info to the + // renderer. The frame must be submitted because WMR requires frames to be + // submitted in the order they're created. + UpdateWMRDataForNextFrame(); + UpdateDisplayInfo(); + main_thread_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(on_display_info_changed_, current_display_info_.Clone())); + return SubmitCompositedFrame(); } void MixedRealityRenderLoop::StopRuntime() { @@ -272,6 +296,8 @@ void MixedRealityRenderLoop::StopRuntime() { rendering_params_ = nullptr; camera_ = nullptr; + user_presence_changed_subscription_ = nullptr; + if (input_helper_) input_helper_->Dispose(); input_helper_ = nullptr; @@ -378,6 +404,51 @@ void MixedRealityRenderLoop::OnCurrentStageChanged() { base::Unretained(this))); } +void MixedRealityRenderLoop::OnUserPresenceChanged() { + // Unretained is safe here because the task_runner() gets invalidated + // during Stop() which happens before our destruction + task_runner()->PostTask(FROM_HERE, + base::BindOnce( + [](MixedRealityRenderLoop* render_loop) { + render_loop->UpdateVisibilityState(); + }, + base::Unretained(this))); +} + +void MixedRealityRenderLoop::UpdateVisibilityState() { + // We could've had a task get queued up during or before a StopRuntime call. + // Which would lead to the holographic space being null. In that case, don't + // update the visibility state. We'll get the fresh state when and if the + // runtime starts back up again. + if (!holographic_space_) { + return; + } + + switch (holographic_space_->UserPresence()) { + // Indicates that the browsers immersive content is visible in the headset + // receiving input, and the headset is being worn. + case HolographicSpaceUserPresence:: + HolographicSpaceUserPresence_PresentActive: + SetVisibilityState(device::mojom::XRVisibilityState::VISIBLE); + return; + // Indicates that the browsers immersive content is visible in the headset + // and the headset is being worn, but a modal dialog is capturing input. + case HolographicSpaceUserPresence:: + HolographicSpaceUserPresence_PresentPassive: + // TODO(1016907): Should report VISIBLE_BLURRED, but changed to VISIBLE to + // work around an issue in some versions of Windows Mixed Reality which + // only report PresentPassive and never PresentActive. Should be reverted + // after the Windows fix has been widely released. + SetVisibilityState(device::mojom::XRVisibilityState::VISIBLE); + return; + // Indicates that the browsers immersive content is not visible in the + // headset or the user is not wearing the headset. + case HolographicSpaceUserPresence::HolographicSpaceUserPresence_Absent: + SetVisibilityState(device::mojom::XRVisibilityState::HIDDEN); + return; + } +} + void MixedRealityRenderLoop::EnsureStageBounds() { if (!spatial_stage_) return; @@ -759,8 +830,6 @@ mojom::XRFrameDataPtr MixedRealityRenderLoop::GetNextFrameData() { if (anchor_origin_ && pose_->TryGetViewTransform(anchor_origin_.get(), &view)) { got_view = true; - // TODO(http://crbug.com/931393): Send down emulated_position_, and report - // reset events when this changes. emulated_position_ = false; ABI::Windows::Foundation::Numerics::Matrix4x4 origin_from_attached; if (attached_coordinates->TryGetTransformTo(anchor_origin_.get(), @@ -836,6 +905,8 @@ mojom::XRFrameDataPtr MixedRealityRenderLoop::GetNextFrameData() { ret->pose->input_state = input_helper_->GetInputState(anchor_origin_.get(), timestamp_.get()); + ret->pose->emulated_position = emulated_position_; + if (emulated_position_ && last_origin_from_attached_) { gfx::DecomposedTransform attached_from_view_decomp; attached_from_view_decomp.quaternion = (*ret->pose->orientation); diff --git a/chromium/device/vr/windows_mixed_reality/mixed_reality_renderloop.h b/chromium/device/vr/windows_mixed_reality/mixed_reality_renderloop.h index 863d04056a7..05f1cdfbbe1 100644 --- a/chromium/device/vr/windows_mixed_reality/mixed_reality_renderloop.h +++ b/chromium/device/vr/windows_mixed_reality/mixed_reality_renderloop.h @@ -79,6 +79,9 @@ class MixedRealityRenderLoop : public XRCompositorCommon { void ClearStageStatics(); void OnCurrentStageChanged(); + void OnUserPresenceChanged(); + void UpdateVisibilityState(); + // Will try to update the stage bounds if the following are true: // 1) We have a spatial_stage. // 2) That spatial stage supports bounded movement. @@ -119,6 +122,9 @@ class MixedRealityRenderLoop : public XRCompositorCommon { std::unique_ptr<base::CallbackList<void()>::Subscription> stage_changed_subscription_; + std::unique_ptr<base::CallbackList<void()>::Subscription> + user_presence_changed_subscription_; + std::vector<gfx::Point3F> bounds_; bool bounds_updated_ = false; diff --git a/chromium/device/vr/windows_mixed_reality/mixed_reality_statics.cc b/chromium/device/vr/windows_mixed_reality/mixed_reality_statics.cc index b58e5f3ef18..138333ddf2b 100644 --- a/chromium/device/vr/windows_mixed_reality/mixed_reality_statics.cc +++ b/chromium/device/vr/windows_mixed_reality/mixed_reality_statics.cc @@ -17,11 +17,8 @@ namespace device { -// TODO(crbug.com/941546): Remove namespaces to comply with coding standard. -using namespace ABI::Windows::Graphics::Holographic; -using namespace Microsoft::WRL; -using namespace Microsoft::WRL::Wrappers; -using namespace Windows::Foundation; +namespace Holographic = ABI::Windows::Graphics::Holographic; +using Microsoft::WRL::ComPtr; class DEVICE_VR_EXPORT MixedRealityDeviceStaticsImpl : public MixedRealityDeviceStatics { @@ -34,7 +31,7 @@ class DEVICE_VR_EXPORT MixedRealityDeviceStaticsImpl private: // Adds get_IsAvailable and get_IsSupported to HolographicSpaceStatics. - ComPtr<IHolographicSpaceStatics2> holographic_space_statics_; + ComPtr<Holographic::IHolographicSpaceStatics2> holographic_space_statics_; }; VRTestHook* MixedRealityDeviceStatics::test_hook_ = nullptr; @@ -73,7 +70,7 @@ MixedRealityDeviceStaticsImpl::MixedRealityDeviceStaticsImpl() { base::win::ScopedHString holographic_space_string = base::win::ScopedHString::Create( RuntimeClass_Windows_Graphics_Holographic_HolographicSpace); - ComPtr<IHolographicSpaceStatics> holographic_space_statics; + ComPtr<Holographic::IHolographicSpaceStatics> holographic_space_statics; HRESULT hr = base::win::RoGetActivationFactory( holographic_space_string.get(), IID_PPV_ARGS(&holographic_space_statics)); if (FAILED(hr)) diff --git a/chromium/device/vr/windows_mixed_reality/wrappers/wmr_holographic_space.cc b/chromium/device/vr/windows_mixed_reality/wrappers/wmr_holographic_space.cc index d45d7c208c6..8c55dfbcb05 100644 --- a/chromium/device/vr/windows_mixed_reality/wrappers/wmr_holographic_space.cc +++ b/chromium/device/vr/windows_mixed_reality/wrappers/wmr_holographic_space.cc @@ -18,21 +18,36 @@ #include "device/vr/windows_mixed_reality/wrappers/test/mock_wmr_holographic_space.h" #include "device/vr/windows_mixed_reality/wrappers/wmr_holographic_frame.h" +using ABI::Windows::Foundation::ITypedEventHandler; using ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice; using ABI::Windows::Graphics::Holographic::HolographicAdapterId; +using ABI::Windows::Graphics::Holographic::HolographicSpace; +using HolographicSpaceUserPresence = + ABI::Windows::Graphics::Holographic::HolographicSpaceUserPresence; using ABI::Windows::Graphics::Holographic::IHolographicFrame; using ABI::Windows::Graphics::Holographic::IHolographicSpace; +using ABI::Windows::Graphics::Holographic::IHolographicSpace2; +using Microsoft::WRL::Callback; using Microsoft::WRL::ComPtr; +typedef ITypedEventHandler<HolographicSpace*, IInspectable*> + HolographicSpaceEventHandler; + namespace device { WMRHolographicSpaceImpl::WMRHolographicSpaceImpl( ComPtr<IHolographicSpace> space) : space_(space) { DCHECK(space_); + HRESULT hr = space_.As(&space2_); + if (SUCCEEDED(hr)) { + SubscribeEvents(); + } } -WMRHolographicSpaceImpl::~WMRHolographicSpaceImpl() = default; +WMRHolographicSpaceImpl::~WMRHolographicSpaceImpl() { + UnsubscribeEvents(); +} HolographicAdapterId WMRHolographicSpaceImpl::PrimaryAdapterId() { HolographicAdapterId id; @@ -55,4 +70,52 @@ bool WMRHolographicSpaceImpl::TrySetDirect3D11Device( HRESULT hr = space_->SetDirect3D11Device(device.Get()); return SUCCEEDED(hr); } + +HolographicSpaceUserPresence WMRHolographicSpaceImpl::UserPresence() { + HolographicSpaceUserPresence user_presence = + HolographicSpaceUserPresence::HolographicSpaceUserPresence_PresentActive; + if (space2_) { + HRESULT hr = space2_->get_UserPresence(&user_presence); + DCHECK(SUCCEEDED(hr)); + } + return user_presence; +} + +std::unique_ptr<base::CallbackList<void()>::Subscription> +WMRHolographicSpaceImpl::AddUserPresenceChangedCallback( + const base::RepeatingCallback<void()>& cb) { + return user_presence_changed_callback_list_.Add(cb); +} + +HRESULT WMRHolographicSpaceImpl::OnUserPresenceChanged(IHolographicSpace*, + IInspectable*) { + user_presence_changed_callback_list_.Notify(); + return S_OK; +} + +void WMRHolographicSpaceImpl::SubscribeEvents() { + if (!space2_) { + return; + } + // The destructor ensures that we're unsubscribed so raw this is fine. + auto user_presence_changed_callback = Callback<HolographicSpaceEventHandler>( + this, &WMRHolographicSpaceImpl::OnUserPresenceChanged); + HRESULT hr = space2_->add_UserPresenceChanged( + user_presence_changed_callback.Get(), &user_presence_changed_token_); + DCHECK(SUCCEEDED(hr)); +} + +void WMRHolographicSpaceImpl::UnsubscribeEvents() { + if (!space2_) { + return; + } + + HRESULT hr = S_OK; + if (user_presence_changed_token_.value != 0) { + hr = space2_->remove_UserPresenceChanged(user_presence_changed_token_); + user_presence_changed_token_.value = 0; + DCHECK(SUCCEEDED(hr)); + } +} + } // namespace device diff --git a/chromium/device/vr/windows_mixed_reality/wrappers/wmr_holographic_space.h b/chromium/device/vr/windows_mixed_reality/wrappers/wmr_holographic_space.h index f213a7a6c74..f63859b1aa8 100644 --- a/chromium/device/vr/windows_mixed_reality/wrappers/wmr_holographic_space.h +++ b/chromium/device/vr/windows_mixed_reality/wrappers/wmr_holographic_space.h @@ -9,6 +9,7 @@ #include <memory> +#include "base/callback_list.h" #include "base/macros.h" namespace device { @@ -24,6 +25,10 @@ class WMRHolographicSpace { const Microsoft::WRL::ComPtr< ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>& device) = 0; + virtual ABI::Windows::Graphics::Holographic::HolographicSpaceUserPresence + UserPresence() = 0; + virtual std::unique_ptr<base::CallbackList<void()>::Subscription> + AddUserPresenceChangedCallback(const base::RepeatingCallback<void()>& cb) = 0; }; class WMRHolographicSpaceImpl : public WMRHolographicSpace { @@ -40,10 +45,28 @@ class WMRHolographicSpaceImpl : public WMRHolographicSpace { const Microsoft::WRL::ComPtr< ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>& device) override; + ABI::Windows::Graphics::Holographic::HolographicSpaceUserPresence + UserPresence() override; + std::unique_ptr<base::CallbackList<void()>::Subscription> + AddUserPresenceChangedCallback( + const base::RepeatingCallback<void()>& cb) override; private: + void SubscribeEvents(); + void UnsubscribeEvents(); + + HRESULT OnUserPresenceChanged( + ABI::Windows::Graphics::Holographic::IHolographicSpace*, + IInspectable*); + Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Holographic::IHolographicSpace> space_; + Microsoft::WRL::ComPtr< + ABI::Windows::Graphics::Holographic::IHolographicSpace2> + space2_; + + EventRegistrationToken user_presence_changed_token_; + base::CallbackList<void()> user_presence_changed_callback_list_; DISALLOW_COPY_AND_ASSIGN(WMRHolographicSpaceImpl); }; diff --git a/chromium/device/vr/windows_mixed_reality/wrappers/wmr_input_location.cc b/chromium/device/vr/windows_mixed_reality/wrappers/wmr_input_location.cc index 8f2b7714a26..e0b8c9b2bf1 100644 --- a/chromium/device/vr/windows_mixed_reality/wrappers/wmr_input_location.cc +++ b/chromium/device/vr/windows_mixed_reality/wrappers/wmr_input_location.cc @@ -83,4 +83,15 @@ bool WMRInputLocationImpl::TryGetAngularVelocity( return TryGetValue(ref, angular_velocity); } +bool WMRInputLocationImpl::TryGetPositionAccuracy( + ABI::Windows::UI::Input::Spatial::SpatialInteractionSourcePositionAccuracy* + position_accuracy) const { + DCHECK(position_accuracy); + if (!location3_) + return false; + HRESULT hr = location3_->get_PositionAccuracy(position_accuracy); + DCHECK(SUCCEEDED(hr)); + return true; +} + } // namespace device diff --git a/chromium/device/vr/windows_mixed_reality/wrappers/wmr_input_location.h b/chromium/device/vr/windows_mixed_reality/wrappers/wmr_input_location.h index 7052cc141d7..2d61915aa02 100644 --- a/chromium/device/vr/windows_mixed_reality/wrappers/wmr_input_location.h +++ b/chromium/device/vr/windows_mixed_reality/wrappers/wmr_input_location.h @@ -24,6 +24,10 @@ class WMRInputLocation { virtual bool TryGetAngularVelocity( ABI::Windows::Foundation::Numerics::Vector3* angular_velocity) const = 0; + virtual bool TryGetPositionAccuracy( + ABI::Windows::UI::Input::Spatial:: + SpatialInteractionSourcePositionAccuracy* position_accuracy) + const = 0; }; class WMRInputLocationImpl : public WMRInputLocation { @@ -47,6 +51,9 @@ class WMRInputLocationImpl : public WMRInputLocation { // Uses ISpatialInteractionSourceLocation3. bool TryGetAngularVelocity(ABI::Windows::Foundation::Numerics::Vector3* angular_velocity) const override; + bool TryGetPositionAccuracy(ABI::Windows::UI::Input::Spatial:: + SpatialInteractionSourcePositionAccuracy* + position_accuracy) const override; private: Microsoft::WRL::ComPtr< |