summaryrefslogtreecommitdiffstats
path: root/wifi/java/android/net/wifi/WifiScanner.java
diff options
context:
space:
mode:
authorVinit Deshapnde <vinitd@google.com>2014-05-07 21:09:11 -0700
committerVinit Deshapnde <vinitd@google.com>2014-05-07 21:14:24 -0700
commit011e1b35a64180d6f0234af8a3c2b70777eb9f39 (patch)
tree79c54cad4f30c493f587e4ac0fb1e89bbaecc714 /wifi/java/android/net/wifi/WifiScanner.java
parentc4bfcc12e92fe3d8f6f1dad0c12176d6dae3131d (diff)
downloadframeworks_base-011e1b35a64180d6f0234af8a3c2b70777eb9f39.zip
frameworks_base-011e1b35a64180d6f0234af8a3c2b70777eb9f39.tar.gz
frameworks_base-011e1b35a64180d6f0234af8a3c2b70777eb9f39.tar.bz2
Initial implementation of WifiScanner
This change implements basic functionality of WifiScanner. Following functionality is enabled 1. Scanning - specify a list of channels to scan 2. Significant change detection 3. AP hotlist Change-Id: Ieef75b96bdbbd3c7d9b9e698bd16e64d3b465254
Diffstat (limited to 'wifi/java/android/net/wifi/WifiScanner.java')
-rw-r--r--wifi/java/android/net/wifi/WifiScanner.java584
1 files changed, 584 insertions, 0 deletions
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
new file mode 100644
index 0000000..e02e14c
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -0,0 +1,584 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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 android.net.wifi;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+
+/**
+ * This class provides a way to scan the Wifi universe around the device
+ * Get an instance of this class by calling
+ * {@link android.content.Context#getSystemService(String) Context.getSystemService(Context
+ * .WIFI_SCANNING_SERVICE)}.
+ * @hide
+ */
+public class WifiScanner {
+
+ public static final int WIFI_BAND_UNSPECIFIED = 0; /* not specified */
+ public static final int WIFI_BAND_24_GHZ = 1; /* 2.4 GHz band */
+ public static final int WIFI_BAND_5_GHZ = 2; /* 5 GHz band without DFS channels */
+ public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 4; /* 5 GHz band with DFS channels */
+ public static final int WIFI_BAND_5_GHZ_WITH_DFS = 6; /* 5 GHz band with DFS channels */
+ public static final int WIFI_BAND_BOTH = 3; /* both bands without DFS channels */
+ public static final int WIFI_BAND_BOTH_WITH_DFS = 7; /* both bands with DFS channels */
+
+ public static final int MIN_SCAN_PERIOD_MS = 300; /* minimum supported period */
+ public static final int MAX_SCAN_PERIOD_MS = 1024000; /* maximum supported period */
+
+ public static final int REASON_SUCCEEDED = 0;
+ public static final int REASON_UNSPECIFIED = -1;
+ public static final int REASON_INVALID_LISTENER = -2;
+ public static final int REASON_INVALID_REQUEST = -3;
+ public static final int REASON_CONFLICTING_REQUEST = -4;
+
+ public static interface ActionListener {
+ public void onSuccess(Object result);
+ public void onFailure(int reason, Object exception);
+ }
+
+ /**
+ * gives you all the possible channels; channel is specified as an
+ * integer with frequency in MHz i.e. channel 1 is 2412
+ */
+ public List<Integer> getAvailableChannels(int band) {
+ return null;
+ }
+
+ /**
+ * provides channel specification to the APIs
+ */
+ public static class ChannelSpec {
+ public int frequency;
+ public boolean passive; /* ignored on DFS channels */
+ public int dwellTimeMS; /* not supported for now */
+
+ public ChannelSpec(int frequency) {
+ this.frequency = frequency;
+ passive = false;
+ dwellTimeMS = 0;
+ }
+ }
+
+ public static final int REPORT_EVENT_AFTER_BUFFER_FULL = 0;
+ public static final int REPORT_EVENT_AFTER_EACH_SCAN = 1;
+ public static final int REPORT_EVENT_FULL_SCAN_RESULT = 2;
+
+ /**
+ * scan configuration parameters
+ */
+ public static class ScanSettings implements Parcelable {
+
+ public int band; /* ignore channels if specified */
+ public ChannelSpec[] channels; /* list of channels to scan */
+ public int periodInMs; /* period of scan */
+ public int reportEvents; /* a valid REPORT_EVENT value */
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(band);
+ dest.writeInt(periodInMs);
+ dest.writeInt(channels.length);
+
+ for (int i = 0; i < channels.length; i++) {
+ dest.writeInt(channels[i].frequency);
+ dest.writeInt(channels[i].dwellTimeMS);
+ dest.writeInt(channels[i].passive ? 1 : 0);
+ }
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<ScanSettings> CREATOR =
+ new Creator<ScanSettings>() {
+ public ScanSettings createFromParcel(Parcel in) {
+
+ ScanSettings settings = new ScanSettings();
+ settings.band = in.readInt();
+ settings.periodInMs = in.readInt();
+ int num_channels = in.readInt();
+ settings.channels = new ChannelSpec[num_channels];
+ for (int i = 0; i < num_channels; i++) {
+ int frequency = in.readInt();
+
+ ChannelSpec spec = new ChannelSpec(frequency);
+ spec.dwellTimeMS = in.readInt();
+ spec.passive = in.readInt() == 1;
+ settings.channels[i] = spec;
+ }
+
+ return settings;
+ }
+
+ public ScanSettings[] newArray(int size) {
+ return new ScanSettings[size];
+ }
+ };
+
+ }
+
+ public static class InformationElement {
+ public int id;
+ public byte[] bytes;
+ }
+
+ public static class FullScanResult {
+ public ScanResult result;
+ public InformationElement informationElements[];
+ }
+
+ /** @hide */
+ public static class ParcelableScanResults implements Parcelable {
+ public ScanResult mResults[];
+
+ public ParcelableScanResults(ScanResult[] results) {
+ mResults = results;
+ }
+
+ public ScanResult[] getResults() {
+ return mResults;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mResults.length);
+ for (int i = 0; i < mResults.length; i++) {
+ ScanResult result = mResults[i];
+ result.writeToParcel(dest, flags);
+ }
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<ParcelableScanResults> CREATOR =
+ new Creator<ParcelableScanResults>() {
+ public ParcelableScanResults createFromParcel(Parcel in) {
+ int n = in.readInt();
+ ScanResult results[] = new ScanResult[n];
+ for (int i = 0; i < n; i++) {
+ results[i] = ScanResult.CREATOR.createFromParcel(in);
+ }
+ return new ParcelableScanResults(results);
+ }
+
+ public ParcelableScanResults[] newArray(int size) {
+ return new ParcelableScanResults[size];
+ }
+ };
+ }
+
+ /**
+ * Framework is co-ordinating scans across multiple apps; so it may not give exactly the
+ * same period requested. The period granted is stated on the onSuccess() event; and
+ * onPeriodChanged() will be called if/when it is changed because of multiple conflicting
+ * requests. This is similar to the way timers are handled.
+ */
+ public interface ScanListener extends ActionListener {
+ public void onPeriodChanged(int periodInMs);
+ public void onResults(ScanResult[] results);
+ public void onFullResult(FullScanResult fullScanResult);
+ }
+
+ public void scan(ScanSettings settings, ScanListener listener) {
+ validateChannel();
+ sAsyncChannel.sendMessage(CMD_SCAN, 0, putListener(listener), settings);
+ }
+ public void startBackgroundScan(ScanSettings settings, ScanListener listener) {
+ validateChannel();
+ sAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, putListener(listener), settings);
+ }
+ public void stopBackgroundScan(boolean flush, ScanListener listener) {
+ validateChannel();
+ sAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, removeListener(listener));
+ }
+ public void retrieveScanResults(boolean flush, ScanListener listener) {
+ validateChannel();
+ sAsyncChannel.sendMessage(CMD_GET_SCAN_RESULTS, 0, getListenerKey(listener));
+ }
+
+ public static class HotspotInfo {
+ public String bssid;
+ public int low; /* minimum RSSI */
+ public int high; /* maximum RSSI */
+ }
+
+ public static class WifiChangeSettings {
+ public int rssiSampleSize; /* sample size for RSSI averaging */
+ public int lostApSampleSize; /* samples to confirm AP's loss */
+ public int unchangedSampleSize; /* samples to confirm no change */
+ public int minApsBreachingThreshold; /* change threshold to trigger event */
+ public HotspotInfo[] hotspotInfos;
+ }
+
+ /* overrides the significant wifi change state machine configuration */
+ public void configureSignificantWifiChange(
+ int rssiSampleSize, /* sample size for RSSI averaging */
+ int lostApSampleSize, /* samples to confirm AP's loss */
+ int unchangedSampleSize, /* samples to confirm no change */
+ int minApsBreachingThreshold, /* change threshold to trigger event */
+ HotspotInfo[] hotspotInfos /* signal thresholds to crosss */
+ )
+ {
+ validateChannel();
+ WifiChangeSettings settings = new WifiChangeSettings();
+ settings.rssiSampleSize = rssiSampleSize;
+ settings.lostApSampleSize = lostApSampleSize;
+ settings.unchangedSampleSize = unchangedSampleSize;
+ settings.minApsBreachingThreshold = minApsBreachingThreshold;
+ settings.hotspotInfos = hotspotInfos;
+
+ sAsyncChannel.sendMessage(CMD_CONFIGURE_WIFI_CHANGE, 0, 0, settings);
+ }
+
+ public interface SignificantWifiChangeListener extends ActionListener {
+ public void onChanging(ScanResult[] results); /* changes are found */
+ public void onQuiescence(ScanResult[] results); /* changes settled down */
+ }
+
+ public void trackSignificantWifiChange(SignificantWifiChangeListener listener) {
+ validateChannel();
+ sAsyncChannel.sendMessage(CMD_START_TRACKING_CHANGE, 0, putListener(listener));
+ }
+ public void untrackSignificantWifiChange(SignificantWifiChangeListener listener) {
+ validateChannel();
+ sAsyncChannel.sendMessage(CMD_STOP_TRACKING_CHANGE, 0, removeListener(listener));
+ }
+
+ public void configureSignificantWifiChange(WifiChangeSettings settings) {
+ validateChannel();
+ sAsyncChannel.sendMessage(CMD_CONFIGURE_WIFI_CHANGE, 0, 0, settings);
+ }
+
+ public static interface HotlistListener extends ActionListener {
+ public void onFound(ScanResult[] results);
+ }
+
+ /** @hide */
+ public static class HotlistSettings implements Parcelable {
+ public HotspotInfo[] hotspotInfos;
+ public int apLostThreshold;
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(apLostThreshold);
+ dest.writeInt(hotspotInfos.length);
+ for (int i = 0; i < hotspotInfos.length; i++) {
+ HotspotInfo info = hotspotInfos[i];
+ dest.writeString(info.bssid);
+ dest.writeInt(info.low);
+ dest.writeInt(info.high);
+ }
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<HotlistSettings> CREATOR =
+ new Creator<HotlistSettings>() {
+ public HotlistSettings createFromParcel(Parcel in) {
+ HotlistSettings settings = new HotlistSettings();
+ settings.apLostThreshold = in.readInt();
+ int n = in.readInt();
+ settings.hotspotInfos = new HotspotInfo[n];
+ for (int i = 0; i < n; i++) {
+ HotspotInfo info = new HotspotInfo();
+ info.bssid = in.readString();
+ info.low = in.readInt();
+ info.high = in.readInt();
+ settings.hotspotInfos[i] = info;
+ }
+ return settings;
+ }
+
+ public HotlistSettings[] newArray(int size) {
+ return new HotlistSettings[size];
+ }
+ };
+ }
+
+ public void setHotlist(HotspotInfo[] hotspots,
+ int apLostThreshold, HotlistListener listener) {
+ validateChannel();
+ HotlistSettings settings = new HotlistSettings();
+ settings.hotspotInfos = hotspots;
+ sAsyncChannel.sendMessage(CMD_SET_HOTLIST, 0, putListener(listener), settings);
+ }
+
+ public void resetHotlist(HotlistListener listener) {
+ validateChannel();
+ sAsyncChannel.sendMessage(CMD_RESET_HOTLIST, 0, removeListener(listener));
+ }
+
+
+ /* private members and methods */
+
+ private static final String TAG = "WifiScanner";
+ private static final boolean DBG = true;
+
+ /* commands for Wifi Service */
+ private static final int BASE = Protocol.BASE_WIFI_SCANNER;
+
+ /** @hide */
+ public static final int CMD_SCAN = BASE + 0;
+ /** @hide */
+ public static final int CMD_START_BACKGROUND_SCAN = BASE + 2;
+ /** @hide */
+ public static final int CMD_STOP_BACKGROUND_SCAN = BASE + 3;
+ /** @hide */
+ public static final int CMD_GET_SCAN_RESULTS = BASE + 4;
+ /** @hide */
+ public static final int CMD_SCAN_RESULT = BASE + 5;
+ /** @hide */
+ public static final int CMD_SET_HOTLIST = BASE + 6;
+ /** @hide */
+ public static final int CMD_RESET_HOTLIST = BASE + 7;
+ /** @hide */
+ public static final int CMD_AP_FOUND = BASE + 9;
+ /** @hide */
+ public static final int CMD_AP_LOST = BASE + 10;
+ /** @hide */
+ public static final int CMD_START_TRACKING_CHANGE = BASE + 11;
+ /** @hide */
+ public static final int CMD_STOP_TRACKING_CHANGE = BASE + 12;
+ /** @hide */
+ public static final int CMD_CONFIGURE_WIFI_CHANGE = BASE + 13;
+ /** @hide */
+ public static final int CMD_WIFI_CHANGE_DETECTED = BASE + 15;
+ /** @hide */
+ public static final int CMD_WIFI_CHANGES_STABILIZED = BASE + 16;
+ /** @hide */
+ public static final int CMD_OP_SUCCEEDED = BASE + 17;
+ /** @hide */
+ public static final int CMD_OP_FAILED = BASE + 18;
+ /** @hide */
+ public static final int CMD_PERIOD_CHANGED = BASE + 19;
+ /** @hide */
+ public static final int CMD_FULL_SCAN_RESULT = BASE + 20;
+
+ private Context mContext;
+ private IWifiScanner mService;
+
+ private static final int INVALID_KEY = 0;
+ private static int sListenerKey = 1;
+
+ private static final SparseArray sListenerMap = new SparseArray();
+ private static final Object sListenerMapLock = new Object();
+
+ private static AsyncChannel sAsyncChannel;
+ private static CountDownLatch sConnected;
+
+ private static final Object sThreadRefLock = new Object();
+ private static int sThreadRefCount;
+ private static HandlerThread sHandlerThread;
+
+ /**
+ * Create a new WifiScanner instance.
+ * Applications will almost always want to use
+ * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
+ * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
+ * @param context the application context
+ * @param service the Binder interface
+ * @hide
+ */
+ public WifiScanner(Context context, IWifiScanner service) {
+ mContext = context;
+ mService = service;
+ init();
+ }
+
+ private void init() {
+ synchronized (sThreadRefLock) {
+ if (++sThreadRefCount == 1) {
+ Messenger messenger = null;
+ try {
+ messenger = mService.getMessenger();
+ } catch (RemoteException e) {
+ /* do nothing */
+ } catch (SecurityException e) {
+ /* do nothing */
+ }
+
+ if (messenger == null) {
+ sAsyncChannel = null;
+ return;
+ }
+
+ sHandlerThread = new HandlerThread("WifiScanner");
+ sAsyncChannel = new AsyncChannel();
+ sConnected = new CountDownLatch(1);
+
+ sHandlerThread.start();
+ Handler handler = new ServiceHandler(sHandlerThread.getLooper());
+ sAsyncChannel.connect(mContext, handler, messenger);
+ try {
+ sConnected.await();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "interrupted wait at init");
+ }
+ }
+ }
+ }
+
+ private void validateChannel() {
+ if (sAsyncChannel == null) throw new IllegalStateException(
+ "No permission to access and change wifi or a bad initialization");
+ }
+
+ private static int putListener(Object listener) {
+ if (listener == null) return INVALID_KEY;
+ int key;
+ synchronized (sListenerMapLock) {
+ do {
+ key = sListenerKey++;
+ } while (key == INVALID_KEY);
+ sListenerMap.put(key, listener);
+ }
+ return key;
+ }
+
+ private static Object getListener(int key) {
+ if (key == INVALID_KEY) return null;
+ synchronized (sListenerMapLock) {
+ Object listener = sListenerMap.get(key);
+ return listener;
+ }
+ }
+
+ private static int getListenerKey(Object listener) {
+ if (listener == null) return INVALID_KEY;
+ synchronized (sListenerMapLock) {
+ int index = sListenerMap.indexOfValue(listener);
+ if (index == -1) {
+ return INVALID_KEY;
+ } else {
+ return sListenerMap.keyAt(index);
+ }
+ }
+ }
+
+ private static Object removeListener(int key) {
+ if (key == INVALID_KEY) return null;
+ synchronized (sListenerMapLock) {
+ Object listener = sListenerMap.get(key);
+ sListenerMap.remove(key);
+ return listener;
+ }
+ }
+
+ private static int removeListener(Object listener) {
+ int key = getListenerKey(listener);
+ if (key == INVALID_KEY) return key;
+ synchronized (sListenerMapLock) {
+ sListenerMap.remove(key);
+ return key;
+ }
+ }
+
+ private static class ServiceHandler extends Handler {
+ ServiceHandler(Looper looper) {
+ super(looper);
+ }
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+ if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+ } else {
+ Log.e(TAG, "Failed to set up channel connection");
+ // This will cause all further async API calls on the WifiManager
+ // to fail and throw an exception
+ sAsyncChannel = null;
+ }
+ sConnected.countDown();
+ return;
+ case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
+ return;
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+ Log.e(TAG, "Channel connection lost");
+ // This will cause all further async API calls on the WifiManager
+ // to fail and throw an exception
+ sAsyncChannel = null;
+ getLooper().quit();
+ return;
+ }
+
+ Object listener = getListener(msg.arg2);
+ if (DBG) Log.d(TAG, "listener key = " + msg.arg2);
+
+ switch (msg.what) {
+ /* ActionListeners grouped together */
+ case CMD_OP_SUCCEEDED :
+ ((ActionListener) listener).onSuccess(msg.obj);
+ break;
+ case CMD_OP_FAILED :
+ ((ActionListener) listener).onFailure(msg.arg1, msg.obj);
+ break;
+ case CMD_SCAN_RESULT :
+ ((ScanListener) listener).onResults(
+ ((ParcelableScanResults) msg.obj).getResults());
+ return;
+ case CMD_FULL_SCAN_RESULT :
+ FullScanResult result = (FullScanResult) msg.obj;
+ ((ScanListener) listener).onFullResult(result);
+ return;
+ case CMD_AP_FOUND:
+ ((HotlistListener) listener).onFound(
+ ((ParcelableScanResults) msg.obj).getResults());
+ return;
+ case CMD_WIFI_CHANGE_DETECTED:
+ ((SignificantWifiChangeListener) listener).onChanging(
+ ((ParcelableScanResults) msg.obj).getResults());
+ return;
+ case CMD_WIFI_CHANGES_STABILIZED:
+ ((SignificantWifiChangeListener) listener).onQuiescence(
+ ((ParcelableScanResults) msg.obj).getResults());
+ return;
+ default:
+ if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
+ return;
+ }
+ }
+ }
+}