wifidirect_legacy_ap/
lib.rs1use std::sync::mpsc::Sender;
2use std::sync::Mutex;
3
4use windows::core::{IInspectable, Result, HSTRING};
5use windows::Devices::WiFiDirect::{
6 WiFiDirectAdvertisementPublisher, WiFiDirectAdvertisementPublisherStatus,
7 WiFiDirectAdvertisementPublisherStatusChangedEventArgs, WiFiDirectConnectionListener,
8 WiFiDirectConnectionRequestedEventArgs, WiFiDirectConnectionStatus, WiFiDirectDevice,
9 WiFiDirectError,
10};
11use windows::Foundation::{AsyncOperationCompletedHandler, AsyncStatus, TypedEventHandler};
12use windows::Security::Credentials::PasswordCredential;
13
14pub struct WlanHostedNetworkHelper {
15 publisher: Mutex<WiFiDirectAdvertisementPublisher>,
16 message_tx: Mutex<Sender<String>>, }
18
19impl WlanHostedNetworkHelper {
20 pub fn new(ssid: &str, password: &str, message_tx: Sender<String>, success_tx: Sender<bool>) -> Result<Self> {
21 let publisher = start(ssid, password, message_tx.clone(), success_tx.clone())?;
22 Ok(WlanHostedNetworkHelper {
23 publisher: Mutex::new(publisher),
24 message_tx: Mutex::new(message_tx),
25 })
26 }
27
28 pub fn stop(&self) -> Result<()> {
29 let publisher = self
30 .publisher
31 .lock()
32 .expect("Couldn't lock publisher mutex.");
33 let status = publisher.Status()?;
34 if status == WiFiDirectAdvertisementPublisherStatus::Started {
35 publisher.Stop()?;
36 } else {
42 self.message_tx
43 .lock()
44 .expect("Couldn't lock sender mutex.")
45 .send("Stop called but WiFiDirectAdvertisementPublisher is not running".to_string())
46 .expect("Could not send on channel.");
47 }
48 Ok(())
49 }
50}
51
52fn start_listener(tx: Sender<String>) -> Result<()> {
53 let listener = WiFiDirectConnectionListener::new()?;
54 let connection_requested_callback = TypedEventHandler::<
55 WiFiDirectConnectionListener,
56 WiFiDirectConnectionRequestedEventArgs,
57 >::new(move |_sender, args| {
58 tx.send("Connection requested...".to_string())
59 .expect("Couldn't send on tx");
60 let request = args
61 .as_ref()
62 .expect("args == None in connection requested callback")
63 .GetConnectionRequest()?;
64 let device_info = request.DeviceInformation()?;
65 let device_id = device_info.Id()?;
66 let wifi_direct_device = WiFiDirectDevice::FromIdAsync(&device_id)?;
67 let async_operation_completed_callback =
68 AsyncOperationCompletedHandler::<WiFiDirectDevice>::new(|async_operation, status| {
69 if status == AsyncStatus::Completed {
70 let wfd_device = async_operation
71 .as_ref()
72 .expect("No device in WiFiDirectDevice AsyncOperation callback")
73 .GetResults()?;
74 let endpoint_pairs = wfd_device.GetConnectionEndpointPairs()?;
75 let endpoint_pair = endpoint_pairs.GetAt(0)?;
76 let remote_hostname = endpoint_pair.RemoteHostName()?;
77 let _display_name = remote_hostname.DisplayName();
78 let connection_status_changed_callback = TypedEventHandler::<
79 WiFiDirectDevice,
80 IInspectable,
81 >::new(
82 |sender, _inspectable| {
83 let status = sender
84 .as_ref()
85 .expect("No sender in connection status changed handler")
86 .ConnectionStatus()?;
87 match status {
91 WiFiDirectConnectionStatus::Disconnected => {
92 let _device_id = sender
93 .as_ref()
94 .expect("No sender in connection status changed handler")
95 .DeviceId()?;
96 }
97 _ => (),
98 }
99 Ok(())
100 },
101 );
102 let _event_registration_token =
106 wfd_device.ConnectionStatusChanged(&connection_status_changed_callback);
107 }
108 Ok(())
109 });
110 wifi_direct_device.SetCompleted(&async_operation_completed_callback)?;
111 Ok(())
112 });
113 listener.ConnectionRequested(&connection_requested_callback)?;
114 Ok(())
115}
116
117fn start(
118 ssid: &str,
119 password: &str,
120 message_tx: Sender<String>,
121 success_tx: Sender<bool>,
122) -> Result<WiFiDirectAdvertisementPublisher> {
123 let publisher = WiFiDirectAdvertisementPublisher::new()?;
124
125 let _ssid = ssid.to_string();
127 let publisher_status_changed_callback = TypedEventHandler::<
128 WiFiDirectAdvertisementPublisher,
129 WiFiDirectAdvertisementPublisherStatusChangedEventArgs,
130 >::new(move |_sender, args| {
131 let status = args
132 .as_ref()
133 .expect("args == None in status change callback")
134 .Status()?;
135 match status {
136 WiFiDirectAdvertisementPublisherStatus::Created => message_tx
137 .send("Hosted network created".to_string())
138 .expect("Couldn't send on tx"),
139 WiFiDirectAdvertisementPublisherStatus::Stopped => message_tx
140 .send("Hosted network stopped".to_string())
141 .expect("Couldn't send on tx"),
142 WiFiDirectAdvertisementPublisherStatus::Started => {
143 start_listener(message_tx.clone())?;
144 message_tx.send(format!("Hosted network {} has started", _ssid))
145 .expect("Couldn't send on tx");
146 success_tx.send(true).expect("Couldn't send hotspot creation success");
148 }
149 WiFiDirectAdvertisementPublisherStatus::Aborted => {
150 let err = match args
151 .as_ref()
152 .expect("args == None in status change callback")
153 .Error()
154 .expect("Couldn't get error")
155 {
156 WiFiDirectError::RadioNotAvailable => "Radio not available",
157 WiFiDirectError::ResourceInUse => "Resource in use",
158 WiFiDirectError::Success => "No WiFi Direct-capable card or other error",
159 _ => panic!("got bad WiFiDirectError"),
160 };
161 message_tx.send(format!("Hosted network aborted: {}", err))
162 .expect("Couldn't send on tx");
163 success_tx.send(false).expect("Couldn't send hotspot creation failure");
165 }
166 _ => panic!("Bad status received in callback."),
167 }
168 Ok(())
169 });
170 publisher.StatusChanged(&publisher_status_changed_callback)?;
171
172 let advertisement = publisher
174 .Advertisement()
175 .expect("Error getting advertisement");
176 advertisement.SetIsAutonomousGroupOwnerEnabled(true)?;
177
178 let legacy_settings = advertisement.LegacySettings()?;
180 legacy_settings.SetIsEnabled(true)?;
181 let _ssid = HSTRING::from(ssid);
182 legacy_settings.SetSsid(&_ssid)?;
183 let password_credential = PasswordCredential::new()?;
184 password_credential.SetPassword(&HSTRING::from(password))?;
185 legacy_settings.SetPassphrase(&password_credential)?;
186
187 publisher.Start()?;
189
190 Ok(publisher)
191}
192
193#[cfg(test)]
194mod tests {
195 use crate::WlanHostedNetworkHelper;
196 use std::sync::mpsc;
197 use std::thread::spawn;
198
199 #[test]
201 fn run_hosted_network() {
202 let (message_tx, message_rx) = mpsc::channel::<String>();
204 let (success_tx, success_rx) = mpsc::channel::<bool>();
205 let wlan_hosted_network_helper =
206 WlanHostedNetworkHelper::new("WiFiDirectTestNetwork", "TestingThisLibrary", message_tx, success_tx)
207 .unwrap();
208
209 spawn(move || loop {
211 let msg = match message_rx.recv() {
212 Ok(m) => m,
213 Err(e) => {
214 println!("WiFiDirect thread exiting: {}", e);
215 break;
216 }
217 };
218 println!("{}", msg);
219 });
220
221 let started = success_rx.recv().unwrap();
223 if !started {
224 panic!("Failed to start hotspot");
225 }
226
227 std::thread::sleep(std::time::Duration::from_secs(10));
229
230 wlan_hosted_network_helper.stop().expect("Error in stop()");
232 }
233}