Refactor snippet RPC methods and events.
1. start/stop ProviderSimulator change to setup/teardown
2. Do not auto start FastPairSimulator in onA2DPSinkProfileConnected callback
3. Add onA2DPSinkProfileConnected event
Test: atest -v CtsSeekerDiscoverProviderTest
BUG: 214015364
Change-Id: I40289fb48919b0890c230b991bc4a0a34a5669d1
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorController.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorController.kt
index 0eacb71..e700144 100644
--- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorController.kt
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorController.kt
@@ -23,62 +23,99 @@
import com.google.android.mobly.snippet.util.Log
import com.google.common.io.BaseEncoding
-class FastPairProviderSimulatorController(
- private val context: Context,
- private val modelId: String,
- private val antiSpoofingKeyString: String,
- private val eventListener: EventListener,
-) : BluetoothController.EventListener {
+class FastPairProviderSimulatorController(private val context: Context) :
+ FastPairSimulator.AdvertisingChangedCallback, BluetoothController.EventListener {
private lateinit var bluetoothController: BluetoothController
- lateinit var simulator: FastPairSimulator
+ private lateinit var eventListener: EventListener
+ private var simulator: FastPairSimulator? = null
- fun startProviderSimulator() {
+ fun setupProviderSimulator(listener: EventListener) {
+ eventListener = listener
+
bluetoothController = BluetoothController(context, this)
bluetoothController.registerBluetoothStateReceiver()
bluetoothController.enableBluetooth()
bluetoothController.connectA2DPSinkProfile()
}
- fun stopProviderSimulator() {
- simulator.destroy()
+ fun teardownProviderSimulator() {
+ simulator?.destroy()
bluetoothController.unregisterBluetoothStateReceiver()
}
- override fun onA2DPSinkProfileConnected() {
- createFastPairSimulator()
+ fun startModelIdAdvertising(
+ modelId: String,
+ antiSpoofingKeyString: String,
+ listener: EventListener
+ ) {
+ eventListener = listener
+
+ val antiSpoofingKey = BaseEncoding.base64().decode(antiSpoofingKeyString)
+ simulator = FastPairSimulator(
+ context, FastPairSimulator.Options.builder(modelId)
+ .setAdvertisingModelId(modelId)
+ .setBluetoothAddress(null)
+ .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
+ .setAdvertisingChangedCallback(this)
+ .setAntiSpoofingPrivateKey(antiSpoofingKey)
+ .setUseRandomSaltForAccountKeyRotation(false)
+ .setDataOnlyConnection(false)
+ .setShowsPasskeyConfirmation(false)
+ .setRemoveAllDevicesDuringPairing(true)
+ .build()
+ )
+
+ // TODO(b/222070055): Workaround the FATAL EXCEPTION after the end of initial pairing.
+ simulator!!.setSuppressSubsequentPairingNotification(true)
}
+ fun getProviderSimulatorBleAddress() = simulator!!.bleAddress!!
+
+ /**
+ * Called when we change our BLE advertisement.
+ *
+ * @param isAdvertising the advertising status.
+ */
+ override fun onAdvertisingChanged(isAdvertising: Boolean) {
+ Log.i("FastPairSimulator onAdvertisingChanged(isAdvertising: $isAdvertising)")
+ eventListener.onAdvertisingChange(isAdvertising)
+ }
+
+ /** The callback for the first onServiceConnected of A2DP sink profile. */
+ override fun onA2DPSinkProfileConnected() {
+ eventListener.onA2DPSinkProfileConnected()
+ }
+
+ /**
+ * Reports the current bond state of the remote device.
+ *
+ * @param bondState the bond state of the remote device.
+ */
override fun onBondStateChanged(bondState: Int) {
}
+ /**
+ * Reports the current connection state of the remote device.
+ *
+ * @param connectionState the bond state of the remote device.
+ */
override fun onConnectionStateChanged(connectionState: Int) {
}
+ /**
+ * Reports the current scan mode of the local Adapter.
+ *
+ * @param mode the current scan mode of the local Adapter.
+ */
override fun onScanModeChange(mode: Int) {
eventListener.onScanModeChange(FastPairSimulator.scanModeToString(mode))
}
- private fun createFastPairSimulator() {
- val antiSpoofingKey = BaseEncoding.base64().decode(antiSpoofingKeyString)
- simulator = FastPairSimulator(context, FastPairSimulator.Options.builder(modelId)
- .setAdvertisingModelId(modelId)
- .setBluetoothAddress(null)
- .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
- .setAdvertisingChangedCallback {
- val isAdvertising = simulator.isAdvertising
- Log.i("FastPairSimulator callback(), isAdvertising: $isAdvertising")
- eventListener.onAdvertisingChange(isAdvertising)
- }
- .setAntiSpoofingPrivateKey(antiSpoofingKey)
- .setUseRandomSaltForAccountKeyRotation(false)
- .setDataOnlyConnection(false)
- .setShowsPasskeyConfirmation(false)
- .setRemoveAllDevicesDuringPairing(true)
- .build())
- }
-
/** Interface for listening the events from Fast Pair Provider Simulator. */
interface EventListener {
+ /** Reports the first onServiceConnected of A2DP sink profile. */
+ fun onA2DPSinkProfileConnected()
+
/**
* Reports the current scan mode of the local Adapter.
*
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorSnippet.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorSnippet.kt
index 356823e..39edfe4 100644
--- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorSnippet.kt
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorSnippet.kt
@@ -28,33 +28,44 @@
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
class FastPairProviderSimulatorSnippet : Snippet {
private val context: Context = InstrumentationRegistry.getInstrumentation().context
- private lateinit var fastPairProviderSimulatorController: FastPairProviderSimulatorController
+ private val fastPairProviderSimulatorController = FastPairProviderSimulatorController(context)
+
+ /** Sets up the Fast Pair provider simulator. */
+ @AsyncRpc(description = "Sets up FP provider simulator.")
+ fun setupProviderSimulator(callbackId: String) {
+ fastPairProviderSimulatorController.setupProviderSimulator(ProviderStatusEvents(callbackId))
+ }
/**
- * Starts the Fast Pair provider simulator.
+ * Starts model id advertising for scanning and initial pairing.
*
* @param callbackId the callback ID corresponding to the
* [FastPairProviderSimulatorSnippet#startProviderSimulator] call that started the scanning.
* @param modelId a 3-byte hex string for seeker side to recognize the device (ex: 0x00000C).
* @param antiSpoofingKeyString a public key for registered headsets.
*/
- @AsyncRpc(description = "Starts FP provider simulator for seekers to discover.")
- fun startProviderSimulator(callbackId: String, modelId: String, antiSpoofingKeyString: String) {
- fastPairProviderSimulatorController = FastPairProviderSimulatorController(
- context, modelId, antiSpoofingKeyString, ProviderStatusEvents(callbackId)
+ @AsyncRpc(description = "Starts model id advertising for scanning and initial pairing.")
+ fun startModelIdAdvertising(
+ callbackId: String,
+ modelId: String,
+ antiSpoofingKeyString: String
+ ) {
+ fastPairProviderSimulatorController.startModelIdAdvertising(
+ modelId,
+ antiSpoofingKeyString,
+ ProviderStatusEvents(callbackId)
)
- fastPairProviderSimulatorController.startProviderSimulator()
}
- /** Stops the Fast Pair provider simulator. */
- @Rpc(description = "Stops FP provider simulator.")
- fun stopProviderSimulator() {
- fastPairProviderSimulatorController.stopProviderSimulator()
+ /** Tears down the Fast Pair provider simulator. */
+ @Rpc(description = "Tears down FP provider simulator.")
+ fun teardownProviderSimulator() {
+ fastPairProviderSimulatorController.teardownProviderSimulator()
}
/** Gets BLE mac address of the Fast Pair provider simulator. */
@Rpc(description = "Gets BLE mac address of the Fast Pair provider simulator.")
fun getBluetoothLeAddress(): String {
- return fastPairProviderSimulatorController.simulator.bleAddress!!
+ return fastPairProviderSimulatorController.getProviderSimulatorBleAddress()
}
}
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/ProviderStatusEvents.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/ProviderStatusEvents.kt
index 20983d3..efa4f02 100644
--- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/ProviderStatusEvents.kt
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/ProviderStatusEvents.kt
@@ -22,6 +22,11 @@
class ProviderStatusEvents(private val callbackId: String) :
FastPairProviderSimulatorController.EventListener {
+ /** Reports the first onServiceConnected of A2DP sink profile. */
+ override fun onA2DPSinkProfileConnected() {
+ postSnippetEvent(callbackId, "onA2DPSinkProfileConnected") {}
+ }
+
/**
* Indicates the Bluetooth scan mode of the Fast Pair provider simulator has changed.
*
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/app/MainActivity.java b/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/app/MainActivity.java
index 97f3bf4..e916c53 100644
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/app/MainActivity.java
+++ b/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/app/MainActivity.java
@@ -671,7 +671,7 @@
mAppLaunchSwitch.isChecked() ? MODEL_ID_APP_LAUNCH : modelId)
.setBluetoothAddress(finalBluetoothAddress)
.setTxPowerLevel(toTxPowerLevel(txPower))
- .setAdvertisingChangedCallback(this::updateStatusView)
+ .setAdvertisingChangedCallback(isAdvertising -> updateStatusView())
.setAntiSpoofingPrivateKey(antiSpoofingKey)
.setUseRandomSaltForAccountKeyRotation(useRandomSaltForAccountKeyRotation)
.setDataOnlyConnection(device != null && device.getDataOnlyConnection())
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/FastPairSimulator.java b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/FastPairSimulator.java
index aa7daa6..232e84b 100644
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/FastPairSimulator.java
+++ b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/FastPairSimulator.java
@@ -572,8 +572,12 @@
/** An optional way to get advertising status updates. */
public interface AdvertisingChangedCallback {
- /** Called when we change our BLE advertisement. */
- void onAdvertisingChanged();
+ /**
+ * Called when we change our BLE advertisement.
+ *
+ * @param isAdvertising the advertising status.
+ */
+ void onAdvertisingChanged(boolean isAdvertising);
}
/** A way for tests to get callbacks when passkey confirmation is invoked. */
@@ -758,7 +762,7 @@
.setModelId(Ascii.toUpperCase(modelId))
.setAdvertisingModelId(Ascii.toUpperCase(modelId))
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
- .setAdvertisingChangedCallback(() -> {
+ .setAdvertisingChangedCallback(isAdvertising -> {
})
.setIncludeTransportDataDescriptor(true)
.setUseRandomSaltForAccountKeyRotation(false)
@@ -1077,7 +1081,7 @@
public void setIsAdvertising(boolean isAdvertising) {
if (this.mIsAdvertising != isAdvertising) {
this.mIsAdvertising = isAdvertising;
- mOptions.getAdvertisingChangedCallback().onAdvertisingChanged();
+ mOptions.getAdvertisingChangedCallback().onAdvertisingChanged(isAdvertising);
}
}
diff --git a/nearby/tests/multidevices/host/fast_pair_provider_simulator.py b/nearby/tests/multidevices/host/fast_pair_provider_simulator.py
index 1f62dfb..37ba387 100644
--- a/nearby/tests/multidevices/host/fast_pair_provider_simulator.py
+++ b/nearby/tests/multidevices/host/fast_pair_provider_simulator.py
@@ -11,6 +11,7 @@
FP_PROVIDER_SIMULATOR_SNIPPETS_PACKAGE = 'android.nearby.multidevices'
# Events reported from the provider simulator snippet.
+ON_A2DP_SINK_PROFILE_CONNECT_EVENT = 'onA2DPSinkProfileConnected'
ON_SCAN_MODE_CHANGE_EVENT = 'onScanModeChange'
ON_ADVERTISING_CHANGE_EVENT = 'onAdvertisingChange'
@@ -39,21 +40,50 @@
self._ad.load_snippet(
name='fp', package=FP_PROVIDER_SIMULATOR_SNIPPETS_PACKAGE)
- def start_provider_simulator(self, model_id: str,
- anti_spoofing_key: str) -> None:
- """Starts the Fast Pair provider simulator.
+ def setup_provider_simulator(self, timeout_seconds: int) -> None:
+ """Sets up the Fast Pair provider simulator.
+
+ Args:
+ timeout_seconds: The number of seconds to wait before giving up.
+ """
+ setup_status_callback = self._ad.fp.setupProviderSimulator()
+
+ def _on_a2dp_sink_profile_connect_event_received(_, elapsed_time: int) -> bool:
+ self._ad.log.info('Provider simulator connected to A2DP sink in %d seconds.',
+ elapsed_time)
+ return True
+
+ def _on_a2dp_sink_profile_connect_event_waiting(elapsed_time: int) -> None:
+ self._ad.log.info(
+ 'Still waiting "%s" event callback from provider side '
+ 'after %d seconds...', ON_A2DP_SINK_PROFILE_CONNECT_EVENT, elapsed_time)
+
+ def _on_a2dp_sink_profile_connect_event_missed() -> None:
+ asserts.fail(f'Timed out after {timeout_seconds} seconds waiting for '
+ f'the specific "{ON_A2DP_SINK_PROFILE_CONNECT_EVENT}" event.')
+
+ wait_for_event(
+ callback_event_handler=setup_status_callback,
+ event_name=ON_A2DP_SINK_PROFILE_CONNECT_EVENT,
+ timeout_seconds=timeout_seconds,
+ on_received=_on_a2dp_sink_profile_connect_event_received,
+ on_waiting=_on_a2dp_sink_profile_connect_event_waiting,
+ on_missed=_on_a2dp_sink_profile_connect_event_missed)
+
+ def start_model_id_advertising(self, model_id: str, anti_spoofing_key: str) -> None:
+ """Starts model id advertising for scanning and initial pairing.
Args:
model_id: A 3-byte hex string for seeker side to recognize the device (ex:
0x00000C).
anti_spoofing_key: A public key for registered headsets.
"""
- self._provider_status_callback = self._ad.fp.startProviderSimulator(
- model_id, anti_spoofing_key)
+ self._provider_status_callback = (
+ self._ad.fp.startModelIdAdvertising(model_id, anti_spoofing_key))
- def stop_provider_simulator(self) -> None:
- """Stops the Fast Pair provider simulator."""
- self._ad.fp.stopProviderSimulator()
+ def teardown_provider_simulator(self) -> None:
+ """Tears down the Fast Pair provider simulator."""
+ self._ad.fp.teardownProviderSimulator()
@retry.retry(tries=3)
def get_ble_mac_address(self) -> str:
diff --git a/nearby/tests/multidevices/host/seeker_discover_provider_test.py b/nearby/tests/multidevices/host/seeker_discover_provider_test.py
index a52ca15..32976c1 100644
--- a/nearby/tests/multidevices/host/seeker_discover_provider_test.py
+++ b/nearby/tests/multidevices/host/seeker_discover_provider_test.py
@@ -17,6 +17,7 @@
# Default public key to simulate as registered headsets.
DEFAULT_ANTI_SPOOFING_KEY = 'Cbj9eCJrTdDgSYxLkqtfADQi86vIaMvxJsQ298sZYWE='
# Time in seconds for events waiting.
+SETUP_TIMEOUT_SEC = 5
BECOME_DISCOVERABLE_TIMEOUT_SEC = 10
START_ADVERTISING_TIMEOUT_SEC = 5
SCAN_TIMEOUT_SEC = 30
@@ -47,8 +48,8 @@
def setup_test(self) -> None:
super().setup_test()
- self._provider.start_provider_simulator(DEFAULT_MODEL_ID,
- DEFAULT_ANTI_SPOOFING_KEY)
+ self._provider.setup_provider_simulator(SETUP_TIMEOUT_SEC)
+ self._provider.start_model_id_advertising(DEFAULT_MODEL_ID, DEFAULT_ANTI_SPOOFING_KEY)
self._provider.wait_for_discoverable_mode(BECOME_DISCOVERABLE_TIMEOUT_SEC)
self._provider.wait_for_advertising_start(START_ADVERTISING_TIMEOUT_SEC)
self._seeker.start_scan()
@@ -56,7 +57,7 @@
def teardown_test(self) -> None:
super().teardown_test()
self._seeker.stop_scan()
- self._provider.stop_provider_simulator()
+ self._provider.teardown_provider_simulator()
# Create per-test excepts of logcat.
for dut in self.duts:
dut.services.create_output_excerpts_all(self.current_test_info)