Thursday, May 10, 2012

Deep look at Android Networking. Part 2. Wi-fi.

Almost every android device has wi-fi module to exchange data wirelessly and provide quick and cheap internet. This technology is very popular nowadays so I think every internet user is superficially acquainted with it. We just need to remember, that all low-level wi-fi stuff is encapsulated to be accessed through supplicant. In Android SDK Wi-Fi API provides a facet' for applications to communicate with the lower-level wireless stack. With Wi-Fi API your application can get information about available networks (plus connected network speed), add wlan network description to the set of configured networks, terminate and initiate wi-fi connections and some other stuff.
Let's start from simple example, which actually not used wi-fi api: define, is wi-fi network connected:
     public static boolean isWifiAvailable() {  
         ConnectivityManager mgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);  
         if(mgr == null) {  
             return false;  
         }  
         NetworkInfo wifi = mgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);  
         return wifi != null && wifi.isConnected();  
   }  
As you can see, we are using ConnectivityManager to get information about wi-fi network. Quite often (when your application downloads a lot of data from internet) it may be essential to perform network operations (such as downloading content) only through wlan networks. To do that we can simply use utility function to check is wlan network available:
     public static boolean canDownload() {  
         return !mIsWifiRequired || isWifiAvailable();  
     }  
As for wi-fi api - key object here is WifiManager. It is well described in the documentation, so let's not pay much attention to it and focus on real problems.
Android broadcasts a lot of information about changes of network state which may be interesting for our application. WifiManager has a few constants which represent intent actions related to network state events. For example, if we will 'subscribe' on WIFI_STATE_CHANGED_ACTION we will receive notifications when wi-fi has been enabled, disabled, enabling, disabling or unknown. Information about previous wi-fi state will be received too:
     NetworkStateBroadcastReciver networkStateBroadcastReciver = new NetworkStateBroadcastReciver();  
     IntentFilter networkStateFilter = new IntentFilter();  
     networkStateFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);  
     context.registerReceiver(networkStateBroadcastReciver, networkStateFilter);  
     

     class NetworkStateBroadcastReciver extends BroadcastReceiver {  
         public void onReceive(Context c, Intent intent) {  
             int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN);    
         
             int wifiPrevState = intent.getIntExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN);    
             ...  
         }
     }  
You can get much more notifications when 'subscribing' on SUPPLICANT_STATE_CHANGED_ACTION - start scanning for wi-fi networks, start authentication on wlan access point, etc. (for more information take a look on SupplicantState).
'Subscribing' on SCAN_RESULTS_AVAILABLE_ACTION you will get list of available (found by WifiManager.html#startScan method) wlan access points.
Another possible feature, which, I think, is rarely used - manually adding of access points (i.e. not using result of scanning). But those of you who needs to use this option will be disappointed due to serious limitation of this operation - you can only 'configure' access points WEP and WPA-PSK encryption scheme (and points w.o. encryption too), which is a little bit unpleasantly, since from Setting it's possible to add wi-fi networks with EAP authentication (such as EAP-TLS).
Here is example how to configure Open, WEP and WPA-PSK points:
   WifiConfiguration wifiConfiguration = new WifiConfiguration();      
   wifiConfiguration.SSID = ssid;  
   wifiConfiguration.status = WifiConfiguration.Status.ENABLED;  
   wifiConfiguration.priority = 1;  
   
   ...  

   int addOpenNetwork(WifiConfiguration configuration) {  
     configureOpenNetwork(configuration);  
     return wifi.addNetwork(configuration);  
   }  
   
   private void configureOpenNetwork(WifiConfiguration configuration) {  
     configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);  
     configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);  
     configuration.allowedProtocols.set(WifiConfiguration.Protocol.WPA);  
     configuration.allowedAuthAlgorithms.clear();  
     configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);  
     configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);  
     configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);  
     configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);  
     configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);  
     configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);  
   }  
   
   int addWEPNetwork(WifiConfiguration configuration, String password) {  
     configureWEPNetwork(configuration, password);  
     return wifi.addNetwork(configuration);  
   }  
   
   private void configureWEPNetwork(WifiConfiguration configuration, String password) {  
     configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);  
     configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);  
     configuration.allowedProtocols.set(WifiConfiguration.Protocol.WPA);  
     configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);  
     configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);  
     configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);  
     configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);  
     configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);  
     configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);  
     configuration.wepKeys[0] = "\"".concat(password).concat("\"");  
     configuration.wepTxKeyIndex = 0;  
   }  
   
   int addWPANetwork(WifiConfiguration configuration, String password) {  
     configureWPANetwork(configuration, password);  
     return wifi.addNetwork(configuration);  
   }  
   
   private void configureWPANetwork(WifiConfiguration configuration, String password) {  
     configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);  
     configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);  
     configuration.allowedProtocols.set(WifiConfiguration.Protocol.WPA);  
     configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);  
     configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);  
     configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);  
     configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);  
     configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);  
     configuration.preSharedKey = "\"".concat(password).concat("\"");  
   }  
After wi-fi networks were configured networks you need to call WifiManager#saveConfiguration. Sometimes this method will return false and in logs (logcat) you will see something like 'wpa supplicant is busy or not responding'. Don't worry, just call this method again after some timeout (you can use WifiManager#pingSupplicant to check supplicant availability). The last step in manual 'wi-fi configuration' is WifiManager#enableNetwork method, which you need to provide with identifier of network you want connect too.

2 comments:

  1. Hi Vlad!

    COuld you help me?
    I'm having some difficulties to make two android devices
    communicating each other from broadcasting their name (existance).
    and connecting one to another.

    ReplyDelete
  2. wifimanager.addnetwork(wificonfig) returns always ≥0 even we enter wrong password or right password . please help me...

    ReplyDelete