# An approximate implementation guide for cross-platform p2p networking using Wi-Fi Aware *Thanks to Andrija Novakovic, Kobi Gurkan, and Natalie Mullins at [Bain Capital Crypto](https://baincapitalcrypto.com/), as well as [chee](https://chee.party/) and [amplice](https://x.com/amplice_eth) for discussions and feedback. This work was my final research project at Bain Capital Crypto. It was part of an exploration into using Wi-Fi Aware for large-scale mesh networks such as Bitchat, which I discuss [here](https://grjte.sh/bitchat-wifi-aware).* Back in March of this year, Ditto published [an article](https://www.ditto.com/blog/cross-platform-p2p-wi-fi-how-the-eu-killed-awdl) about Apple's then-upcoming adoption of the Wi-Fi Aware framework and the promise of cross-platform P2P Wi-Fi. It's an excellent overview of the changing landscape of high-throughput peer-to-peer networking capabilities, including a history of the development of peer-to-peer Wi-Fi technologies from Wi-Fi Ad-hoc to Wi-Fi Direct, AWDL (Apple Wireless Direct Link), and finally Wi-Fi Aware. Since then, Apple's support for the Wi-Fi Aware framework has gone live for any iPhone 12+ running iOS 26.0+ (the last ~5 years of iPhones) as well as for the various iPad models released in roughly the same period. The full list of supported devices is available in [Apple's docs](https://developer.apple.com/documentation/wifiaware). An exciting side note is that Apple's inclusion of the iPad rather than just the iPhone broadens standard support to include tablets, a trend we can hope will be followed on the Android side, where support has mostly been limited to phones. On Android, Wi-Fi Aware has been supported for over 8 years, since the 2017 release of Android 8.0 (Oreo). Despite poor (and poorly-documented) support in the early years, many Android phones do now support Wi-Fi Aware, including the Google Pixel line from Pixel 2 onwards and Samsung phones from the past several years. Unfortunately, there is no straightforward list of Android devices supporting Wi-Fi Aware, but there is an [app for checking](https://github.com/getditto/wifi-aware-checker) (thanks again to Ditto), [a list of *certified* devices](https://www.wi-fi.org/product-finder) on the Wi-Fi Alliance page (where certified devices seem to be a subset of all devices with support), and scattered anecdotes in the depths of internet forums. Does this mean that cross-platform peer-to-peer Wi-Fi connections are now a possibility using Wi-Fi Aware? The short answer is no. Cross-platform Wi-Fi Aware is still impossible. It also seems clear that when it does become possible, it will be annoying. To ease the burden, Ditto is leading the charge and adding support to their SDK, but it is unfortunately closed-source, which means there isn't yet a useful cross-platform reference for the rest of us. This "implementation guide" does not provide a full step-by-step guide to the impossible. Instead, it aims to provide some useful answers to the challenge of cross-platform Wi-Fi Aware implementation by presenting: 1. a discussion of the remaining blockers that stand between us and working cross-platform Wi-Fi Aware networking and 2. an overview of the requirements for implementing cross-platform multi-device connectivity using Wi-Fi Aware, including minimal code samples. 3. a rough guide to what you can do now. ## What is Wi-Fi Aware? Wi-Fi Aware is a standard for peer-to-peer networking over Wi-Fi by the Wi-Fi Alliance. It enables two devices to open a direct Wi-Fi connection between each other without sacrificing other Wi-Fi connectivity. In other words, your phone can remain connected to a router for internet access, while also opening a high-bandwidth connection directly to your friend's phone (or multiple friends) to transfer files, stream videos, or play games without the additional latency of making calls to a server. One of the challenges with peer-to-peer networking technologies is that devices need a way to discover each other, and active scanning (the method used in Bluetooth Classic or Wi-Fi Direct) is very power intensive. Wi-Fi Aware uses a protocol called [Neighbor Awareness Networking (NAN)](https://www.researchgate.net/publication/275673964_Enabling_always_on_service_discovery_Wifi_neighbor_awareness_networking) which is specifically designed to make nearby device discovery efficient, even for mobile devices on the move. The NAN protocol helps devices form and join clusters which sync all devices to use the same "Discovery Window" (channel and time) for exchanging discovery messages. This avoids active scanning or needing to check multiple channels. Instead of being constantly active, the "always-on" discovery of Wi-Fi Aware takes ~5% of the active time of traditional Wi-Fi scanning, leading to huge savings in terms of power expenditure as well as faster discovery. The main advantage of Wi-Fi Aware over previous technologies like Wi-Fi Direct is in this efficient discovery enabled by NAN. However, it was also designed for coexistence with infrastructure Wi-Fi, which means that other Wi-Fi connections (e.g. to a router) remain stable while Wi-Fi Aware is active. ## Wi-Fi Aware connection lifecycle To understand the process of connecting devices via Wi-Fi Aware on iOS, Android or both, it's helpful to understand the basic lifecycle of Wi-Fi Aware connectivity. Historically, discovering and connecting devices via Wi-Fi Aware has looked like this: 1. Connect to a Wi-Fi Aware cluster. (This is what manages the Discovery Window. It's managed by the system service and cannot be controlled at the app level.) 2. Start publishing/subscribing to a specific "service". This is the discovery phase. Subscribers are notified of devices publishing their specified service. They can then send a small message to negotiate a connection. Devices that have discovered each other can send messages up to 255 bytes. Both Android and iOS provide simple APIs for accessing the publish/subscribe discovery capabilities. 3. Once devices have discovered each other, they should open a Wi-Fi connection called a "NAN Data Path" (NDP). To do this, the publisher opens a server socket and the subscriber connects to it as a client. Both TCP and UDP connections can be opened. A password can be specified, to open an encrypted connection. In Wi-Fi Aware version 4, an additional step was added, enabling the option to require pairing for greater security. Pairing can be required at the time of discovery (i.e. discover, then pair, then connect). Alternatively, devices can be required to pair before they can discover each other. Android's default takes the first route, omitting pairing altogether. Apple takes the second, requiring pairing before discovery can begin. On iOS, the flow looks like this: 1. Pair devices 2. Connect to a Wi-Fi Aware cluster 3. Begin discovery (only pre-paired devices are discoverable) 4. Open a client-server connection between peers that have discovered each other. On Android, the default flow looks like the following (although the same flow as iOS could theoretically be implemented using various flags and settings): 1. Connect to a Wi-Fi Aware cluster 2. Begin discovery, optionally requiring devices to pair during discovery. 3. Open a client-server connection between peers that have discovered each other. ## Why doesn't cross-platform Wi-Fi Aware work (yet)? At last, Android and Apple have adopted the same p2p Wi-Fi networking standard! Both Android and iOS have the API support required for cross-platform Wi-Fi Aware connectivity that would enable your iPhone and my Android (or vice versa) to connect and send data at Wi-Fi speeds. The unfortunate sticking point is that iOS only supports Wi-Fi Aware version 4, and I cannot find any Android phone that supports Wi-Fi Aware v4. Frustratingly, device support is incredibly poorly documented, so it is sometimes impossible to know whether or not a device supports it without buying the device and testing it! To address this problem, Ditto created the [wifi-aware-checker](https://github.com/getditto/wifi-aware-checker) app, which can be used to test Wi-Fi Aware availability on Android devices. I've forked and updated this app to [check for Wi-Fi Aware pairing support](https://github.com/grjte/wifi-aware-checker) (in addition to basic Wi-Fi Aware framework support). To test, I bought the Android device I considered most likely to support Wi-Fi Aware version 4 - Google's Pixel 10. Historically, Wi-Fi Aware has been well supported on Google Pixel phones, available since Android 8.0 in phones from Pixel 2 onwards. Samsung has also had support for several years. Unfortunately, the Pixel 10 does not support version 4, and I can't find an announcement discussing Wi-Fi Aware, so it's hard to say when we'll have true support for cross-platform p2p Wi-Fi connections. ![pixel-10-no-pairing](https://hackmd.io/_uploads/ryo1TXBAxe.jpg) Poor device support has been a problem holding back Wi-Fi Aware adoption from the beginning. In the early days, there were new requirements that had to be implemented in hardware. With the more recent versions, there are firmware requirements that must be implemented by the chip manufacturers. Because Apple maintains tighter control over all layers of its stack than Android, they've been able to roll out the required changes at all levels (and likely had many of the requirements already implemented, thanks to AWDL). Android seems to be stuck with a longer development cycle as we wait for hardware support to catch up. The Android API has supported Wi-Fi Aware version 4 since Android 14 (API version 34) was released in 2023, and the Hardware Abstraction Layer (HAL) has been updated, so it looks like we're waiting for firmware updates by the chip manufacturers. The good news is that the API changes do signal an expectation that hardware support will catch up, so hopefully we'll see full support for Wi-Fi Aware version 4 on Android in the next 1-2 years. Perhaps there will be shorter development cycles in the future if cross-platform use takes off. ## Implementing cross-platform Wi-Fi Aware connections between iOS and Android As discussed above, the big blocker to cross-platform connectivity is the lack of hardware support for Wi-Fi Aware version 4 on Android phones. This means that pairing devices, which is required by Apple, is impossible on Android. As far as I can discover, there are no Android phones that support pairing with Wi-Fi Aware. (If you do find one, please reach out and let me know!) When hardware support on Android devices does catch up - what then? A cross-platform Wi-Fi Aware connection must conform to Apple's guidelines for the Wi-Fi Aware Version 4.0 specification, which requires that devices pair before beginning the discovery phase. Here's a rough outline of how to proceed: ### on iOS Apple provides two device pairing methods for Wi-Fi Aware: > For your app to pair devices, or ask the person using your app for access to existing devices, you can use either of the following two methods: > > - [`DeviceDiscoveryUI`](https://developer.apple.com/documentation/devicediscoveryui): Pair any kind of device for device-to-device and app-to-app use cases, such as file transfer and media streaming. > - [`AccessorySetupKit`](https://developer.apple.com/documentation/accessorysetupkit): Pair a person’s personal hardware accessory with the accessory’s companion app, such as for setup and accessory management. The DeviceDiscoveryUI is appropriate for connecting two Apple devices, and the [sample Wi-Fi Aware connectivity implementation](https://developer.apple.com/documentation/wifiaware/building-peer-to-peer-apps) that Apple provides uses this approach. For pairing with an Android device, it seems that the iOS implementation needs to treat the non-iOS device as an accessory, so this sample code won't work. You'll need to handle pairing via the AccessorySetupKit. The only [implementation sample](https://developer.apple.com/documentation/accessorysetupkit/setting-up-and-authorizing-a-bluetooth-accessory) that Apple provides for this uses Bluetooth. Wi-Fi Aware will be similar, but requires some adjustment. From the [docs](https://developer.apple.com/documentation/AccessorySetupKit): > To use AccessorySetupKit with [Wi-Fi Aware](https://developer.apple.com/documentation/WiFiAware), specify Wi-Fi Aware properties in a [`ASDiscoveryDescriptor`](https://developer.apple.com/documentation/accessorysetupkit/asdiscoverydescriptor) prior to beginning accessory discovery. Once you've successfully paired, you'll move on to the Wi-Fi Aware discovery phase using the AccessorySetupKit, which will look something like: ```     **private** **static** **let** subscriberPeer: ASPickerDisplayItem = {         **let** descriptor = ASDiscoveryDescriptor()         descriptor.wifiAwareServiceName = wifiAwareServiceName         descriptor.wifiAwareServiceRole = ASDiscoveryDescriptor.WiFiAwareServiceRole.subscriber         **return** ASPickerDisplayItem(             name: PeerType.subscriber.displayName,             productImage: UIImage(named: PeerType.subscriber.peerName)!,             descriptor: descriptor         )     }() ``` You'll also need to add WiFi to the "AccessorySetupKit - Supports" in the "Information Property List" (in addition to adding your service to the "Wi-Fi Aware Services" as "Publishable" and "Subscribable", which is the same as in Apple's sample code). ### on Android An implementation of Wi-Fi Aware connectivity on Android follows the general pattern outlined in the [Wi-Fi Aware overview](https://developer.android.com/develop/connectivity/wifi/wifi-aware) in the Android docs. 1. Handle the initial setup, including checking that the device supports Wi-Fi Aware and granting required permissions. 2. Get a WifiAwareSession, which turns on the Wi-Fi Aware hardware, joins or forms a cluster, and creates a new Wi-Fi Aware session to hold all discovery sessions you create. 3. Publish or subscribe to a service (or both) to begin the discovery phase. 4. Once devices have discovered each other, create a client-server connection between them. For a cross-platform implementation, we need a few adjustments. With your iPhone now searching for accessories that implement Wi-Fi Aware, your Android implementation must be recognized as an Accessory. That means following Apple's [Accessory Design Guidelines](https://developer.apple.com/accessories/Accessory-Design-Guidelines.pdf) on Wi-Fi Aware, listed in the "Transports" section. The guidelines require the accessory to perform the pairing phase using specific parameters before continuing to the discovery phase, so the largest change required is to update the discovery phase (step 3) to specify that it must perform pairing, as described in the Android API reference for [AwarePairingConfig](https://developer.android.com/reference/android/net/wifi/aware/AwarePairingConfig). Other requirements from Apple's guidelines include: - pairing must be implemented using a pin code - PublishConfig should be set to PUBLISH_TYPE_SOLICITED and SubscribeConfig should be correspondingly set to SUBSCRIBE_TYPE_ACTIVE - the supported cipher suites are specified (NCS-GTK-CCMP-128 and NCS-PK-PASN-128) - the format of the "service name" used during the discovery phase - and many more (listed in the [Accessory Design Guidelines](https://developer.apple.com/accessories/Accessory-Design-Guidelines.pdf)) [Adding pairing to the Android implementation](https://developer.android.com/reference/android/net/wifi/aware/AwarePairingConfig) only changes the initial setup for publishing and subscribing. The modifications to support pairing should look roughly like the code below, (although this part is untested due to the lack of hardware support already discussed). As always when implementing Wi-Fi Aware on Android, the Wi-Fi Aware session needs to be attached first. ``` private fun publish() { // Create device info in TXT Record format for iOS compatibility // According to Apple guidelines, this is required for pairing val deviceInfo = buildString { append("vendorName=$vendorName") append("\u0000") // Null separator between TXT records append("modelName=$modelName") append("\u0000") append("pairingName=$pairingName") }.toByteArray() val publishConfigBuilder = PublishConfig.Builder() .setServiceName(SERVICE_NAME) .setPublishType(PublishConfig.PUBLISH_TYPE_SOLICITED) .setServiceSpecificInfo(deviceInfo) // Add pairing configuration for iOS compatibility only if supported if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && isPairingSupported) { log("Setting up pairing configuration for publisher") val pairingConfig = AwarePairingConfig.Builder() .setPairingSetupEnabled(true) .setPairingCacheEnabled(true) .setBootstrappingMethods(AwarePairingConfig.PAIRING_BOOTSTRAPPING_PIN_CODE_DISPLAY) .setPairingVerificationEnabled(true) .setSupportedCipherSuites(Characteristics.WIFI_AWARE_CIPHER_SUITE_NCS_PK_PASN_128) .build() publishConfigBuilder.setPairingConfig(pairingConfig) } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { log("Pairing not supported on this device - skipping pairing configuration") } val publishConfig = publishConfigBuilder.build() try { wifiAwareSession?.publish(publishConfig, PublisherDiscoverySessionCallback(), null) } catch (e: SecurityException) { log("Permissions missing: ${e.message}") } } ``` ``` private fun subscribe() { // Create device info in TXT Record format for iOS compatibility // According to Apple guidelines, this is required for pairing val deviceInfo = buildString { append("vendorName=$vendorName") append("\u0000") // Null separator between TXT records append("modelName=$modelName") append("\u0000") append("pairingName=$pairingName") }.toByteArray() val subscribeConfigBuilder = SubscribeConfig.Builder() .setServiceName(SERVICE_NAME) .setSubscribeType(SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE) .setServiceSpecificInfo(deviceInfo) // Add pairing configuration for iOS compatibility only if supported if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && isPairingSupported) { log("Setting up pairing configuration for subscriber") val pairingConfig = AwarePairingConfig.Builder() .setPairingSetupEnabled(true) .setPairingCacheEnabled(true) .setPairingVerificationEnabled(true) .setBootstrappingMethods(AwarePairingConfig.PAIRING_BOOTSTRAPPING_PIN_CODE_KEYPAD) .setSupportedCipherSuites(Characteristics.WIFI_AWARE_CIPHER_SUITE_NCS_PK_PASN_128) .build() subscribeConfigBuilder.setPairingConfig(pairingConfig) } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { log("Pairing not supported on this device - skipping pairing configuration") } val subscribeConfig = subscribeConfigBuilder.build() try { wifiAwareSession?.subscribe(subscribeConfig, SubscriberDiscoverySessionCallback(), null) } catch (e: SecurityException) { log("Permissions missing: ${e.message}") } } ``` ## Making the most of same-platform implementations (e.g. Android-Android and iOS-iOS) ### iOS Apple's sample implementation shows everything you need for pairing with other iOS devices, so there isn't much to add here. If you want more, there's another detailed write-up here. Since iOS already had AWDL, the addition of Wi-Fi Aware support doesn't add much power until cross-platform compatibility becomes a reality. ### Android For Android-to-Android connections, there are a few interesting possibilities that may not be immediately obvious from the documentation. Firstly, note that once pairing is supported by the devices themselves (and I do hope that's coming someday), we'll also be able to pair devices via QR code or NFC, among other options. In the realm of _things we can actually do now_, the most interesting one is that while old devices (e.g. Pixel 4a) could only support 2 concurrent Wi-Fi Aware "NDP" connections, newer devices (e.g. Pixel 7+ and perhaps earlier) can support up to 8. This connectivity comes with 2 basic restrictions: 1. Only one "client" connection can be made, which means the other 7 must be made by having 7 other "clients" connect to the device's "server". I've put these in quotes because once the connection is made it's bidirectional, but the connection setup follows a client-server protocol. 2. In order for the device to accept multiple "client" connections, the connection needs to be initiated in a different way from what's described in the Android documentation. (Documentation is available in the [source code](https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Wifi/framework/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java;drc=61197364367c9e404c7da6900658f1b16c42d0da;l=403)) See the code sample below. Here's the code-sample for maximizing device connectivity: ``` private fun requestWifiAwareNetwork(publishDiscoverySession: PublishDiscoverySession, port: Int) { Log.d(TAG, "Requesting single Wi-Fi Aware network for all peers on port: $port") val networkSpecifier = WifiAwareNetworkSpecifier.Builder(publishDiscoverySession) .setPskPassphrase("shared_passphrase") .setPort(port) .build() val networkRequest = NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE) .setNetworkSpecifier(networkSpecifier) .build() val connectivityManager = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager serverNetworkCallback = NetworkConnectivityCallback() connectivityManager.requestNetwork(networkRequest, serverNetworkCallback!!) } inner class NetworkConnectivityCallback : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { ... } override fun onUnavailable() { ... } override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) { ... } ... } ``` Note two key points: 1. In order to enable this, you do not provide a PeerHandle to the `WifiAwareNetworkSpecifier.Builder` 2. This means that the created network is no longer restricted to a specified peer, and the security properties of each p2p connection change, since all connecting peers will now be required to use the same password. To request the wifi aware network on the subscriber side, the subscriber should still specify the PeerHandle (of the publisher device). ``` val networkSpecifier = WifiAwareNetworkSpecifier.Builder(subscribeDiscoverySession, publisherPeerHandle) .setPskPassphrase("shared_passphrase") .build() ``` Another interesting point with Android's more relaxed implementation of the Wi-Fi Aware specification is that since pairing is not required, Wi-Fi Aware could be used much more openly without requiring user interaction through pin codes, NFC/QR code scanning, etc. This presents interesting opportunities for using Wi-Fi Aware in a large-scale mesh such as Bitchat, which is an idea I explore more in more detail [here](https://grjte.sh/bitchat-wifi-aware). ## What's next? Cross-platform peer-to-peer Wi-Fi isn't here yet, but the door has been opened, and it should arrive in the next 1-2 years. Hopefully, we'll see support broaden beyond phone to tablets, personal computers, and IoT devices. There are hints of a wider range of supported device in the future, such as informal acknowledgement from Apple that they're aware of interest in MacOS support, and Espressif's [support for Wi-Fi Aware](https://docs.espressif.com/projects/esp-idf/en/stable/esp32s2/api-reference/network/esp_nan.html) on some of their IoT devices. ![macOS wi-fi aware demand](https://hackmd.io/_uploads/Bkwz6XrReg.png) What possibilities will this enable for us? Apple's existing ecosystem reveals a range of useful options, from Airdrop to AirPlay (peer-to-peer), GameKit, Handoff / Continuity / Universal Clipboard, and Sidecar. The most obvious starting-place is local file sharing. With the newer versions that allow increased numbers of connections, Wi-Fi Aware could be used to rapidly share files locally amongst a group, rather than simply between 2 devices. Other interesting uses include streaming data from one device to another, such as camera input, allowing one device to observe the view and perspective of another device in real time. Back in 2019, Felipe Erias did some interesting explorations into the [possibilities enabled by device-to-device networks](https://darker.ink/writings/Mobile-design-with-device-to-device-networks) using Wi-Fi Aware which included collaborative meeting presentations and a method for bootstrapping a local p2p game by piggybacking on an independently created Wi-Fi Aware network. He proposed the following idea, with an accompanying demo video and talk linked in his article. > if I: > > - connected to another device, > - copied the remote IP, > - launched a different application, > - and pasted that IP in the new app > > I should be able to use Wi-Fi Aware with applications that were not created for it, right? >... there is one application that works out of the box, and it is… **OpenArena** > > OpenArena is a game based on the Quake 3 engine, ported from the desktop to mobile. Though exploring the technology and tinkering with it, we now have a cool demo of fast multiplayer gaming. A final interesting case to consider is to expand beyond the local small-group p2p network used in examples like group media sharing or collaborative presentations and use Wi-Fi Aware as a high-bandwidth, longer-range transport in a large-scale mobile ad-hoc network, such as the one underlying the Bitchat p2p messenger. I've written a separate article with a preliminary exploration of this idea which discusses some of the possibilities as well as implementation challenges: [An exploration of Wi-Fi Aware for Bitchat (and other ad-hoc networks)](https://grjte.sh/bitchat-wifi-aware). Eight years after its rollout on Android, Wi-Fi Aware is finally beginning to show promise for cross-platform p2p Wi-Fi. It's not here yet, but the groundwork has been laid, and I hope that the next few years will bring a new range of capabilities and applications built on local high-bandwidth peer-to-peer networks. *We'd love to chat to anyone exploring problems in ad-hoc networking and p2p communications or related topics of local networks/data/compute. If this is you, please reach out to [@grjte on X](https://bsky.app/profile/grjte.sh) or [@grjte.sh on Bluesky](https://bsky.app/profile/grjte.sh) or to Bain Capital Crypto by [email](https://baincapitalcrypto.com/contact/), [Bluesky](https://bsky.app/profile/baincapitalcrypto.com), or [X](https://x.com/BainCapCrypto).*