blob: 0d7ded31b64cbe2b66217ab8a269a51d8637aee6 [file] [log] [blame]
/*
* Copyright (C) 2014 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.googlecode.android_scripting.facade.wifi;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.googlecode.android_scripting.Log;
import com.googlecode.android_scripting.MainThread;
import com.googlecode.android_scripting.facade.EventFacade;
import com.googlecode.android_scripting.facade.FacadeManager;
import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
import com.googlecode.android_scripting.rpc.Rpc;
import com.googlecode.android_scripting.rpc.RpcOptional;
import com.googlecode.android_scripting.rpc.RpcParameter;
import android.app.Service;
import android.content.Context;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiScanner;
import android.net.wifi.WifiScanner.BssidInfo;
import android.net.wifi.WifiScanner.ChannelSpec;
import android.net.wifi.WifiScanner.ScanData;
import android.net.wifi.WifiScanner.ScanSettings;
import android.os.Bundle;
import android.os.SystemClock;
import android.provider.Settings.Global;
import android.provider.Settings.SettingNotFoundException;
/**
* WifiScanner functions.
*/
public class WifiScannerFacade extends RpcReceiver {
private final Service mService;
private final EventFacade mEventFacade;
private final WifiScanner mScan;
// These counters are just for indexing;
// they do not represent the total number of listeners
private static int WifiScanListenerCnt;
private static int WifiChangeListenerCnt;
private static int WifiBssidListenerCnt;
private final ConcurrentHashMap<Integer, WifiScanListener> scanListeners;
private final ConcurrentHashMap<Integer, WifiScanListener> scanBackgroundListeners;
private final ConcurrentHashMap<Integer, ChangeListener> trackChangeListeners;
private final ConcurrentHashMap<Integer, WifiBssidListener> trackBssidListeners;
private static ConcurrentHashMap<Integer, ScanResult[]> wifiScannerResultList;
private static ConcurrentHashMap<Integer, ScanData[]> wifiScannerDataList;
public WifiScannerFacade(FacadeManager manager) {
super(manager);
mService = manager.getService();
mScan = (WifiScanner) mService.getSystemService(Context.WIFI_SCANNING_SERVICE);
mEventFacade = manager.getReceiver(EventFacade.class);
scanListeners = new ConcurrentHashMap<Integer, WifiScanListener>();
scanBackgroundListeners = new ConcurrentHashMap<Integer, WifiScanListener>();
trackChangeListeners = new ConcurrentHashMap<Integer, ChangeListener>();
trackBssidListeners = new ConcurrentHashMap<Integer, WifiBssidListener>();
wifiScannerResultList = new ConcurrentHashMap<Integer, ScanResult[]>();
wifiScannerDataList = new ConcurrentHashMap<Integer, ScanData[]>();
}
public static List<ScanResult> getWifiScanResult(Integer listenerIndex) {
ScanResult[] sr = wifiScannerResultList.get(listenerIndex);
return Arrays.asList(sr);
}
private class WifiActionListener implements WifiScanner.ActionListener {
private final Bundle mResults;
public int mIndex;
protected String mEventType;
private long startScanElapsedRealTime;
public WifiActionListener(String type, int idx, Bundle resultBundle, long startScanERT) {
this.mIndex = idx;
this.mEventType = type;
this.mResults = resultBundle;
this.startScanElapsedRealTime = startScanERT;
}
@Override
public void onSuccess() {
Log.d("onSuccess " + mEventType + " " + mIndex);
mResults.putString("Type", "onSuccess");
mResults.putInt("Index", mIndex);
mResults.putLong("ScanElapsedRealtime", startScanElapsedRealTime);
mEventFacade.postEvent(mEventType + mIndex + "onSuccess", mResults.clone());
mResults.clear();
}
@Override
public void onFailure(int reason, String description) {
Log.d("onFailure " + mEventType + " " + mIndex);
mResults.putString("Type", "onFailure");
mResults.putInt("Index", mIndex);
mResults.putInt("Reason", reason);
mResults.putString("Description", description);
mEventFacade.postEvent(mEventType + mIndex + "onFailure", mResults.clone());
mResults.clear();
}
public void reportResult(ScanResult[] results, String type) {
Log.d("reportResult " + mEventType + " " + mIndex);
mResults.putInt("Index", mIndex);
mResults.putLong("ResultElapsedRealtime", SystemClock.elapsedRealtime());
mResults.putString("Type", type);
mResults.putParcelableArray("Results", results);
mEventFacade.postEvent(mEventType + mIndex + type, mResults.clone());
mResults.clear();
}
}
/**
* Constructs a wifiScanListener obj and returns it
*
* @return WifiScanListener
*/
private WifiScanListener genWifiScanListener() {
WifiScanListener mWifiScannerListener = MainThread.run(mService,
new Callable<WifiScanListener>() {
@Override
public WifiScanListener call() throws Exception {
return new WifiScanListener();
}
});
scanListeners.put(mWifiScannerListener.mIndex, mWifiScannerListener);
return mWifiScannerListener;
}
/**
* Constructs a wifiScanListener obj for background scan and returns it
*
* @return WifiScanListener
*/
private WifiScanListener genBackgroundWifiScanListener() {
WifiScanListener mWifiScannerListener = MainThread.run(mService,
new Callable<WifiScanListener>() {
@Override
public WifiScanListener call() throws Exception {
return new WifiScanListener();
}
});
scanBackgroundListeners.put(mWifiScannerListener.mIndex, mWifiScannerListener);
return mWifiScannerListener;
}
private class WifiScanListener implements WifiScanner.ScanListener {
private static final String mEventType = "WifiScannerScan";
protected final Bundle mScanResults;
protected final Bundle mScanData;
private final WifiActionListener mWAL;
public int mIndex;
public WifiScanListener() {
mScanResults = new Bundle();
mScanData = new Bundle();
WifiScanListenerCnt += 1;
mIndex = WifiScanListenerCnt;
mWAL = new WifiActionListener(mEventType, mIndex, mScanResults,
SystemClock.elapsedRealtime());
}
@Override
public void onSuccess() {
mWAL.onSuccess();
}
@Override
public void onFailure(int reason, String description) {
scanListeners.remove(mIndex);
mWAL.onFailure(reason, description);
}
@Override
public void onPeriodChanged(int periodInMs) {
Log.d("onPeriodChanged " + mEventType + " " + mIndex);
mScanResults.putString("Type", "onPeriodChanged");
mScanResults.putInt("NewPeriod", periodInMs);
mEventFacade.postEvent(mEventType + mIndex, mScanResults.clone());
mScanResults.clear();
}
@Override
public void onFullResult(ScanResult fullScanResult) {
Log.d("onFullResult WifiScanListener " + mIndex);
mWAL.reportResult(new ScanResult[] {
fullScanResult
}, "onFullResult");
}
public void onResults(ScanData[] results) {
Log.d("onResult WifiScanListener " + mIndex);
wifiScannerDataList.put(mIndex, results);
mScanData.putInt("Index", mIndex);
mScanData.putLong("ResultElapsedRealtime", SystemClock.elapsedRealtime());
mScanData.putString("Type", "onResults");
mScanData.putParcelableArray("Results", results);
mEventFacade.postEvent(mEventType + mIndex + "onResults", mScanData.clone());
mScanData.clear();
}
}
/**
* Constructs a ChangeListener obj and returns it
*
* @return ChangeListener
*/
private ChangeListener genWifiChangeListener() {
ChangeListener mWifiChangeListener = MainThread.run(mService,
new Callable<ChangeListener>() {
@Override
public ChangeListener call() throws Exception {
return new ChangeListener();
}
});
trackChangeListeners.put(mWifiChangeListener.mIndex, mWifiChangeListener);
return mWifiChangeListener;
}
private class ChangeListener implements WifiScanner.WifiChangeListener {
private static final String mEventType = "WifiScannerChange";
protected final Bundle mResults;
private final WifiActionListener mWAL;
public int mIndex;
public ChangeListener() {
mResults = new Bundle();
WifiChangeListenerCnt += 1;
mIndex = WifiChangeListenerCnt;
mWAL = new WifiActionListener(mEventType, mIndex, mResults,
SystemClock.elapsedRealtime());
}
@Override
public void onSuccess() {
mWAL.onSuccess();
}
@Override
public void onFailure(int reason, String description) {
trackChangeListeners.remove(mIndex);
mWAL.onFailure(reason, description);
}
/**
* indicates that changes were detected in wifi environment
*
* @param results indicate the access points that exhibited change
*/
@Override
public void onChanging(ScanResult[] results) { /* changes are found */
mWAL.reportResult(results, "onChanging");
}
/**
* indicates that no wifi changes are being detected for a while
*
* @param results indicate the access points that are bing monitored for change
*/
@Override
public void onQuiescence(ScanResult[] results) { /* changes settled down */
mWAL.reportResult(results, "onQuiescence");
}
}
private WifiBssidListener genWifiBssidListener() {
WifiBssidListener mWifiBssidListener = MainThread.run(mService,
new Callable<WifiBssidListener>() {
@Override
public WifiBssidListener call() throws Exception {
return new WifiBssidListener();
}
});
trackBssidListeners.put(mWifiBssidListener.mIndex, mWifiBssidListener);
return mWifiBssidListener;
}
private class WifiBssidListener implements WifiScanner.BssidListener {
private static final String mEventType = "WifiScannerBssid";
protected final Bundle mResults;
private final WifiActionListener mWAL;
public int mIndex;
public WifiBssidListener() {
mResults = new Bundle();
WifiBssidListenerCnt += 1;
mIndex = WifiBssidListenerCnt;
mWAL = new WifiActionListener(mEventType, mIndex, mResults,
SystemClock.elapsedRealtime());
}
@Override
public void onSuccess() {
mWAL.onSuccess();
}
@Override
public void onFailure(int reason, String description) {
trackBssidListeners.remove(mIndex);
mWAL.onFailure(reason, description);
}
@Override
public void onFound(ScanResult[] results) {
mWAL.reportResult(results, "onFound");
}
@Override
public void onLost(ScanResult[] results) {
mWAL.reportResult(results, "onLost");
}
}
private ScanSettings parseScanSettings(JSONObject j) throws JSONException {
if (j == null) {
return null;
}
ScanSettings result = new ScanSettings();
if (j.has("band")) {
result.band = j.optInt("band");
}
if (j.has("channels")) {
JSONArray chs = j.getJSONArray("channels");
ChannelSpec[] channels = new ChannelSpec[chs.length()];
for (int i = 0; i < channels.length; i++) {
channels[i] = new ChannelSpec(chs.getInt(i));
}
result.channels = channels;
}
if (j.has("maxScansToCache")) {
result.maxScansToCache = j.getInt("maxScansToCache");
}
/* periodInMs and reportEvents are required */
result.periodInMs = j.getInt("periodInMs");
result.reportEvents = j.getInt("reportEvents");
if (j.has("numBssidsPerScan")) {
result.numBssidsPerScan = j.getInt("numBssidsPerScan");
}
return result;
}
private BssidInfo[] parseBssidInfo(JSONArray jBssids) throws JSONException {
BssidInfo[] bssids = new BssidInfo[jBssids.length()];
for (int i = 0; i < bssids.length; i++) {
JSONObject bi = (JSONObject) jBssids.get(i);
BssidInfo bssidInfo = new BssidInfo();
bssidInfo.bssid = bi.getString("BSSID");
bssidInfo.high = bi.getInt("high");
bssidInfo.low = bi.getInt("low");
if (bi.has("frequencyHint")) {
bssidInfo.frequencyHint = bi.getInt("frequencyHint");
}
bssids[i] = bssidInfo;
}
return bssids;
}
/**
* Starts periodic WifiScanner scan
*
* @param scanSettings
* @return the id of the scan listener associated with this scan
* @throws JSONException
*/
@Rpc(description = "Starts a WifiScanner Background scan")
public Integer wifiScannerStartBackgroundScan(
@RpcParameter(name = "scanSettings") JSONObject scanSettings)
throws JSONException {
ScanSettings ss = parseScanSettings(scanSettings);
Log.d("startWifiScannerScan with " + ss.channels);
WifiScanListener listener = genBackgroundWifiScanListener();
mScan.startBackgroundScan(ss, listener);
return listener.mIndex;
}
/**
* Get currently available scan results on appropriate listeners
*
* @return true if all scan results were reported correctly
* @throws JSONException
*/
@Rpc(description = "Get currently available scan results on appropriate listeners")
public Boolean wifiScannerGetScanResults() throws JSONException {
mScan.getScanResults();
return true;
}
/**
* Stops a WifiScanner scan
*
* @param listenerIndex the id of the scan listener whose scan to stop
* @throws Exception
*/
@Rpc(description = "Stops an ongoing WifiScanner Background scan")
public void wifiScannerStopBackgroundScan(
@RpcParameter(name = "listener") Integer listenerIndex)
throws Exception {
if (!scanBackgroundListeners.containsKey(listenerIndex)) {
throw new Exception("Background scan session " + listenerIndex + " does not exist");
}
WifiScanListener listener = scanBackgroundListeners.get(listenerIndex);
Log.d("stopWifiScannerScan listener " + listener.mIndex);
mScan.stopBackgroundScan(listener);
wifiScannerResultList.remove(listenerIndex);
scanBackgroundListeners.remove(listenerIndex);
}
/**
* Starts periodic WifiScanner scan
*
* @param scanSettings
* @return the id of the scan listener associated with this scan
* @throws JSONException
*/
@Rpc(description = "Starts a WifiScanner single scan")
public Integer wifiScannerStartScan(
@RpcParameter(name = "scanSettings") JSONObject scanSettings)
throws JSONException {
ScanSettings ss = parseScanSettings(scanSettings);
Log.d("startWifiScannerScan with " + ss.channels);
WifiScanListener listener = genWifiScanListener();
mScan.startScan(ss, listener);
return listener.mIndex;
}
/**
* Stops a WifiScanner scan
*
* @param listenerIndex the id of the scan listener whose scan to stop
* @throws Exception
*/
@Rpc(description = "Stops an ongoing WifiScanner Single scan")
public void wifiScannerStopScan(@RpcParameter(name = "listener") Integer listenerIndex)
throws Exception {
if (!scanListeners.containsKey(listenerIndex)) {
throw new Exception("Single scan session " + listenerIndex + " does not exist");
}
WifiScanListener listener = scanListeners.get(listenerIndex);
Log.d("stopWifiScannerScan listener " + listener.mIndex);
mScan.stopScan(listener);
wifiScannerResultList.remove(listener.mIndex);
scanListeners.remove(listenerIndex);
}
/** RPC Methods */
@Rpc(description = "Returns the channels covered by the specified band number.")
public List<Integer> wifiScannerGetAvailableChannels(
@RpcParameter(name = "band") Integer band) {
return mScan.getAvailableChannels(band);
}
/**
* Starts tracking wifi changes
*
* @return the id of the change listener associated with this track
* @throws Exception
*/
@Rpc(description = "Starts tracking wifi changes")
public Integer wifiScannerStartTrackingChange() throws Exception {
ChangeListener listener = genWifiChangeListener();
mScan.startTrackingWifiChange(listener);
return listener.mIndex;
}
/**
* Stops tracking wifi changes
*
* @param listenerIndex the id of the change listener whose track to stop
* @throws Exception
*/
@Rpc(description = "Stops tracking wifi changes")
public void wifiScannerStopTrackingChange(
@RpcParameter(name = "listener") Integer listenerIndex) throws Exception {
if (!trackChangeListeners.containsKey(listenerIndex)) {
throw new Exception("Wifi change tracking session " + listenerIndex
+ " does not exist");
}
ChangeListener listener = trackChangeListeners.get(listenerIndex);
mScan.stopTrackingWifiChange(listener);
trackChangeListeners.remove(listenerIndex);
}
/**
* Starts tracking changes of the specified bssids.
*
* @param bssidInfos An array of json strings, each representing a BssidInfo object.
* @param apLostThreshold
* @return The index of the listener used to start the tracking.
* @throws JSONException
*/
@Rpc(description = "Starts tracking changes of the specified bssids.")
public Integer wifiScannerStartTrackingBssids(
@RpcParameter(name = "bssidInfos") JSONArray bssidInfos,
@RpcParameter(name = "apLostThreshold") Integer apLostThreshold) throws JSONException {
BssidInfo[] bssids = parseBssidInfo(bssidInfos);
WifiBssidListener listener = genWifiBssidListener();
mScan.startTrackingBssids(bssids, apLostThreshold, listener);
return listener.mIndex;
}
/**
* Stops tracking the list of APs associated with the input listener
*
* @param listenerIndex the id of the bssid listener whose track to stop
* @throws Exception
*/
@Rpc(description = "Stops tracking changes in the APs on the list")
public void wifiScannerStopTrackingBssids(
@RpcParameter(name = "listener") Integer listenerIndex) throws Exception {
if (!trackBssidListeners.containsKey(listenerIndex)) {
throw new Exception("Bssid tracking session " + listenerIndex + " does not exist");
}
WifiBssidListener listener = trackBssidListeners.get(listenerIndex);
mScan.stopTrackingBssids(listener);
trackBssidListeners.remove(listenerIndex);
}
@Rpc(description = "Toggle the 'WiFi scan always available' option. If an input is given, the "
+ "option is set to what the input boolean indicates.")
public void wifiScannerToggleAlwaysAvailable(
@RpcParameter(name = "alwaysAvailable") @RpcOptional Boolean alwaysAvailable)
throws SettingNotFoundException {
int new_state = 0;
if (alwaysAvailable == null) {
int current_state = Global.getInt(mService.getContentResolver(),
Global.WIFI_SCAN_ALWAYS_AVAILABLE);
new_state = current_state ^ 0x1;
} else {
new_state = alwaysAvailable ? 1 : 0;
}
Global.putInt(mService.getContentResolver(), Global.WIFI_SCAN_ALWAYS_AVAILABLE, new_state);
}
@Rpc(description = "Returns true if WiFi scan is always available, false otherwise.")
public Boolean wifiScannerIsAlwaysAvailable() throws SettingNotFoundException {
int current_state = Global.getInt(mService.getContentResolver(),
Global.WIFI_SCAN_ALWAYS_AVAILABLE);
if (current_state == 1) {
return true;
}
return false;
}
@Rpc(description = "Returns a list of mIndexes of existing listeners")
public Set<Integer> wifiGetCurrentScanIndexes() {
return scanListeners.keySet();
}
/**
* Starts tracking wifi changes
*
* @return the id of the change listener associated with this track
* @throws Exception
*/
@Rpc(description = "Starts tracking wifi changes with track settings")
public Integer wifiScannerStartTrackingChangeWithSetting(
@RpcParameter(name = "trackSettings") JSONArray bssidSettings,
@RpcParameter(name = "rssiSS") Integer rssiSS,
@RpcParameter(name = "lostApSS") Integer lostApSS,
@RpcParameter(name = "unchangedSS") Integer unchangedSS,
@RpcParameter(name = "minApsBreachingThreshold") Integer minApsBreachingThreshold,
@RpcParameter(name = "periodInMs") Integer periodInMs) throws Exception {
Log.d("starting change track with track settings");
BssidInfo[] bssids = parseBssidInfo(bssidSettings);
mScan.configureWifiChange(rssiSS, lostApSS, unchangedSS, minApsBreachingThreshold,
periodInMs, bssids);
ChangeListener listener = genWifiChangeListener();
mScan.startTrackingWifiChange(listener);
return listener.mIndex;
}
/**
* Shuts down all activities associated with WifiScanner
*/
@Rpc(description = "Shuts down all WifiScanner activities and remove listeners.")
public void wifiScannerShutdown() {
this.shutdown();
}
/**
* Stops all activity
*/
@Override
public void shutdown() {
try {
if (!scanListeners.isEmpty()) {
Iterator<ConcurrentHashMap.Entry<Integer, WifiScanListener>> iter = scanListeners
.entrySet().iterator();
while (iter.hasNext()) {
ConcurrentHashMap.Entry<Integer, WifiScanListener> entry = iter.next();
this.wifiScannerStopScan(entry.getKey());
}
}
if (!scanBackgroundListeners.isEmpty()) {
Iterator<ConcurrentHashMap.Entry<Integer, WifiScanListener>> iter = scanBackgroundListeners
.entrySet().iterator();
while (iter.hasNext()) {
ConcurrentHashMap.Entry<Integer, WifiScanListener> entry = iter.next();
this.wifiScannerStopBackgroundScan(entry.getKey());
}
}
if (!trackChangeListeners.isEmpty()) {
Iterator<ConcurrentHashMap.Entry<Integer, ChangeListener>> iter = trackChangeListeners
.entrySet().iterator();
while (iter.hasNext()) {
ConcurrentHashMap.Entry<Integer, ChangeListener> entry = iter.next();
this.wifiScannerStopTrackingChange(entry.getKey());
}
}
if (!trackBssidListeners.isEmpty()) {
Iterator<ConcurrentHashMap.Entry<Integer, WifiBssidListener>> iter = trackBssidListeners
.entrySet().iterator();
while (iter.hasNext()) {
ConcurrentHashMap.Entry<Integer, WifiBssidListener> entry = iter.next();
this.wifiScannerStopTrackingBssids(entry.getKey());
}
}
} catch (Exception e) {
Log.e("Shutdown failed: " + e.toString());
}
}
}