Better way to detect WiFi enabled/disabled on iOS?

2019-07-23 06:59发布

问题:

After an unbelievable amount of teeth gnashing, I finally have a method that successfully detects if WiFi is enabled on iOS independent of whether it's connected or not. There are at least a couple uses for such a thing, and I don't think this violates the spirit or the letter of Apple Law(tm).

However, it's ugly and probably won't work forever. It currently works on iOS 10.2.1, as of Jan 31, 2017. I'll put my answer below and hope that someone can improve it. I've heavily researched Reachability (doesn't meet the requirements), CaptiveNetwork, HotspotHelper, SCNetworkConfiguration, Xamarin's System.Net.NetworkInterface, and more. This is it that actually works as far as I can tell.

回答1:

The gist of the solution is that when there's TWO interfaces reported by getifaddrs() with the name "awdl0" then WiFi is enabled. Just one and it's disabled.

I credit pebble8888 for pointing me to https://github.com/alirp88/SMTWiFiStatus which is Objective-C, where the lack of comments make it hard to understand what's going on or what the author's intention was.

Here's my full and complete Xamarin/C# solution, which ought to be pretty readable for any other major language user:

using System;
using System.Runtime.InteropServices;

namespace Beacon.iOS
{
/// <summary>
/// code stolen from the Xamarin source code to work around goofy interactions between
/// the good-god-why-would-it-work-that-way iOS and the entirely reasonable Xamarin
/// (it doesn't report interfaces that are reported multiple times)
/// </summary>
class XamHack
{
    //
    // Types
    //
    internal struct ifaddrs
    {
#pragma warning disable 0649
        public IntPtr ifa_next;
        public string ifa_name;
        public uint ifa_flags;
        public IntPtr ifa_addr;
        public IntPtr ifa_netmask;
        public IntPtr ifa_dstaddr;
        public IntPtr ifa_data;
#pragma warning restore
    }

    //
    // OS methods
    //
    [DllImport("libc")]
    protected static extern int getifaddrs(out IntPtr ifap);

    [DllImport("libc")]
    protected static extern void freeifaddrs(IntPtr ifap);


    //
    // Methods
    //

    /// <summary>
    /// Our glorious hack.  I apologize to the programming gods for my sins
    /// but this works (for now) and functionality trumps elegance.  Even this.
    /// Reverse engineered from: https://github.com/alirp88/SMTWiFiStatus
    /// </summary>
    public static bool IsWifiEnabled()
    {
        int count = 0;
        IntPtr ifap;

        // get the OS to put info about all the NICs into a linked list of buffers
        if (getifaddrs(out ifap) != 0)
            throw new SystemException("getifaddrs() failed");

        try
        {
            // iterate throug those buffers
            IntPtr next = ifap;
            while (next != IntPtr.Zero)
            {
                // marshall the data into our struct
                ifaddrs addr = (ifaddrs)Marshal.PtrToStructure(next, typeof(ifaddrs));

                // count the instances of the sacred interface name
                if ("awdl0" == addr.ifa_name)
                    count++;

                // move on to the next interface
                next = addr.ifa_next;
            }
        }
        finally
        {
            // leaking memory is for jerks
            freeifaddrs(ifap);
        }

        // if there's two of the sacred interface, that means WiFi is enabled.  Seriously.
        return (2 == count);
    }

} // class
} // namespace