Mobile (3G) vs. WIFI Network Detection with Adobe AIR

Did you know that you can determine whether your mobile device is on wifi or your mobile data connection when using Adobe AIR for mobile? To be honest, I wasn’t aware of it either until yesterday when my friend and former colleague Brian O’Connor pointed me towards a recent tweet from @adobe_cookbook that showed an example how to do it.

These networking APIs have been around since AIR 2.0, but I’ve seldom had the need to dig into them for the desktop. Now that AIR for mobile is widely available, this can be critically important for your applications. For example, what if you want to minimize network usage while on the mobile network? This may even prevent you being chastised for eating up expensive mobile bandwidth.

Using the NetworkInfo class’ findInterfaces() method, you can retrieve a list of all network interfaces on your computer/device. If you iterate through these, you can see which are active, what their IP and MAC addresses are, and even which IP protocol version they are using. Here’s a quick example (code below the video):

Network Detection on AIR Mobile

I know that video is a little hard to see, so here are some screen captures. First, a capture showing the mobile network connection active:

Mobile Network Active

Next, a capture showing the WIFI network active:

WIFI Network Active

Basically, I just have a list that shows all of the network interfaces. When the app loads, or whenever the network connection changes, the content of that list is updated to reflect the current state of the network interfaces. If you want to determine whether you are on wifi, you can compare the name of the active network interface to see if it contains the string “wifi”, as shown in the Adobe Cookbook.

<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009" 
		xmlns:s="library://ns.adobe.com/flex/spark" 
		title="Network Detection"
		creationComplete="view1_creationCompleteHandler(event)">
	
	<fx:Script>
		<![CDATA[
			import mx.collections.ArrayCollection;
			import mx.events.FlexEvent;
			
			[Bindable]
			private var interfaces : ArrayCollection = new ArrayCollection();
			
			protected function view1_creationCompleteHandler(event:FlexEvent):void
			{
				NativeApplication.nativeApplication.addEventListener(Event.NETWORK_CHANGE, onNetworkChange);
				updateInterfaceDataProvider();
			}
			
			protected function onNetworkChange(event : Event) : void
			{
				updateInterfaceDataProvider();
			}
			
			//this is used b/c you can't bind directly to a Vector
			protected function updateInterfaceDataProvider() : void
			{
				interfaces.disableAutoUpdate();
				interfaces.removeAll();
				
				for each ( var ni : NetworkInterface in NetworkInfo.networkInfo.findInterfaces() )
				{
					interfaces.addItem( ni );
				}
				interfaces.enableAutoUpdate();
			}
		]]>
	</fx:Script>
	
	<s:List dataProvider="{ interfaces }" 
			width="100%" height="100%"
			itemRenderer="NetworkInterfaceDetailsRenderer"/>
</s:View>

In the list renderer, I’m simply setting an iconFunction and messageFunction to reflect the details on each individual NetworkInterface.

<?xml version="1.0" encoding="utf-8"?>
<s:IconItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009" 
					xmlns:s="library://ns.adobe.com/flex/spark"  
					labelField="name" 
					messageFunction="networkInterfaceDetail" 
					iconFunction="networkInterfaceIcon"
					iconWidth="64" iconHeight="64" >
	
	<fx:Script>
		<![CDATA[
			
			[Embed(source="assets/active.png")]
			public static var activeImageClass:Class;
			
			[Embed(source="assets/inactive.png")]
			public static var inactiveImageClass:Class;
			
			private function networkInterfaceDetail(item:Object):String
			{
				var ni : NetworkInterface = this.data as NetworkInterface;
				if ( ni )
				{
					var result : String = "mac: " + ni.hardwareAddress;
					for each ( var ia : InterfaceAddress in ni.addresses )
					{
						result += "\nip: " + ia.address + " " + ia.ipVersion;
					}
					result += "\nmtu: " + ni.mtu.toString();
					return result;
				}
				
				return "Error: Unable to identify network interface.";
			}
			
			private function networkInterfaceIcon(item:Object):Object
			{
				var ni : NetworkInterface = this.data as NetworkInterface;
				if ( ni && ni.active )
					return activeImageClass;
				return inactiveImageClass;
			}
		]]>
	</fx:Script>
</s:IconItemRenderer>

One thing not to forget: You must make sure that your application has been provisioned to allow access to network interfaces! You’ll just need to uncomment the Android permissions in your app.xml for network state:

			    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
			    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
  • http://www.tricedesigns.com Andrew Trice

    Just a forewarning… this does not work on iOS. The NetworkInfo.networkInfo.findInterfaces() method is not implemented. For now, you can only do this on Android.

  • http://www.innovatology.nl Jon

    Works on Blackberry PlayBook too!

    • http://www.tricedesigns.com Andrew

      Great! I haven’t tested it on the playbook yet. However, I found out the hard way that it definitely does not work on iOS. Just be sure to use NetworkInfo.isSupported to determine if it will work on your target platform.

  • Mike Lyons

    I would love to find a way to do this from the browser without AIR but it seems impossible at the moment.

    • http://www.tricedesigns.com Andrew

      You are correct, the NetworkInterface classes are AIR-specific and will not be supported in the browser. I am not aware of any plan to bring these features to the Flash Player.

  • Thijs

    Any ETA on when this is available in AIR for iOS?

  • Frank Baumi

    The code above does NOT work on iOS. Try this code for iOS :

    var vNetworkInterfaces:Object;
    if (flash.net.NetworkInfo.isSupported)
    {
    vNetworkInterfaces = getDefinitionByName(‘flash.net.NetworkInfo’)[‘networkInfo’][‘findInterfaces’]();
    mytrace(“fall 1″ );
    }
    else
    {
    vNetworkInterfaces = getDefinitionByName(‘com.adobe.nativeExtensions.Networkinfo.NetworkInfo’)[‘networkInfo’][‘findInterfaces’]();
    mytrace(“fall 2″ );
    }

    var hasWifi: Boolean = false;
    var hasMobile: Boolean = false;

    for each (var networkInterface:Object in vNetworkInterfaces)
    {
    if ( networkInterface.active && (networkInterface.name == “en0″ || networkInterface.name == “en1″) ) hasWifi = true;
    if ( networkInterface.active && (networkInterface.name == “pdp_ip0″ || networkInterface.name == “pdp_ip1″ || networkInterface.name == “pdp_ip2″) ) hasMobile = true;

    mytrace( “active: ” + networkInterface.active );
    mytrace( “displayName: ” + networkInterface.displayName );
    mytrace( “name: ” + networkInterface.name );
    mytrace( “hwAddress: ” + networkInterface.hardwareAddress );
    mytrace( “——————–” );
    }

    mytrace( “has Mobile Internet: ” + hasMobile );
    mytrace( “has Wifi Internet: ” + hasWifi );

    • http://www.tricedesigns.com Andrew

      As I mentioned above: “Just a forewarning… this does not work on iOS. The NetworkInfo.networkInfo.findInterfaces() method is not implemented. ” You could write a native extension to access this information on iOS, however it is not exposed via the NetworkInfo class on iOS.

  • Frank

    Andrew thats good news, thanks!

    However, the code i have posted above does work on iOS!! – It’s true that you simply cannot instanciate with “new NetworkInfo();”. But by using the getDefinitionByName method, you can call the findInterfaces() method and it is definately working, i’m using this code in my project for an iOS project …

    • http://www.tricedesigns.com Andrew

      Oh, OK. Thanks for letting me know! I must have misread, and didn’t realize that you actually had it working. I’d be careful that it could stop working if there are any changes to the flash player or AIR.

  • devilonbike

    Is there any way to determine available wifi and to connect to them using as3