Les fonctionnalités Wi-Fi Aware permettent aux appareils équipés d'Android 8.0 (niveau d'API 26) ou version ultérieure de se détecter et de se connecter directement les uns aux autres, sans aucun autre type de connectivité entre eux. Wi-Fi Aware est également appelé Neighbor Awareness Networking (NAN).
La mise en réseau Wi-Fi Aware fonctionne en formant des clusters avec les appareils voisins ou en créant un cluster si l'appareil est le premier dans une zone. Ce comportement de clustering s'applique à l'ensemble de l'appareil et est géré par le service système Wi-Fi Aware. Les applications n'ont aucun contrôle sur ce comportement. Elles utilisent les API Wi-Fi Aware pour communiquer avec le service système Wi-Fi Aware, qui gère le matériel Wi-Fi Aware sur l'appareil.
Les API Wi-Fi Aware permettent aux applications d'effectuer les opérations suivantes :
Détecter d'autres appareils : l'API dispose d'un mécanisme permettant de trouver d'autres appareils à proximité. Le processus commence lorsqu'un appareil publie un ou plusieurs services détectables. Ensuite, lorsqu'un appareil s'abonne à un ou plusieurs services et entre dans la zone de couverture Wi-Fi de l'éditeur, l'abonné reçoit une notification indiquant qu'un éditeur correspondant a été détecté. Une fois qu'il a détecté un éditeur, l'abonné peut envoyer un court message ou établir une connexion réseau avec l'appareil détecté. Les appareils peuvent être à la fois éditeurs et abonnés.
Créer une connexion réseau : une fois que deux appareils se sont détectés, ils peuvent créer une connexion réseau Wi-Fi Aware bidirectionnelle sans point d'accès.
Les connexions réseau Wi-Fi Aware offrent des débits plus élevés sur de plus longues distances que les Bluetooth connexions. Ces types de connexions sont utiles pour les applications qui partagent de grandes quantités de données entre les utilisateurs, comme les applications de partage de photos.
Améliorations d'Android 13 (niveau d'API 33)
Sur les appareils équipés d'Android 13 (niveau d'API 33) ou version ultérieure qui prennent en charge le mode de communication instantanée, les applications peuvent utiliser les méthodes
PublishConfig.Builder.setInstantCommunicationModeEnabled() et
SubscribeConfig.Builder.setInstantCommunicationModeEnabled() pour activer ou désactiver ce mode pour une session de détection d'éditeur ou d'abonné. Le mode de communication instantanée accélère l'échange de messages, la découverte des services et la configuration de tout chemin de données dans le cadre d'une session de détection d'éditeur ou d'abonné. Pour déterminer si un appareil est compatible avec ce mode, utilisez la méthode isInstantCommunicationModeSupported().
Améliorations d'Android 12 (niveau d'API 31)
Android 12 (niveau d'API 31) apporte quelques améliorations à Wi-Fi Aware :
- Sur les appareils équipés d'Android 12 (niveau d'API 31) ou version ultérieure, vous pouvez utiliser le
onServiceLost()rappel pour être averti lorsque votre application a perdu un service détecté parce qu'il a été arrêté ou qu'il est hors de portée. - La configuration des chemins de données Wi-Fi Aware a été simplifiée. Les versions précédentes utilisaient la messagerie L2 pour fournir l'adresse MAC de l'initiateur, ce qui introduisait une latence. Sur les appareils équipés d'Android 12 ou version ultérieure, le répondeur (serveur) peut être configuré pour accepter n'importe quel pair, c'est-à-dire qu'il n'a pas besoin de connaître l'adresse MAC de l'initiateur à l'avance. Cela accélère la mise en place du chemin de données et permet d'établir plusieurs liens point à point avec une seule requête réseau.
- Les applications exécutées sous Android 12 ou version ultérieure peuvent utiliser la
WifiAwareManager.getAvailableAwareResources()méthode pour obtenir le nombre de chemins de données, de sessions de publication, et de sessions d'abonnement actuellement disponibles. Cela peut aider l'application à déterminer si elle dispose de suffisamment de ressources pour exécuter la fonctionnalité souhaitée.
Configuration initiale
Pour configurer votre application afin qu'elle utilise la détection et la mise en réseau Wi-Fi Aware, procédez comme suit :
Demandez les autorisations suivantes dans le fichier manifeste de votre application :
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <!-- If your app targets Android 13 (API level 33) or higher, you must declare the NEARBY_WIFI_DEVICES permission. --> <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" <!-- If your app derives location information from Wi-Fi APIs, don't include the "usesPermissionFlags" attribute. --> android:usesPermissionFlags="neverForLocation" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" <!-- If any feature in your app relies on precise location information, don't include the "maxSdkVersion" attribute. --> android:maxSdkVersion="32" />
Vérifiez si l'appareil est compatible avec Wi-Fi Aware à l'aide de l'API
PackageManager, comme indiqué ci-dessous :Kotlin
context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)
Java
context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE);
Vérifiez si Wi-Fi Aware est actuellement disponible. Il peut exister sur l'appareil, mais ne pas être disponible pour le moment, car l'utilisateur a désactivé le Wi-Fi ou la localisation. En fonction de leurs capacités matérielles et micrologicielles, certains appareils peuvent ne pas être compatibles avec Wi-Fi Aware si Wi-Fi Direct, SoftAP ou le partage de connexion sont utilisés. Pour vérifier si Wi-Fi Aware est actuellement disponible, appelez
isAvailable().La disponibilité de Wi-Fi Aware peut changer à tout moment. Votre application doit enregistrer un
BroadcastReceiverpour recevoirACTION_WIFI_AWARE_STATE_CHANGED, qui est envoyé chaque fois que la disponibilité change. Lorsque votre application reçoit l'intent de diffusion, elle doit ignorer toutes les sessions existantes (en supposant que le service Wi-Fi Aware a été interrompu), puis vérifier l'état de disponibilité actuel et ajuster son comportement en conséquence. Exemple :Kotlin
val wifiAwareManager = context.getSystemService(Context.WIFI_AWARE_SERVICE) as WifiAwareManager? val filter = IntentFilter(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED) val myReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { // discard current sessions if (wifiAwareManager?.isAvailable) { ... } else { ... } } } context.registerReceiver(myReceiver, filter)
Java
WifiAwareManager wifiAwareManager = (WifiAwareManager)context.getSystemService(Context.WIFI_AWARE_SERVICE) IntentFilter filter = new IntentFilter(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED); BroadcastReceiver myReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // discard current sessions if (wifiAwareManager.isAvailable()) { ... } else { ... } } }; context.registerReceiver(myReceiver, filter);
Pour en savoir plus, consultez la section Diffusions.
Obtenir une session
Pour commencer à utiliser Wi-Fi Aware, votre application doit obtenir une WifiAwareSession en appelant attach(). Cette méthode effectue les opérations suivantes :
- Active le matériel Wi-Fi Aware.
- Rejoint ou forme un cluster Wi-Fi Aware.
- Crée une session Wi-Fi Aware avec un espace de noms unique qui sert de conteneur pour toutes les sessions de détection créées dans celui-ci.
Si l'application est associée, le système exécute le rappel onAttached().
Ce rappel fournit un objet WifiAwareSession que votre application doit utiliser pour toutes les opérations de session ultérieures. Une application peut utiliser la
session pour publier un service ou
s'abonner à un service.
Votre application ne doit appeler attach() qu'une seule fois. Si
elle l'appelle attach()
plusieurs fois, elle reçoit une session différente pour chaque appel, chacune avec
son propre espace de noms. Cela peut être utile dans des scénarios complexes, mais il est généralement préférable de l'éviter.
Publier un service
Pour rendre un service détectable, appelez la méthode publish(), qui accepte les paramètres suivants :
PublishConfigspécifie le nom du service et d'autres propriétés de configuration, telles que le filtre de correspondance.DiscoverySessionCallbackspécifie les actions à exécuter lorsque des événements se produisent, par exemple lorsque l'abonné reçoit un message.
Exemple :
Kotlin
val config: PublishConfig = PublishConfig.Builder() .setServiceName(AWARE_FILE_SHARE_SERVICE_NAME) .build() awareSession.publish(config, object : DiscoverySessionCallback() { override fun onPublishStarted(session: PublishDiscoverySession) { ... } override fun onMessageReceived(peerHandle: PeerHandle, message: ByteArray) { ... } })
Java
PublishConfig config = new PublishConfig.Builder() .setServiceName(“Aware_File_Share_Service_Name”) .build(); awareSession.publish(config, new DiscoverySessionCallback() { @Override public void onPublishStarted(PublishDiscoverySession session) { ... } @Override public void onMessageReceived(PeerHandle peerHandle, byte[] message) { ... } }, null);
Si la publication réussit, la méthode de rappel onPublishStarted() est appelée.
Après la publication, lorsque les appareils exécutant des applications d'abonné correspondantes se déplacent dans la zone de couverture Wi-Fi de l'appareil de publication, les abonnés découvrent le service. Lorsqu'un abonné détecte un éditeur, celui-ci ne reçoit pas de notification. Toutefois, si l'abonné lui envoie un message, l'éditeur reçoit une notification. Dans ce cas, la méthode de rappel onMessageReceived() est appelée. Vous pouvez utiliser l'
PeerHandle argument de cette méthode pour
envoyer un message à l'abonné ou
créer une connexion avec lui.
Pour arrêter la publication du service, appelez DiscoverySession.close().
Les sessions de détection sont associées à leur WifiAwareSession parent. Si la session parente est fermée, les sessions de détection associées le sont également. Bien que les objets supprimés soient également fermés, le système ne garantit pas le moment où les sessions hors champ sont fermées. Nous vous recommandons donc d'appeler explicitement les méthodes close().
S'abonner à un service
Pour vous abonner à un service, appelez la méthode subscribe(), qui accepte les paramètres suivants :
-
SubscribeConfigspécifie le nom du service auquel s'abonner et d'autres propriétés de configuration, telles que le filtre de correspondance. DiscoverySessionCallbackspécifie les actions à exécuter lorsque des événements se produisent, par exemple lorsqu'un éditeur est détecté.
Exemple :
Kotlin
val config: SubscribeConfig = SubscribeConfig.Builder() .setServiceName(AWARE_FILE_SHARE_SERVICE_NAME) .build() awareSession.subscribe(config, object : DiscoverySessionCallback() { override fun onSubscribeStarted(session: SubscribeDiscoverySession) { ... } override fun onServiceDiscovered( peerHandle: PeerHandle, serviceSpecificInfo: ByteArray, matchFilter: List<ByteArray> ) { ... } }, null)
Java
SubscribeConfig config = new SubscribeConfig.Builder() .setServiceName("Aware_File_Share_Service_Name") .build(); awareSession.subscribe(config, new DiscoverySessionCallback() { @Override public void onSubscribeStarted(SubscribeDiscoverySession session) { ... } @Override public void onServiceDiscovered(PeerHandle peerHandle, byte[] serviceSpecificInfo, List<byte[]> matchFilter) { ... } }, null);
Si l'opération d'abonnement réussit, le système appelle le rappel onSubscribeStarted() dans votre application. Étant donné que vous pouvez utiliser l'argument SubscribeDiscoverySession dans le rappel pour communiquer avec un éditeur une fois que votre application en a détecté un, vous devez enregistrer cette référence. Vous pouvez mettre à jour la session d'abonnement à tout moment en appelant updateSubscribe() sur la session de détection.
À ce stade, votre abonnement attend que les éditeurs correspondants entrent dans la zone de couverture Wi-Fi. Dans ce cas, le système exécute la
onServiceDiscovered()
méthode de rappel. Vous pouvez utiliser le PeerHandle
argument de ce rappel pour envoyer un message ou
créer une connexion à cet éditeur.
Pour arrêter de vous abonner à un service, appelez DiscoverySession.close().
Les sessions de détection sont associées à leur WifiAwareSession parent. Si la session parente est fermée, les sessions de détection associées le sont également. Bien que les objets supprimés soient également fermés, le système ne garantit pas le moment où les sessions hors champ sont fermées. Nous vous recommandons donc d'appeler explicitement les méthodes close().
Envoyer un message
Pour envoyer un message à un autre appareil, vous avez besoin des objets suivants :
Un
DiscoverySession. Cet objet vous permet d'appelersendMessage(). Votre application obtient unDiscoverySessionen publiant un service ou s'abonnant à un service.Le
PeerHandlede l'autre appareil, pour acheminer le message. Votre application obtient lePeerHandled'un autre appareil de deux manières :- Votre application publie un service et reçoit un message d'un abonné.
Elle obtient le
PeerHandlede l'abonné à partir du rappelonMessageReceived(). - Votre application s'abonne à un service. Ensuite, lorsqu'elle détecte un éditeur correspondant, elle obtient le
PeerHandlede l'éditeur à partir duonServiceDiscovered()rappel.
- Votre application publie un service et reçoit un message d'un abonné.
Elle obtient le
Pour envoyer un message, appelez sendMessage(). Les rappels suivants peuvent alors se produire :
- Lorsque le message est reçu par le pair, le système appelle le rappel
onMessageSendSucceeded()dans l'application d'envoi. - Lorsque le pair reçoit un message, le système appelle le rappel
onMessageReceived()dans l'application de réception.
Bien que le PeerHandle soit nécessaire pour communiquer avec les pairs, vous ne devez pas vous y fier comme identifiant permanent des pairs. Des identifiants de niveau supérieur peuvent être utilisés par l'application, intégrés au service de détection lui-même ou dans les messages suivants. Vous pouvez intégrer un identifiant dans le service de détection à l'aide de
la
setMatchFilter()
ou
setServiceSpecificInfo()
méthode de PublishConfig ou
SubscribeConfig. La méthode setMatchFilter() affecte la détection, tandis que la méthode setServiceSpecificInfo() ne l'affecte pas.
L'intégration d'un identifiant dans un message implique la modification du tableau d'octets du message pour inclure un identifiant (par exemple, comme les deux premiers octets).
Créer une connexion
Wi-Fi Aware est compatible avec la mise en réseau client-serveur entre deux appareils Wi-Fi Aware.
Pour configurer la connexion client-serveur :
Utilisez la détection Wi-Fi Aware pour publier un service (sur le serveur) et vous abonner à un service (sur le client).
Une fois que l'abonné a détecté l'éditeur, envoyez un message de l'abonné à l'éditeur.
Démarrez un
ServerSocketsur l'appareil de l'éditeur et définissez ou obtenez son port :Kotlin
val ss = ServerSocket(0) val port = ss.localPort
Java
ServerSocket ss = new ServerSocket(0); int port = ss.getLocalPort();
Utilisez le
ConnectivityManagerpour demander un réseau Wi-Fi Aware sur l'éditeur à l'aide d'unWifiAwareNetworkSpecifier, en spécifiant la session de détection et lePeerHandlede l'abonné, que vous avez obtenu à partir du message transmis par l'abonné :Kotlin
val networkSpecifier = WifiAwareNetworkSpecifier.Builder(discoverySession, peerHandle) .setPskPassphrase("somePassword") .setPort(port) .build() val myNetworkRequest = NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE) .setNetworkSpecifier(networkSpecifier) .build() val callback = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { ... } override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) { ... } override fun onLost(network: Network) { ... } } connMgr.requestNetwork(myNetworkRequest, callback);
Java
NetworkSpecifier networkSpecifier = new WifiAwareNetworkSpecifier.Builder(discoverySession, peerHandle) .setPskPassphrase("somePassword") .setPort(port) .build(); NetworkRequest myNetworkRequest = new NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE) .setNetworkSpecifier(networkSpecifier) .build(); ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback() { @Override public void onAvailable(Network network) { ... } @Override public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) { ... } @Override public void onLost(Network network) { ... } }; ConnectivityManager connMgr.requestNetwork(myNetworkRequest, callback);
Une fois que l'éditeur a demandé un réseau, il doit envoyer un message à l'abonné.
Une fois que l'abonné a reçu le message de l'éditeur, demandez un réseau Wi-Fi Aware sur l'abonné en utilisant la même méthode que sur l'éditeur. Ne spécifiez pas de port lors de la création du
NetworkSpecifier. Les méthodes de rappel appropriées sont appelées lorsque la connexion réseau est disponible, modifiée ou perdue.Une fois la méthode
onAvailable()appelée sur l'abonné, unNetworkobjet est disponible avec lequel vous pouvez ouvrir unSocketpour communiquer avec leServerSocketsur l'éditeur, mais vous avez besoin de connaître l'adresse IPv6 et le port duServerSocket. Vous les obtenez à partir de l'NetworkCapabilitiesobjet fourni dans leonCapabilitiesChanged()rappel :Kotlin
val peerAwareInfo = networkCapabilities.transportInfo as WifiAwareNetworkInfo val peerIpv6 = peerAwareInfo.peerIpv6Addr val peerPort = peerAwareInfo.port ... val socket = network.getSocketFactory().createSocket(peerIpv6, peerPort)
Java
WifiAwareNetworkInfo peerAwareInfo = (WifiAwareNetworkInfo) networkCapabilities.getTransportInfo(); Inet6Address peerIpv6 = peerAwareInfo.getPeerIpv6Addr(); int peerPort = peerAwareInfo.getPort(); ... Socket socket = network.getSocketFactory().createSocket(peerIpv6, peerPort);
Lorsque vous avez terminé avec la connexion réseau, appelez
unregisterNetworkCallback().
Détection de pairs et détection basée sur la localisation
Un appareil doté de fonctionnalités de localisation Wi-Fi RTT peut mesurer directement la distance par rapport aux pairs et utiliser ces informations pour limiter la découverte des services Wi-Fi Aware.
L'API Wi-Fi RTT permet une mesure directe de la distance par rapport à un pair Wi-Fi Aware à l'aide de son adresse MAC ou de son PeerHandle.
La détection Wi-Fi Aware peut être limitée à la détection de services dans une zone de géorepérage spécifique. Par exemple, vous pouvez configurer une zone de géorepérage qui permet de détecter
un appareil publiant un "Aware_File_Share_Service_Name" service qui n'est pas
plus proche de 3 mètres (spécifié comme 3 000 mm) et pas plus éloigné de 10 mètres
(spécifié comme 10 000 mm).
Pour activer le géorepérage, l'éditeur et l'abonné doivent tous deux agir :
L'éditeur doit activer la mesure de la distance sur le service publié à l'aide de setRangingEnabled(true).
Si l'éditeur n'active pas la mesure de la distance, toutes les contraintes de géorepérage spécifiées par l'abonné sont ignorées et la détection normale est effectuée, sans tenir compte de la distance.
L'abonné doit spécifier une zone de géorepérage à l'aide d'une combinaison de setMinDistanceMm et setMaxDistanceMm.
Pour l'une ou l'autre valeur, une distance non spécifiée n'implique aucune limite. Si vous ne spécifiez que la distance maximale, la distance minimale est de 0. Si vous ne spécifiez que la distance minimale, il n'y a pas de maximum.
Lorsqu'un service pair est détecté dans une zone de géorepérage, le onServiceDiscoveredWithinRange rappel est déclenché, ce qui fournit la distance mesurée par rapport au pair. L'API Wi-Fi RTT directe peut ensuite être appelée si nécessaire pour mesurer la distance ultérieurement.