using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using Microsoft.Maui.Networking; namespace Microsoft.Maui.Platform.Linux.Services; public class ConnectivityService : IConnectivity, IDisposable { private static readonly Lazy _instance = new Lazy(() => new ConnectivityService()); private NetworkAccess _networkAccess; private IEnumerable _connectionProfiles; private bool _disposed; public static ConnectivityService Instance => _instance.Value; public NetworkAccess NetworkAccess { get { RefreshConnectivity(); return _networkAccess; } } public IEnumerable ConnectionProfiles { get { RefreshConnectivity(); return _connectionProfiles; } } public event EventHandler? ConnectivityChanged; public ConnectivityService() { _connectionProfiles = new List(); RefreshConnectivity(); NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged; NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged; } private void RefreshConnectivity() { try { IEnumerable activeInterfaces = from ni in NetworkInterface.GetAllNetworkInterfaces() where ni.OperationalStatus == OperationalStatus.Up && ni.NetworkInterfaceType != NetworkInterfaceType.Loopback select ni; if (!activeInterfaces.Any()) { _networkAccess = NetworkAccess.None; _connectionProfiles = Enumerable.Empty(); return; } List profiles = new List(); foreach (var networkInterface in activeInterfaces) { switch (networkInterface.NetworkInterfaceType) { case NetworkInterfaceType.Ethernet: case NetworkInterfaceType.FastEthernetT: case NetworkInterfaceType.FastEthernetFx: case NetworkInterfaceType.GigabitEthernet: profiles.Add(ConnectionProfile.Ethernet); break; case NetworkInterfaceType.Wireless80211: profiles.Add(ConnectionProfile.WiFi); break; case NetworkInterfaceType.Ppp: case NetworkInterfaceType.Slip: profiles.Add(ConnectionProfile.Cellular); break; default: profiles.Add(ConnectionProfile.Unknown); break; } } _connectionProfiles = profiles.Distinct().ToList(); if (CheckInternetAccess()) { _networkAccess = NetworkAccess.Internet; } else if (_connectionProfiles.Any()) { _networkAccess = NetworkAccess.Local; } else { _networkAccess = NetworkAccess.None; } } catch { _networkAccess = NetworkAccess.Unknown; _connectionProfiles = new ConnectionProfile[] { ConnectionProfile.Unknown }; } } private bool CheckInternetAccess() { try { return Dns.GetHostEntry("dns.google").AddressList.Length != 0; } catch { try { foreach (NetworkInterface item in from n in NetworkInterface.GetAllNetworkInterfaces() where n.OperationalStatus == OperationalStatus.Up select n) { if (item.GetIPProperties().GatewayAddresses.Any((GatewayIPAddressInformation g) => g.Address.AddressFamily == AddressFamily.InterNetwork)) { return true; } } } catch { } return false; } } private void OnNetworkAvailabilityChanged(object? sender, NetworkAvailabilityEventArgs e) { NetworkAccess previousAccess = _networkAccess; List previousProfiles = _connectionProfiles.ToList(); RefreshConnectivity(); if (previousAccess != _networkAccess || !previousProfiles.SequenceEqual(_connectionProfiles)) { ConnectivityChanged?.Invoke(this, new ConnectivityChangedEventArgs(_networkAccess, _connectionProfiles)); } } private void OnNetworkAddressChanged(object? sender, EventArgs e) { NetworkAccess previousAccess = _networkAccess; List previousProfiles = _connectionProfiles.ToList(); RefreshConnectivity(); if (previousAccess != _networkAccess || !previousProfiles.SequenceEqual(_connectionProfiles)) { ConnectivityChanged?.Invoke(this, new ConnectivityChangedEventArgs(_networkAccess, _connectionProfiles)); } } public void Dispose() { if (!_disposed) { _disposed = true; NetworkChange.NetworkAvailabilityChanged -= OnNetworkAvailabilityChanged; NetworkChange.NetworkAddressChanged -= OnNetworkAddressChanged; } } }