/*
Copyright (C) 2004-2006 Nokia Corporation
Copyright (C) 2008-2011, Dirk Trossen, airs@dirk-trossen.de
Copyright (C) 2013, TecVis LP, support@tecvis.co.uk
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation as version 2.1 of the License.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.airs.handlers;
import java.util.List;
import java.util.concurrent.Semaphore;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.NetworkInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.WifiLock;
import android.os.Handler;
import android.os.Message;
import android.telephony.PhoneStateListener;
import com.airs.R;
import com.airs.helper.SerialPortLogger;
import com.airs.helper.Waker;
import com.airs.platform.HandlerManager;
import com.airs.platform.SensorRepository;
/**
* Class to read WiFi related sensors, specifically the WF, WI, WM, WS, WC sensor
* @see Handler
*/
public class WifiHandler extends PhoneStateListener implements com.airs.handlers.Handler, Runnable
{
private static final int INIT_WIFI = 1;
private Context nors;
// phone state classes
private WifiManager wm;
private WifiLock wifi_lock;
// are these there?
private boolean enable = false, enableWIFI = false, sleepWIFI = false, initialized = false;
// polltime
private int polltime = 5000;
private long oldtime = 0;
private boolean wifi_first = true, Wifi_scanning = false;
/**
* current MAC reading
*/
public StringBuffer MAC_reading;
/**
* current SSID reading
*/
public StringBuffer SSID_reading;
/**
* current RSSI reading
*/
public StringBuffer RSSI_reading;
/**
* current WLAN reading
*/
public StringBuffer WLAN_reading;
private int old_wifi_connected = -1, wifi_connected;
/**
* semaphore for nearby thread - released by GPS handler
*/
public Semaphore nearby_semaphore = new Semaphore(1);
/**
* semaphore for MAC reading - released by GPS handler
*/
public Semaphore mac_semaphore = new Semaphore(1);
/**
* semaphore for SSID reading - released by GPS handler
*/
public Semaphore ssid_semaphore = new Semaphore(1);
/**
* semaphore for RSSI reading - released by GPS handler
*/
public Semaphore rssi_semaphore = new Semaphore(1);
/**
* semaphore for WiFi combined reading - released by GPS handler
*/
public Semaphore wlan_semaphore = new Semaphore(1);
/**
* semaphore for WiFi connected reading - released by GPS handler
*/
public Semaphore connected_semaphore = new Semaphore(1);
private Thread runnable = null;
private boolean running = false, shutdown = false;
/**
* Sleep function
* @param millis
*/
private void sleep(long millis)
{
Waker.sleep(millis);
}
private void wait(Semaphore sema)
{
try
{
sema.acquire();
}
catch(Exception e)
{
}
}
/**
* Method to acquire sensor data
* Initialise WiFi scanning thread, if not done before
* Also initialise the WC sensor thread
* @param sensor String of the sensor symbol
* @param query String of the query to be fulfilled - not used here
* @see com.airs.handlers.Handler#Acquire(java.lang.String, java.lang.String)
*/
public byte[] Acquire(String sensor, String query)
{
byte[] reading = null;
// are we shutting down?
if (shutdown == true)
return null;
// has WiFi been started?
if (initialized == false)
{
// send message to handler thread to start WiFi
mHandler.sendMessage(mHandler.obtainMessage(INIT_WIFI));
// wait for starting wifi
while (initialized == false)
sleep(100);
}
// Discovery thread started for anything but WC?
if (runnable == null && sensor.compareTo("WC") != 0)
{
running = true;
runnable = new Thread(this);
runnable.start();
}
switch(sensor.charAt(1))
{
case 'M':
wait(mac_semaphore);
reading = MAC_reading.toString().getBytes();
break;
case 'I':
wait(ssid_semaphore);
reading = SSID_reading.toString().getBytes();
break;
case 'S':
wait(rssi_semaphore);
reading = RSSI_reading.toString().getBytes();
break;
case 'F':
wait(wlan_semaphore);
reading = WLAN_reading.toString().getBytes();
break;
case 'C':
wait(connected_semaphore);
// did value change since last time?
if (wifi_connected != old_wifi_connected)
{
reading = new byte[6];
reading[0] = (byte)sensor.charAt(0);
reading[1] = (byte)sensor.charAt(1);
reading[2] = (byte)((wifi_connected>>24) & 0xff);
reading[3] = (byte)((wifi_connected>>16) & 0xff);
reading[4] = (byte)((wifi_connected>>8) & 0xff);
reading[5] = (byte)(wifi_connected & 0xff);
old_wifi_connected = wifi_connected;
}
else
reading = null;
break;
default:
reading = null;
}
return reading;
}
/**
* Method to share the last value of the given sensor - here doing nothing
* @param sensor String of the sensor symbol to be shared
* @return human-readable string of the last sensor value
* @see com.airs.handlers.Handler#Share(java.lang.String)
*/
public String Share(String sensor)
{
return null;
}
/**
* Method to view historical chart of the given sensor symbol - here doing nothing
* @param sensor String of the symbol for which the history is being requested
* @see com.airs.handlers.Handler#History(java.lang.String)
*/
public void History(String sensor)
{
}
/**
* Method to discover the sensor symbols support by this handler
* As the result of the discovery, appropriate {@link com.airs.platform.Sensor} entries will be added to the {@link com.airs.platform.SensorRepository}, if WiFi available
* @see com.airs.handlers.Handler#Discover()
* @see com.airs.platform.Sensor
* @see com.airs.platform.SensorRepository
*/
public void Discover()
{
if (enable == true)
{
SensorRepository.insertSensor(new String("WF"), new String("txt"), nors.getString(R.string.WF_d), nors.getString(R.string.WF_e), new String("txt"), 0, 0, 1, false, 0, this);
SensorRepository.insertSensor(new String("WI"), new String("SSID"), nors.getString(R.string.WI_d), nors.getString(R.string.WI_e), new String("txt"), 0, 0, 1, false, 0, this);
SensorRepository.insertSensor(new String("WM"), new String("MAC"), nors.getString(R.string.WM_d), nors.getString(R.string.WM_e), new String("txt"), 0, 0, 1, false, 0, this);
SensorRepository.insertSensor(new String("WS"), new String("dBm"), nors.getString(R.string.WS_d), nors.getString(R.string.WS_e), new String("txt"), 0, 0, 1, false, 0, this);
SensorRepository.insertSensor(new String("WC"), new String("boolean"), nors.getString(R.string.WC_d), nors.getString(R.string.WC_e), new String("int"), 0, 0, 1, false, 0, this);
}
}
/**
* Constructor, allocating all necessary resources for the handler
* Here, it's reading the preference settings for the polltime and enabling WiFi
* Then, it's getting a reference to the {@link android.net.wifi.WifiManager} and finally, it's arming the semaphores
* @param nors Reference to the calling {@link android.content.Context}
*/
public WifiHandler(Context nors)
{
this.nors = nors;
polltime = HandlerManager.readRMS_i("LocationHandler::WifiPoll", 30) * 1000;
// read whether or not we need to enable Wifi
enableWIFI = HandlerManager.readRMS_b("LocationHandler::WIFION", false);
// read whether or not we need to have Wifi sleep
sleepWIFI = HandlerManager.readRMS_b("LocationHandler::WIFISleep", false);
// try getting wifi manager
try
{
// now get wifi manager
wm = (WifiManager)nors.getSystemService(Context.WIFI_SERVICE);
if (wm!=null)
enable = true;
else
enable = false;
// arm semaphores
wait(nearby_semaphore);
wait(mac_semaphore);
wait(ssid_semaphore);
wait(rssi_semaphore);
wait(wlan_semaphore);
wait(connected_semaphore);
// save current time and set so that first Acquire() will discover
oldtime = System.currentTimeMillis() - polltime;
}
catch(Exception e)
{
enable = false;
}
}
/**
* Method to release all handler resources
* Here, we release all semaphores, then shut down any acquisition threads and release the wifi lock, if held
* @see com.airs.handlers.Handler#destroyHandler()
*/
public void destroyHandler()
{
// we are shutting down!
shutdown = true;
// release semaphores to unlock any Acquire() threads
mac_semaphore.release();
ssid_semaphore.release();
rssi_semaphore.release();
wlan_semaphore.release();
connected_semaphore.release();
// signal thread to close down
if (running == true)
{
runnable.interrupt();
running = false;
}
// unregister listeners
if (initialized == true)
nors.unregisterReceiver(WifiReceiver);
// release wifi lock
try
{
if (wifi_lock != null)
wifi_lock.release();
}
catch(Exception e)
{
}
// now release the semaphore for the adaptive GPS thread
nearby_semaphore.release();
// remove all messages
mHandler.removeMessages(INIT_WIFI);
}
/**
* run WiFi discovery in separate thread
* @see java.lang.Runnable#run()
*/
public void run()
{
long now;
while(running==true)
{
now = System.currentTimeMillis();
// try to discover when it's time to do so
if (oldtime+polltime<=now)
{
oldtime = now;
// shall we scan and is previous scan over?
if (Wifi_scanning==false)
{
// is Wifi switched off?
if (wm.isWifiEnabled() == false)
{
// create empty readings for next round
MAC_reading = new StringBuffer("WM");
SSID_reading = new StringBuffer("WI");
RSSI_reading = new StringBuffer("WS");
WLAN_reading = new StringBuffer("WF");
// signal that reading is done
nearby_semaphore.release();
mac_semaphore.release();
rssi_semaphore.release();
ssid_semaphore.release();
wlan_semaphore.release();
}
else
{
// start next scan here!
if (wm.startScan() == true)
Wifi_scanning = true;
}
}
}
else
{
try
{
Thread.sleep(oldtime + polltime - now);
}
catch(Exception e)
{
// if we got interrupted, it's either for closing down (handled by while()) or for re-scanning due to disconnect
oldtime = now - polltime;
Wifi_scanning = false;
}
}
}
SerialPortLogger.debug("WifiHandler::scanning thread - terminating");
}
// We use a handler here to allow for the Acquire() function, which runs in a different thread, to issue an initialization of the WiFi
private final Handler mHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
// we are shutting down!
if (shutdown == true)
return;
switch (msg.what)
{
case INIT_WIFI:
if (wm != null)
{
// create wifi lock if not sleeping enabled
if (sleepWIFI == false)
wifi_lock = wm.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, "NORSWifiLock");
// is Wifi switched off and shall we switch it on?
if (wm.isWifiEnabled() == false && enableWIFI == true)
{
try
{
wm.setWifiEnabled(true);
}
catch(Exception e)
{
}
}
// Register Broadcast Receivers
nors.registerReceiver(WifiReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
nors.registerReceiver(WifiReceiver, new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION));
nors.registerReceiver(WifiReceiver, new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION));
// if wifi is not locked, do so to prevent it from sleeping!
if (sleepWIFI == false)
wifi_lock.acquire();
// signal to Acquire()
initialized = true;
}
break;
default:
break;
}
}
};
private final BroadcastReceiver WifiReceiver = new BroadcastReceiver()
{
@Override
public void onReceive(Context c, Intent intent)
{
int i;
ScanResult result;
// network adapter state changed?
if (intent.getAction().compareTo(WifiManager.WIFI_STATE_CHANGED_ACTION) == 0)
{
// is it disabled -> re-scan!
if (wm.isWifiEnabled() == false)
if (runnable!=null)
runnable.interrupt();
}
// connection state changed?
if (intent.getAction().compareTo(WifiManager.NETWORK_STATE_CHANGED_ACTION) == 0)
{
NetworkInfo info = (NetworkInfo)intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
// get connection state
if (info.getState().equals(NetworkInfo.State.CONNECTED))
wifi_connected = 1;
else
{
// re-scan!
if (runnable!=null)
runnable.interrupt();
wifi_connected = 0;
}
// release semaphore to unlock Acquire()
connected_semaphore.release();
}
// scan results back?
if (intent.getAction().compareTo(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) == 0)
{
List<ScanResult> results = wm.getScanResults();
// scanning is over
Wifi_scanning = false;
MAC_reading = new StringBuffer("WM");
SSID_reading = new StringBuffer("WI");
RSSI_reading = new StringBuffer("WS");
WLAN_reading = new StringBuffer("WF");
wifi_first = true;
// run through all results
for (i=0; i<results.size();i++)
{
// first device? -> then no \n at the end of it!
if (wifi_first == true)
wifi_first = false;
else
{
MAC_reading.append("\n");
SSID_reading.append("\n");
RSSI_reading.append("\n");
WLAN_reading.append("\n");
}
// get i-th result from list
result = results.get(i);
MAC_reading.append(result.BSSID);
SSID_reading.append(result.SSID);
RSSI_reading.append(String.valueOf(result.level));
WLAN_reading.append(result.BSSID + ":" + result.SSID + ":" + String.valueOf(result.level));
}
// now release the semaphores
nearby_semaphore.release();
mac_semaphore.release();
rssi_semaphore.release();
ssid_semaphore.release();
wlan_semaphore.release();
}
}
};
}