Setup Wi-Fi P2P connection
Add functionalities to setup Wi-Fi P2P connection for subsequent
Wi-Fi P2P mDns tests
Bug: 343311941
Test: atest CtsConnectivityMultiDevicesTestCases
Change-Id: I060509deb32c078829c1108f9ae2a29561e00d53
diff --git a/tests/cts/multidevices/snippet/Wifip2pMultiDevicesSnippet.kt b/tests/cts/multidevices/snippet/Wifip2pMultiDevicesSnippet.kt
index e0929bb..f8c9351 100644
--- a/tests/cts/multidevices/snippet/Wifip2pMultiDevicesSnippet.kt
+++ b/tests/cts/multidevices/snippet/Wifip2pMultiDevicesSnippet.kt
@@ -16,13 +16,28 @@
package com.google.snippet.connectivity
+import android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.net.MacAddress
import android.net.wifi.WifiManager
+import android.net.wifi.p2p.WifiP2pConfig
+import android.net.wifi.p2p.WifiP2pDevice
+import android.net.wifi.p2p.WifiP2pDeviceList
+import android.net.wifi.p2p.WifiP2pGroup
import android.net.wifi.p2p.WifiP2pManager
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.net.module.util.ArrayTrackRecord
+import com.android.testutils.runAsShell
import com.google.android.mobly.snippet.Snippet
import com.google.android.mobly.snippet.rpc.Rpc
+import com.google.snippet.connectivity.Wifip2pMultiDevicesSnippet.Wifip2pIntentReceiver.IntentReceivedEvent.ConnectionChanged
+import com.google.snippet.connectivity.Wifip2pMultiDevicesSnippet.Wifip2pIntentReceiver.IntentReceivedEvent.PeersChanged
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
+import kotlin.test.assertNotNull
import kotlin.test.fail
private const val TIMEOUT_MS = 60000L
@@ -38,6 +53,35 @@
?: fail("Could not get WifiP2pManager service")
}
private lateinit var wifip2pChannel: WifiP2pManager.Channel
+ private val wifip2pIntentReceiver = Wifip2pIntentReceiver()
+
+ private class Wifip2pIntentReceiver : BroadcastReceiver() {
+ val history = ArrayTrackRecord<IntentReceivedEvent>().newReadHead()
+
+ sealed class IntentReceivedEvent {
+ abstract val intent: Intent
+ data class ConnectionChanged(override val intent: Intent) : IntentReceivedEvent()
+ data class PeersChanged(override val intent: Intent) : IntentReceivedEvent()
+ }
+
+ override fun onReceive(context: Context, intent: Intent) {
+ when (intent.action) {
+ WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> {
+ history.add(ConnectionChanged(intent))
+ }
+ WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> {
+ history.add(PeersChanged(intent))
+ }
+ }
+ }
+
+ inline fun <reified T : IntentReceivedEvent> eventuallyExpectedIntent(
+ timeoutMs: Long = TIMEOUT_MS,
+ crossinline predicate: (T) -> Boolean = { true }
+ ): T = history.poll(timeoutMs) { it is T && predicate(it) }.also {
+ assertNotNull(it, "Intent ${T::class} not received within ${timeoutMs}ms.")
+ } as T
+ }
@Rpc(description = "Check whether the device supports Wi-Fi P2P.")
fun isP2pSupported() = wifiManager.isP2pSupported()
@@ -55,6 +99,10 @@
}
}
p2pStateEnabledFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+ // Register an intent filter to receive Wi-Fi P2P intents
+ val filter = IntentFilter(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)
+ filter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION)
+ context.registerReceiver(wifip2pIntentReceiver, filter)
}
@Rpc(description = "Stop Wi-Fi P2P")
@@ -63,5 +111,202 @@
wifip2pManager.cancelConnect(wifip2pChannel, null)
wifip2pManager.removeGroup(wifip2pChannel, null)
}
+ // Unregister the intent filter
+ context.unregisterReceiver(wifip2pIntentReceiver)
+ }
+
+ @Rpc(description = "Get the current device name")
+ fun getDeviceName(): String {
+ // Retrieve current device info
+ val deviceFuture = CompletableFuture<String>()
+ wifip2pManager.requestDeviceInfo(wifip2pChannel) { wifiP2pDevice ->
+ if (wifiP2pDevice != null) {
+ deviceFuture.complete(wifiP2pDevice.deviceName)
+ }
+ }
+ // Return current device name
+ return deviceFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+ }
+
+ @Rpc(description = "Wait for a p2p connection changed intent and check the group")
+ @Suppress("DEPRECATION")
+ fun waitForP2pConnectionChanged(ignoreGroupCheck: Boolean, groupName: String) {
+ wifip2pIntentReceiver.eventuallyExpectedIntent<ConnectionChanged>() {
+ val p2pGroup: WifiP2pGroup? =
+ it.intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP)
+ val groupMatched = p2pGroup?.networkName == groupName
+ return@eventuallyExpectedIntent ignoreGroupCheck || groupMatched
+ }
+ }
+
+ @Rpc(description = "Create a Wi-Fi P2P group")
+ fun createGroup(groupName: String, groupPassphrase: String) {
+ // Create a Wi-Fi P2P group
+ val wifip2pConfig = WifiP2pConfig.Builder()
+ .setNetworkName(groupName)
+ .setPassphrase(groupPassphrase)
+ .build()
+ val createGroupFuture = CompletableFuture<Boolean>()
+ wifip2pManager.createGroup(
+ wifip2pChannel,
+ wifip2pConfig,
+ object : WifiP2pManager.ActionListener {
+ override fun onFailure(reason: Int) = Unit
+ override fun onSuccess() { createGroupFuture.complete(true) }
+ }
+ )
+ createGroupFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+
+ // Ensure the Wi-Fi P2P group is created.
+ waitForP2pConnectionChanged(false, groupName)
+ }
+
+ @Rpc(description = "Start Wi-Fi P2P peers discovery")
+ fun startPeersDiscovery() {
+ // Start discovery Wi-Fi P2P peers
+ wifip2pManager.discoverPeers(wifip2pChannel, null)
+
+ // Ensure the discovery is started
+ val p2pDiscoveryStartedFuture = CompletableFuture<Boolean>()
+ wifip2pManager.requestDiscoveryState(wifip2pChannel) { state ->
+ if (state == WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED) {
+ p2pDiscoveryStartedFuture.complete(true)
+ }
+ }
+ p2pDiscoveryStartedFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+ }
+
+ /**
+ * Get the device address from the given intent that matches the given device name.
+ *
+ * @param peersChangedIntent the intent to get the device address from
+ * @param deviceName the target device name
+ * @return the address of the target device or null if no devices match.
+ */
+ @Suppress("DEPRECATION")
+ private fun getDeviceAddress(peersChangedIntent: Intent, deviceName: String): String? {
+ val peers: WifiP2pDeviceList? =
+ peersChangedIntent.getParcelableExtra(WifiP2pManager.EXTRA_P2P_DEVICE_LIST)
+ return peers?.deviceList?.firstOrNull { it.deviceName == deviceName }?.deviceAddress
+ }
+
+ /**
+ * Ensure the given device has been discovered and returns the associated device address for
+ * connection.
+ *
+ * @param deviceName the target device name
+ * @return the address of the target device.
+ */
+ @Rpc(description = "Ensure the target Wi-Fi P2P device is discovered")
+ fun ensureDeviceDiscovered(deviceName: String): String {
+ val changedEvent = wifip2pIntentReceiver.eventuallyExpectedIntent<PeersChanged>() {
+ return@eventuallyExpectedIntent getDeviceAddress(it.intent, deviceName) != null
+ }
+ return getDeviceAddress(changedEvent.intent, deviceName)
+ ?: fail("Missing device in filtered intent")
+ }
+
+ @Rpc(description = "Invite a Wi-Fi P2P device to the group")
+ fun inviteDeviceToGroup(groupName: String, groupPassphrase: String, deviceAddress: String) {
+ // Connect to the device to send invitation
+ val wifip2pConfig = WifiP2pConfig.Builder()
+ .setNetworkName(groupName)
+ .setPassphrase(groupPassphrase)
+ .setDeviceAddress(MacAddress.fromString(deviceAddress))
+ .build()
+ val connectedFuture = CompletableFuture<Boolean>()
+ wifip2pManager.connect(
+ wifip2pChannel,
+ wifip2pConfig,
+ object : WifiP2pManager.ActionListener {
+ override fun onFailure(reason: Int) = Unit
+ override fun onSuccess() {
+ connectedFuture.complete(true)
+ }
+ }
+ )
+ connectedFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+ }
+
+ private fun runExternalApproverForGroupProcess(
+ deviceAddress: String,
+ isGroupInvitation: Boolean
+ ) {
+ val peer = MacAddress.fromString(deviceAddress)
+ runAsShell(MANAGE_WIFI_NETWORK_SELECTION) {
+ val connectionRequestFuture = CompletableFuture<Boolean>()
+ val attachedFuture = CompletableFuture<Boolean>()
+ wifip2pManager.addExternalApprover(
+ wifip2pChannel,
+ peer,
+ object : WifiP2pManager.ExternalApproverRequestListener {
+ override fun onAttached(deviceAddress: MacAddress) {
+ attachedFuture.complete(true)
+ }
+ override fun onDetached(deviceAddress: MacAddress, reason: Int) = Unit
+ override fun onConnectionRequested(
+ requestType: Int,
+ config: WifiP2pConfig,
+ device: WifiP2pDevice
+ ) {
+ connectionRequestFuture.complete(true)
+ }
+ override fun onPinGenerated(deviceAddress: MacAddress, pin: String) = Unit
+ }
+ )
+ if (isGroupInvitation) attachedFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS) else
+ connectionRequestFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+
+ val resultFuture = CompletableFuture<Boolean>()
+ wifip2pManager.setConnectionRequestResult(
+ wifip2pChannel,
+ peer,
+ WifiP2pManager.CONNECTION_REQUEST_ACCEPT,
+ object : WifiP2pManager.ActionListener {
+ override fun onFailure(reason: Int) = Unit
+ override fun onSuccess() {
+ resultFuture.complete(true)
+ }
+ }
+ )
+ resultFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+
+ val removeFuture = CompletableFuture<Boolean>()
+ wifip2pManager.removeExternalApprover(
+ wifip2pChannel,
+ peer,
+ object : WifiP2pManager.ActionListener {
+ override fun onFailure(reason: Int) = Unit
+ override fun onSuccess() {
+ removeFuture.complete(true)
+ }
+ }
+ )
+ removeFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+ }
+ }
+
+ @Rpc(description = "Accept P2P group invitation from device")
+ fun acceptGroupInvitation(deviceAddress: String) {
+ // Accept the Wi-Fi P2P group invitation
+ runExternalApproverForGroupProcess(deviceAddress, true /* isGroupInvitation */)
+ }
+
+ @Rpc(description = "Wait for connection request from the peer and accept joining")
+ fun waitForPeerConnectionRequestAndAcceptJoining(deviceAddress: String) {
+ // Wait for connection request from the peer and accept joining
+ runExternalApproverForGroupProcess(deviceAddress, false /* isGroupInvitation */)
+ }
+
+ @Rpc(description = "Ensure the target device is connected")
+ fun ensureDeviceConnected(deviceName: String) {
+ // Retrieve peers and ensure the target device is connected
+ val connectedFuture = CompletableFuture<Boolean>()
+ wifip2pManager.requestPeers(wifip2pChannel) { peers -> peers?.deviceList?.any {
+ it.deviceName == deviceName && it.status == WifiP2pDevice.CONNECTED }.let {
+ connectedFuture.complete(true)
+ }
+ }
+ connectedFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
}
}