/**
*
* Funf: Open Sensing Framework
* Copyright (C) 2010-2011 Nadav Aharony, Wei Pan, Alex Pentland.
* Acknowledgments: Alan Gardner
* Contact: nadav@media.mit.edu
*
* This file is part of Funf.
*
* Funf 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, either version 3 of
* the License, or (at your option) any later version.
*
* Funf 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 Funf. If not, see <http://www.gnu.org/licenses/>.
*
*/
package edu.mit.media.funf.probe.builtin;
import java.util.List;
import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.WifiLock;
import android.util.Log;
import com.google.gson.Gson;
import edu.mit.media.funf.Schedule;
import edu.mit.media.funf.Schedule.DefaultSchedule;
import edu.mit.media.funf.probe.Probe.Base;
import edu.mit.media.funf.probe.Probe.DisplayName;
import edu.mit.media.funf.probe.Probe.RequiredFeatures;
import edu.mit.media.funf.probe.Probe.RequiredPermissions;
import edu.mit.media.funf.util.LogUtil;
@Schedule.DefaultSchedule(interval=300)
@RequiredPermissions({Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.CHANGE_WIFI_STATE})
@RequiredFeatures("android.hardware.wifi")
@DisplayName("Nearby Wifi Devices Probe")
public class WifiProbe extends Base {
private static final String LOCK_KEY = WifiProbe.class.getName();
private WifiManager wifiManager;
private int numberOfAttempts;
private int previousWifiState; // TODO: should this be persisted to disk?
private WifiLock wifiLock;
private BroadcastReceiver scanResultsReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(intent.getAction())) {
List<ScanResult> results = wifiManager.getScanResults();
if (results != null) {
Gson gson = getGson();
for (ScanResult result : results) {
sendData(gson.toJsonTree(result).getAsJsonObject());
}
}
if (getState() == State.RUNNING) {
stop();
}
}
}
};
private BroadcastReceiver waitingToStartScanReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context ctx, Intent i) {
if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(i.getAction())) {
try {
getContext().unregisterReceiver(this); // TODO: sometimes this throws an IllegalArgumentException
saveWifiStateAndRunScan();
} catch (IllegalArgumentException e) {
Log.e(LogUtil.TAG, "Unregistered WIFIE_STATE_CHANGED receiver more than once.");
}
}
}
};
private BroadcastReceiver retryScanReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context ctx, Intent i) {
if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(i.getAction())) {
try {
getContext().unregisterReceiver(this);
runScan();
} catch (IllegalArgumentException e) {
// Not sure why, but sometimes this is not registered
// Probably two intents at once
}
}
}
};
@Override
protected void onEnable() {
super.onEnable();
wifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
numberOfAttempts = 0;
getContext().registerReceiver(scanResultsReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
}
@Override
protected void onStart() {
super.onStart();
acquireWifiLock();
saveWifiStateAndRunScan();
}
@Override
protected void onStop() {
super.onStop();
releaseWifiLock();
loadPreviousWifiState();
}
@Override
protected void onDisable() {
super.onDisable();
getContext().unregisterReceiver(scanResultsReceiver);
}
private void loadPreviousWifiState() {
// Enable wifi if previous sate was enabled, otherwise disable
wifiManager.setWifiEnabled(previousWifiState == WifiManager.WIFI_STATE_ENABLED);
}
private void saveWifiStateAndRunScan() {
int state = wifiManager.getWifiState();
if(state==WifiManager.WIFI_STATE_DISABLING ||state==WifiManager.WIFI_STATE_ENABLING){
// Wait until the Wifi state stabilizes, then run
getContext().registerReceiver(waitingToStartScanReceiver, new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION));
} else {
previousWifiState = state;
runScan();
}
}
private void acquireWifiLock() {
wifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, LOCK_KEY);
wifiLock.setReferenceCounted(false);
wifiLock.acquire();
}
private void releaseWifiLock() {
if (wifiLock != null) {
if (wifiLock.isHeld()) {
wifiLock.release();
}
wifiLock = null;
}
}
private void runScan() {
numberOfAttempts += 1;
int state = wifiManager.getWifiState();
if (state == WifiManager.WIFI_STATE_ENABLED) {
boolean successfulStart = wifiManager.startScan();
if (successfulStart) {
Log.i(LogUtil.TAG, "WIFI scan started succesfully");
} else {
Log.e(LogUtil.TAG, "WIFI scan failed.");
}
numberOfAttempts = 0;
} else if (numberOfAttempts <= 3) {
// Prevent infinite recursion by keeping track of number of attempts to change wifi state
getContext().registerReceiver(retryScanReceiver, new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION));
wifiManager.setWifiEnabled(true);
} else { // After 3 attempts stop trying
// TODO: possibly send error
stop();
}
}
}