2 // System.Net.NetworkInformation.NetworkChange
5 // Gonzalo Paniagua Javier (gonzalo@novell.com)
6 // Aaron Bockover (abock@xamarin.com)
8 // Copyright (c) 2006,2011 Novell, Inc. (http://www.novell.com)
9 // Copyright (c) 2013 Xamarin, Inc. (http://www.xamarin.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.Net.Sockets;
32 using System.Runtime.InteropServices;
33 using System.Threading;
35 namespace System.Net.NetworkInformation {
36 internal interface INetworkChange {
37 event NetworkAddressChangedEventHandler NetworkAddressChanged;
38 event NetworkAvailabilityChangedEventHandler NetworkAvailabilityChanged;
41 public sealed class NetworkChange {
42 static INetworkChange networkChange;
44 static NetworkChange ()
46 if (MacNetworkChange.IsEnabled) {
47 networkChange = new MacNetworkChange ();
49 networkChange = new LinuxNetworkChange ();
53 public static event NetworkAddressChangedEventHandler NetworkAddressChanged {
54 add { networkChange.NetworkAddressChanged += value; }
55 remove { networkChange.NetworkAddressChanged -= value; }
58 public static event NetworkAvailabilityChangedEventHandler NetworkAvailabilityChanged {
59 add { networkChange.NetworkAvailabilityChanged += value; }
60 remove { networkChange.NetworkAvailabilityChanged -= value; }
64 internal sealed class MacNetworkChange : INetworkChange {
65 public static bool IsEnabled {
66 get { return mono_sc_reachability_enabled () != 0; }
69 event NetworkAddressChangedEventHandler networkAddressChanged;
70 event NetworkAvailabilityChangedEventHandler networkAvailabilityChanged;
72 public event NetworkAddressChangedEventHandler NetworkAddressChanged {
76 networkAddressChanged += value;
77 value (null, EventArgs.Empty);
82 networkAddressChanged -= value;
87 public event NetworkAvailabilityChangedEventHandler NetworkAvailabilityChanged {
91 networkAvailabilityChanged += value;
92 var available = handle != IntPtr.Zero && mono_sc_reachability_is_available (handle) != 0;
93 value (null, new NetworkAvailabilityEventArgs (available));
98 networkAvailabilityChanged -= value;
104 MonoSCReachabilityCallback callback;
106 void Callback (int available)
108 var addressChanged = networkAddressChanged;
109 if (addressChanged != null) {
110 addressChanged (null, EventArgs.Empty);
113 var availabilityChanged = networkAvailabilityChanged;
114 if (availabilityChanged != null) {
115 availabilityChanged (null, new NetworkAvailabilityEventArgs (available != 0));
119 void MaybeInitialize ()
122 if (handle == IntPtr.Zero) {
123 callback = new MonoSCReachabilityCallback (Callback);
124 handle = mono_sc_reachability_new (callback);
132 var addressChanged = networkAddressChanged;
133 var availabilityChanged = networkAvailabilityChanged;
134 if (handle != IntPtr.Zero && addressChanged == null && availabilityChanged == null) {
135 mono_sc_reachability_free (handle);
136 handle = IntPtr.Zero;
141 #if MONOTOUCH || MONODROID
142 const string LIBNAME = "__Internal";
144 const string LIBNAME = "MonoPosixHelper";
147 delegate void MonoSCReachabilityCallback (int available);
149 [DllImport (LIBNAME)]
150 static extern int mono_sc_reachability_enabled ();
152 [DllImport (LIBNAME)]
153 static extern IntPtr mono_sc_reachability_new (MonoSCReachabilityCallback callback);
155 [DllImport (LIBNAME)]
156 static extern void mono_sc_reachability_free (IntPtr handle);
158 [DllImport (LIBNAME)]
159 static extern int mono_sc_reachability_is_available (IntPtr handle);
162 internal sealed class LinuxNetworkChange : INetworkChange {
165 Availability = 1 << 0,
169 object _lock = new object ();
171 SocketAsyncEventArgs nl_args;
172 EventType pending_events;
175 NetworkAddressChangedEventHandler AddressChanged;
176 NetworkAvailabilityChangedEventHandler AvailabilityChanged;
178 public event NetworkAddressChangedEventHandler NetworkAddressChanged {
179 add { Register (value); }
180 remove { Unregister (value); }
183 public event NetworkAvailabilityChangedEventHandler NetworkAvailabilityChanged {
184 add { Register (value); }
185 remove { Unregister (value); }
188 //internal Socket (AddressFamily family, SocketType type, ProtocolType proto, IntPtr sock)
195 IntPtr fd = CreateNLSocket ();
196 if (fd.ToInt64 () == -1)
199 nl_sock = new Socket (0, SocketType.Raw, ProtocolType.Udp, fd);
200 nl_args = new SocketAsyncEventArgs ();
201 nl_args.SetBuffer (new byte [8192], 0, 8192);
202 nl_args.Completed += OnDataAvailable;
203 nl_sock.ReceiveAsync (nl_args);
208 // _lock is held by the caller
209 void MaybeCloseSocket ()
211 if (nl_sock == null || AvailabilityChanged != null || AddressChanged != null)
214 CloseNLSocket (nl_sock.Handle);
215 GC.SuppressFinalize (nl_sock);
220 bool GetAvailability ()
222 NetworkInterface [] adapters = NetworkInterface.GetAllNetworkInterfaces ();
223 foreach (NetworkInterface n in adapters) {
224 // TODO: also check for a default route present?
225 if (n.NetworkInterfaceType == NetworkInterfaceType.Loopback)
227 if (n.OperationalStatus == OperationalStatus.Up)
233 void OnAvailabilityChanged (object unused)
235 NetworkAvailabilityChangedEventHandler d = AvailabilityChanged;
236 d (null, new NetworkAvailabilityEventArgs (GetAvailability ()));
239 void OnAddressChanged (object unused)
241 NetworkAddressChangedEventHandler d = AddressChanged;
242 d (null, EventArgs.Empty);
245 void OnEventDue (object unused)
249 evts = pending_events;
251 timer.Change (-1, -1);
253 if ((evts & EventType.Availability) != 0)
254 ThreadPool.QueueUserWorkItem (OnAvailabilityChanged);
255 if ((evts & EventType.Address) != 0)
256 ThreadPool.QueueUserWorkItem (OnAddressChanged);
259 void QueueEvent (EventType type)
263 timer = new Timer (OnEventDue);
264 if (pending_events == 0)
265 timer.Change (150, -1);
266 pending_events |= type;
270 unsafe void OnDataAvailable (object sender, SocketAsyncEventArgs args)
273 fixed (byte *ptr = args.Buffer) {
274 type = ReadEvents (nl_sock.Handle, new IntPtr (ptr), args.BytesTransferred, 8192);
276 nl_sock.ReceiveAsync (nl_args);
281 void Register (NetworkAddressChangedEventHandler d)
287 void Register (NetworkAvailabilityChangedEventHandler d)
290 AvailabilityChanged += d;
293 void Unregister (NetworkAddressChangedEventHandler d)
301 void Unregister (NetworkAvailabilityChangedEventHandler d)
304 AvailabilityChanged -= d;
309 #if MONOTOUCH || MONODROID
310 const string LIBNAME = "__Internal";
312 const string LIBNAME = "MonoPosixHelper";
315 [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
316 static extern IntPtr CreateNLSocket ();
318 [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
319 static extern EventType ReadEvents (IntPtr sock, IntPtr buffer, int count, int size);
321 [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
322 static extern IntPtr CloseNLSocket (IntPtr sock);