diff options
Diffstat (limited to 'mcs/class/System/System.Net.NetworkInformation/NetworkChange.cs')
-rw-r--r-- | mcs/class/System/System.Net.NetworkInformation/NetworkChange.cs | 319 |
1 files changed, 245 insertions, 74 deletions
diff --git a/mcs/class/System/System.Net.NetworkInformation/NetworkChange.cs b/mcs/class/System/System.Net.NetworkInformation/NetworkChange.cs index c60954c949..de9754e951 100644 --- a/mcs/class/System/System.Net.NetworkInformation/NetworkChange.cs +++ b/mcs/class/System/System.Net.NetworkInformation/NetworkChange.cs @@ -2,8 +2,8 @@ // System.Net.NetworkInformation.NetworkChange // // Authors: -// Gonzalo Paniagua Javier (gonzalo@novell.com) -// Aaron Bockover (abock@xamarin.com) +// Gonzalo Paniagua Javier (LinuxNetworkChange) (gonzalo@novell.com) +// Aaron Bockover (MacNetworkChange) (abock@xamarin.com) // // Copyright (c) 2006,2011 Novell, Inc. (http://www.novell.com) // Copyright (c) 2013 Xamarin, Inc. (http://www.xamarin.com) @@ -15,10 +15,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -28,137 +28,298 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // +using System; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Threading; +#if NETWORK_CHANGE_STANDALONE +namespace NetworkInformation { + + public class NetworkAvailabilityEventArgs : EventArgs + { + public bool IsAvailable { get; set; } + + public NetworkAvailabilityEventArgs (bool available) + { + IsAvailable = available; + } + } + + public delegate void NetworkAddressChangedEventHandler (object sender, EventArgs args); + public delegate void NetworkAvailabilityChangedEventHandler (object sender, NetworkAvailabilityEventArgs args); +#else namespace System.Net.NetworkInformation { - internal interface INetworkChange { +#endif + + internal interface INetworkChange : IDisposable { event NetworkAddressChangedEventHandler NetworkAddressChanged; event NetworkAvailabilityChangedEventHandler NetworkAvailabilityChanged; + bool HasRegisteredEvents { get; } } public sealed class NetworkChange { static INetworkChange networkChange; - static NetworkChange () + public static event NetworkAddressChangedEventHandler NetworkAddressChanged { + add { + lock (typeof (INetworkChange)) { + MaybeCreate (); + if (networkChange != null) + networkChange.NetworkAddressChanged += value; + } + } + + remove { + lock (typeof (INetworkChange)) { + if (networkChange != null) { + networkChange.NetworkAddressChanged -= value; + MaybeDispose (); + } + } + } + } + + public static event NetworkAvailabilityChangedEventHandler NetworkAvailabilityChanged { + add { + lock (typeof (INetworkChange)) { + MaybeCreate (); + if (networkChange != null) + networkChange.NetworkAvailabilityChanged += value; + } + } + + remove { + lock (typeof (INetworkChange)) { + if (networkChange != null) { + networkChange.NetworkAvailabilityChanged -= value; + MaybeDispose (); + } + } + } + } + + static void MaybeCreate () { - if (MacNetworkChange.IsEnabled) { + if (networkChange != null) + return; + + try { networkChange = new MacNetworkChange (); - } else { + } catch { +#if !NETWORK_CHANGE_STANDALONE networkChange = new LinuxNetworkChange (); +#endif } } - public static event NetworkAddressChangedEventHandler NetworkAddressChanged { - add { networkChange.NetworkAddressChanged += value; } - remove { networkChange.NetworkAddressChanged -= value; } + static void MaybeDispose () + { + if (networkChange != null && networkChange.HasRegisteredEvents) { + networkChange.Dispose (); + networkChange = null; + } } + } - public static event NetworkAvailabilityChangedEventHandler NetworkAvailabilityChanged { - add { networkChange.NetworkAvailabilityChanged += value; } - remove { networkChange.NetworkAvailabilityChanged -= value; } + internal sealed class MacNetworkChange : INetworkChange + { + const string DL_LIB = "/usr/lib/libSystem.dylib"; + const string CORE_SERVICES_LIB = "/System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration"; + const string CORE_FOUNDATION_LIB = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"; + + [UnmanagedFunctionPointerAttribute (CallingConvention.Cdecl)] + delegate void SCNetworkReachabilityCallback (IntPtr target, NetworkReachabilityFlags flags, IntPtr info); + + [DllImport (DL_LIB)] + static extern IntPtr dlopen (string path, int mode); + + [DllImport (DL_LIB)] + static extern IntPtr dlsym (IntPtr handle, string symbol); + + [DllImport (DL_LIB)] + static extern int dlclose (IntPtr handle); + + [DllImport (CORE_FOUNDATION_LIB)] + static extern void CFRelease (IntPtr handle); + + [DllImport (CORE_FOUNDATION_LIB)] + static extern IntPtr CFRunLoopGetMain (); + + [DllImport (CORE_SERVICES_LIB)] + static extern IntPtr SCNetworkReachabilityCreateWithAddress (IntPtr allocator, ref sockaddr_in sockaddr); + + [DllImport (CORE_SERVICES_LIB)] + static extern bool SCNetworkReachabilityGetFlags (IntPtr reachability, out NetworkReachabilityFlags flags); + + [DllImport (CORE_SERVICES_LIB)] + static extern bool SCNetworkReachabilitySetCallback (IntPtr reachability, SCNetworkReachabilityCallback callback, ref SCNetworkReachabilityContext context); + + [DllImport (CORE_SERVICES_LIB)] + static extern bool SCNetworkReachabilityScheduleWithRunLoop (IntPtr reachability, IntPtr runLoop, IntPtr runLoopMode); + + [DllImport (CORE_SERVICES_LIB)] + static extern bool SCNetworkReachabilityUnscheduleFromRunLoop (IntPtr reachability, IntPtr runLoop, IntPtr runLoopMode); + + [StructLayout (LayoutKind.Explicit, Size = 28)] + struct sockaddr_in { + [FieldOffset (0)] public byte sin_len; + [FieldOffset (1)] public byte sin_family; + + public static sockaddr_in Create () + { + return new sockaddr_in { + sin_len = 28, + sin_family = 2 // AF_INET + }; + } } - } - internal sealed class MacNetworkChange : INetworkChange { - public static bool IsEnabled { - get { return mono_sc_reachability_enabled () != 0; } + [StructLayout (LayoutKind.Sequential)] + struct SCNetworkReachabilityContext { + public IntPtr version; + public IntPtr info; + public IntPtr retain; + public IntPtr release; + public IntPtr copyDescription; + } + + [Flags] + enum NetworkReachabilityFlags { + None = 0, + TransientConnection = 1 << 0, + Reachable = 1 << 1, + ConnectionRequired = 1 << 2, + ConnectionOnTraffic = 1 << 3, + InterventionRequired = 1 << 4, + ConnectionOnDemand = 1 << 5, + IsLocalAddress = 1 << 16, + IsDirect = 1 << 17, + IsWWAN = 1 << 18, + ConnectionAutomatic = ConnectionOnTraffic } + IntPtr handle; + IntPtr runLoopMode; + SCNetworkReachabilityCallback callback; + bool scheduledWithRunLoop; + NetworkReachabilityFlags flags; + event NetworkAddressChangedEventHandler networkAddressChanged; event NetworkAvailabilityChangedEventHandler networkAvailabilityChanged; public event NetworkAddressChangedEventHandler NetworkAddressChanged { add { - if (value != null) { - MaybeInitialize (); - networkAddressChanged += value; - value (null, EventArgs.Empty); - } + value (null, EventArgs.Empty); + networkAddressChanged += value; } - remove { - networkAddressChanged -= value; - MaybeDispose (); - } + remove { networkAddressChanged -= value; } } public event NetworkAvailabilityChangedEventHandler NetworkAvailabilityChanged { add { - if (value != null) { - MaybeInitialize (); - networkAvailabilityChanged += value; - var available = handle != IntPtr.Zero && mono_sc_reachability_is_available (handle) != 0; - value (null, new NetworkAvailabilityEventArgs (available)); - } + value (null, new NetworkAvailabilityEventArgs (IsAvailable)); + networkAvailabilityChanged += value; } - remove { - networkAvailabilityChanged -= value; - MaybeDispose (); + remove { networkAvailabilityChanged -= value; } + } + + bool IsAvailable { + get { + return (flags & NetworkReachabilityFlags.Reachable) != 0 && + (flags & NetworkReachabilityFlags.ConnectionRequired) == 0; } } - IntPtr handle; - MonoSCReachabilityCallback callback; + public bool HasRegisteredEvents { + get { return networkAddressChanged != null || networkAvailabilityChanged != null; } + } - void Callback (int available) + public MacNetworkChange () { - var addressChanged = networkAddressChanged; - if (addressChanged != null) { - addressChanged (null, EventArgs.Empty); - } + var sockaddr = sockaddr_in.Create (); + handle = SCNetworkReachabilityCreateWithAddress (IntPtr.Zero, ref sockaddr); + if (handle == IntPtr.Zero) + throw new Exception ("SCNetworkReachabilityCreateWithAddress returned NULL"); - var availabilityChanged = networkAvailabilityChanged; - if (availabilityChanged != null) { - availabilityChanged (null, new NetworkAvailabilityEventArgs (available != 0)); - } + callback = new SCNetworkReachabilityCallback (HandleCallback); + var info = new SCNetworkReachabilityContext { + info = GCHandle.ToIntPtr (GCHandle.Alloc (this)) + }; + + SCNetworkReachabilitySetCallback (handle, callback, ref info); + + scheduledWithRunLoop = + LoadRunLoopMode () && + SCNetworkReachabilityScheduleWithRunLoop (handle, CFRunLoopGetMain (), runLoopMode); + + SCNetworkReachabilityGetFlags (handle, out flags); } - void MaybeInitialize () + bool LoadRunLoopMode () { - lock (this) { - if (handle == IntPtr.Zero) { - callback = new MonoSCReachabilityCallback (Callback); - handle = mono_sc_reachability_new (callback); + var cfLibHandle = dlopen (CORE_FOUNDATION_LIB, 0); + if (cfLibHandle == IntPtr.Zero) + return false; + + try { + runLoopMode = dlsym (cfLibHandle, "kCFRunLoopDefaultMode"); + if (runLoopMode != IntPtr.Zero) { + runLoopMode = Marshal.ReadIntPtr (runLoopMode); + return runLoopMode != IntPtr.Zero; } + } finally { + dlclose (cfLibHandle); } + + return false; } - void MaybeDispose () + public void Dispose () { lock (this) { - var addressChanged = networkAddressChanged; - var availabilityChanged = networkAvailabilityChanged; - if (handle != IntPtr.Zero && addressChanged == null && availabilityChanged == null) { - mono_sc_reachability_free (handle); - handle = IntPtr.Zero; - } + if (handle == IntPtr.Zero) + return; + + if (scheduledWithRunLoop) + SCNetworkReachabilityUnscheduleFromRunLoop (handle, CFRunLoopGetMain (), runLoopMode); + + CFRelease (handle); + handle = IntPtr.Zero; + callback = null; + flags = NetworkReachabilityFlags.None; + scheduledWithRunLoop = false; } } -#if MONOTOUCH || MONODROID - const string LIBNAME = "__Internal"; -#else - const string LIBNAME = "MonoPosixHelper"; +#if MONOTOUCH + [MonoTouch.MonoPInvokeCallback (typeof (SCNetworkReachabilityCallback))] #endif + static void HandleCallback (IntPtr reachability, NetworkReachabilityFlags flags, IntPtr info) + { + if (info == IntPtr.Zero) + return; - delegate void MonoSCReachabilityCallback (int available); - - [DllImport (LIBNAME)] - static extern int mono_sc_reachability_enabled (); + var instance = GCHandle.FromIntPtr (info).Target as MacNetworkChange; + if (instance == null || instance.flags == flags) + return; - [DllImport (LIBNAME)] - static extern IntPtr mono_sc_reachability_new (MonoSCReachabilityCallback callback); + instance.flags = flags; - [DllImport (LIBNAME)] - static extern void mono_sc_reachability_free (IntPtr handle); + var addressChanged = instance.networkAddressChanged; + if (addressChanged != null) + addressChanged (null, EventArgs.Empty); - [DllImport (LIBNAME)] - static extern int mono_sc_reachability_is_available (IntPtr handle); + var availabilityChanged = instance.networkAvailabilityChanged; + if (availabilityChanged != null) + availabilityChanged (null, new NetworkAvailabilityEventArgs (instance.IsAvailable)); + } } +#if !NETWORK_CHANGE_STANDALONE + internal sealed class LinuxNetworkChange : INetworkChange { [Flags] enum EventType { @@ -185,6 +346,14 @@ namespace System.Net.NetworkInformation { remove { Unregister (value); } } + public bool HasRegisteredEvents { + get { return AddressChanged != null || AvailabilityChanged != null; } + } + + public void Dispose () + { + } + //internal Socket (AddressFamily family, SocketType type, ProtocolType proto, IntPtr sock) bool EnsureSocket () @@ -321,5 +490,7 @@ namespace System.Net.NetworkInformation { [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)] static extern IntPtr CloseNLSocket (IntPtr sock); } -} +#endif + +} |