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: I4fbb2cccbb15df21aae7a81f5d9b17fde2bda8c0
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index 161f0f8..ed8be96 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -17,6 +17,8 @@
package com.android.server.wifi;
import android.net.wifi.BatchedScanSettings;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiScanner;
import android.net.wifi.WpsInfo;
import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pGroup;
@@ -1022,24 +1024,27 @@
private long mWifiHalHandle; /* used by JNI to save wifi_handle */
private long[] mWifiIfaceHandles; /* used by JNI to save interface handles */
+ private int mWlan0Index;
+ private int mP2p0Index;
- public native boolean startHalNative();
-
- public native void stopHalNative();
-
- public native void waitForHalEventNative();
+ private native boolean startHalNative();
+ private native void stopHalNative();
+ private native void waitForHalEventNative();
private class MonitorThread extends Thread {
public void run() {
+ Log.i(mTAG, "Waiting for HAL events");
waitForHalEventNative();
}
}
- public void startHal() {
+ public boolean startHal() {
if (startHalNative()) {
new MonitorThread().start();
+ return true;
} else {
Log.i(mTAG, "Could not start hal");
+ return false;
}
}
@@ -1050,7 +1055,17 @@
private native int getInterfacesNative();
public int getInterfaces() {
- return getInterfacesNative();
+ int num = getInterfacesNative();
+ for (int i = 0; i < num; i++) {
+ String name = getInterfaceNameNative(i);
+ Log.i(mTAG, "interface[" + i + "] = " + name);
+ if (name.equals("wlan0")) {
+ mWlan0Index = i;
+ } else if (name.equals("p2p0")) {
+ mP2p0Index = i;
+ }
+ }
+ return num;
}
private native String getInterfaceNameNative(int index);
@@ -1062,44 +1077,183 @@
}
}
- private native boolean startScanNative(int iface, int id);
- private native boolean stopScanNative(int iface, int id);
-
- public static class ScanResult {
- public String SSID;
- public String BSSID;
- public String capabilities;
- public int level;
- public int frequency;
- public long timestamp;
+ public static class ScanCapabilities {
+ public int max_scan_cache_size; // in number of scan results??
+ public int max_scan_buckets;
+ public int max_ap_cache_per_scan;
+ public int max_rssi_sample_size;
+ public int max_scan_reporting_threshold; // in number of scan results??
+ public int max_hotlist_aps;
+ public int max_significant_wifi_change_aps;
}
- void onScanResults(int id, ScanResult[] results) {
+ public boolean getScanCapabilities(ScanCapabilities capabilities) {
+ return getScanCapabilitiesNative(mWlan0Index, capabilities);
+ }
- /* !! This gets called on a different thread !! */
+ private native boolean getScanCapabilitiesNative(int iface, ScanCapabilities capabilities);
- for (int i = 0; i < results.length; i++) {
- Log.i(mTAG, "results[" + i + "].ssid = " + results[i].SSID);
- }
+ private native boolean startScanNative(int iface, int id, ScanSettings settings);
+ private native boolean stopScanNative(int iface, int id);
+ private native ScanResult[] getScanResultsNative(int iface, boolean flush);
+
+ public static class ChannelSettings {
+ int frequency;
+ int dwell_time_ms;
+ boolean passive;
+ }
+
+ public static class BucketSettings {
+ int bucket;
+ int band;
+ int period_ms;
+ int report_events;
+ int num_channels;
+ ChannelSettings channels[] = new ChannelSettings[8];
+ }
+
+ public static class ScanSettings {
+ int base_period_ms;
+ int max_ap_per_scan;
+ int report_threshold;
+ int num_buckets;
+ BucketSettings buckets[] = new BucketSettings[8];
+ }
+
+ public interface ScanEventHandler {
+ void onScanResultsAvailable();
+ void onFullScanResult(ScanResult result, WifiScanner.InformationElement elems[]);
+ }
+
+ void onScanResultsAvailable(int id) {
+ mScanEventHandler.onScanResultsAvailable();
+ }
+
+ void onFullScanResult(int id, ScanResult result, WifiScanner.InformationElement elems[]) {
+ mScanEventHandler.onFullScanResult(result, elems);
}
private int mScanCmdId = 0;
+ private ScanEventHandler mScanEventHandler;
- public boolean startScan() {
+ public boolean startScan(ScanSettings settings, ScanEventHandler eventHandler) {
synchronized (mLock) {
if (mScanCmdId != 0) {
return false;
} else {
mScanCmdId = getNewCmdIdLocked();
}
- }
- return startScanNative(0, mScanCmdId); // results are reported by onScanResults
+ mScanEventHandler = eventHandler;
+
+ if (startScanNative(mWlan0Index, mScanCmdId, settings) == false) {
+ mScanEventHandler = null;
+ return false;
+ }
+
+ return true;
+ }
}
public void stopScan() {
synchronized (mLock) {
- stopScanNative(0, mScanCmdId);
+ stopScanNative(mWlan0Index, mScanCmdId);
+ mScanEventHandler = null;
+ mScanCmdId = 0;
}
}
+
+ public ScanResult[] getScanResults() {
+ return getScanResultsNative(mWlan0Index, /* flush = */ false);
+ }
+
+ public interface HotlistEventHandler {
+ void onHotlistApFound(ScanResult[] result);
+ }
+
+ private int mHotlistCmdId = 0;
+ private HotlistEventHandler mHotlistEventHandler;
+
+ private native boolean setHotlistNative(int iface, int id,
+ WifiScanner.HotlistSettings settings);
+ private native boolean resetHotlistNative(int iface, int id);
+
+ boolean setHotlist(WifiScanner.HotlistSettings settings, HotlistEventHandler eventHandler) {
+ synchronized (mLock) {
+ if (mHotlistCmdId != 0) {
+ return false;
+ } else {
+ mHotlistCmdId = getNewCmdIdLocked();
+ }
+
+ mHotlistEventHandler = eventHandler;
+ if (setHotlistNative(mWlan0Index, mScanCmdId, settings) == false) {
+ mHotlistEventHandler = null;
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ void resetHotlist() {
+ synchronized (mLock) {
+ if (mHotlistCmdId != 0) {
+ resetHotlistNative(mWlan0Index, mHotlistCmdId);
+ mHotlistCmdId = 0;
+ mHotlistEventHandler = null;
+ }
+ }
+ }
+
+ void onHotlistApFound(int id, ScanResult[] results) {
+ mHotlistEventHandler.onHotlistApFound(results);
+ }
+
+ public interface SignificantWifiChangeEventHandler {
+ void onChangesFound(ScanResult[] result);
+ }
+
+ SignificantWifiChangeEventHandler mSignificantWifiChangeHandler;
+ int mSignificantWifiChangeCmdId;
+
+ private native boolean trackSignificantWifiChangeNative(
+ int iface, int id, WifiScanner.WifiChangeSettings settings);
+ private native boolean untrackSignificantWifiChangeNative(int iface, int id);
+
+ boolean trackSignificantWifiChange(WifiScanner.WifiChangeSettings settings,
+ SignificantWifiChangeEventHandler handler) {
+ synchronized (mLock) {
+ if (mSignificantWifiChangeCmdId != 0) {
+ return false;
+ } else {
+ mSignificantWifiChangeCmdId = getNewCmdIdLocked();
+ }
+
+ mSignificantWifiChangeHandler = handler;
+ if (trackSignificantWifiChangeNative(mWlan0Index, mScanCmdId, settings) == false) {
+ mHotlistEventHandler = null;
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ void untrackSignificantWifiChange() {
+ synchronized (mLock) {
+ if (mSignificantWifiChangeCmdId != 0) {
+ untrackSignificantWifiChangeNative(mWlan0Index, mSignificantWifiChangeCmdId);
+ mSignificantWifiChangeCmdId = 0;
+ mSignificantWifiChangeHandler = null;
+ }
+ }
+ }
+
+ void onSignificantWifiChange(int id, ScanResult[] results) {
+ mSignificantWifiChangeHandler.onChangesFound(results);
+ }
+
+
+
}
diff --git a/service/java/com/android/server/wifi/WifiScanningService.java b/service/java/com/android/server/wifi/WifiScanningService.java
new file mode 100644
index 0000000..f11c206
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiScanningService.java
@@ -0,0 +1,52 @@
+/*
+ * 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 com.android.server.wifi;
+
+
+import android.content.Context;
+import android.util.Log;
+
+import com.android.server.SystemService;
+
+public class WifiScanningService extends SystemService {
+
+ private static final String TAG = "WifiScanningService";
+ WifiScanningServiceImpl mImpl;
+
+ public WifiScanningService() {
+ Log.i(TAG, "Creating " + Context.WIFI_SCANNING_SERVICE);
+ }
+
+ @Override
+ public void onStart() {
+ mImpl = new WifiScanningServiceImpl(getContext());
+
+ Log.i(TAG, "Starting " + Context.WIFI_SCANNING_SERVICE);
+ publishBinderService(Context.WIFI_SCANNING_SERVICE, mImpl);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ Log.i(TAG, "Registering " + Context.WIFI_SCANNING_SERVICE);
+ if (mImpl == null) {
+ mImpl = new WifiScanningServiceImpl(getContext());
+ }
+ mImpl.startService(getContext());
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/WifiScanningServiceImpl.java b/service/java/com/android/server/wifi/WifiScanningServiceImpl.java
new file mode 100644
index 0000000..8c637f3
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiScanningServiceImpl.java
@@ -0,0 +1,1301 @@
+/*
+ * 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 com.android.server.wifi;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.IWifiScanner;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiScanner;
+import android.net.wifi.WifiScanner.ScanSettings;
+import android.net.wifi.WifiScanner.FullScanResult;
+import android.net.wifi.WifiSsid;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+import com.android.internal.util.StateMachine;
+import com.android.internal.util.State;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+public class WifiScanningServiceImpl extends IWifiScanner.Stub {
+
+ private static final String TAG = "WifiScanningService";
+ private static final boolean DBG = true;
+ private static final int INVALID_KEY = 0; // same as WifiScanner
+
+ @Override
+ public Messenger getMessenger() {
+ return new Messenger(mClientHandler);
+ }
+
+ private class ClientHandler extends Handler {
+
+ ClientHandler(android.os.Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+
+ if (DBG) Log.d(TAG, "ClientHandler got" + msg);
+
+ switch (msg.what) {
+
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+ if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ AsyncChannel c = (AsyncChannel) msg.obj;
+ if (DBG) Slog.d(TAG, "New client listening to asynchronous messages: " +
+ msg.replyTo);
+ ClientInfo cInfo = new ClientInfo(c, msg.replyTo);
+ mClients.put(msg.replyTo, cInfo);
+ } else {
+ Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
+ }
+ return;
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+ if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
+ Slog.e(TAG, "Send failed, client connection lost");
+ } else {
+ if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
+ }
+ if (DBG) Slog.d(TAG, "closing client " + msg.replyTo);
+ mClients.remove(msg.replyTo);
+ return;
+ case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
+ AsyncChannel ac = new AsyncChannel();
+ ac.connect(mContext, this, msg.replyTo);
+ return;
+ }
+
+ ClientInfo ci = mClients.get(msg.replyTo);
+ if (ci == null) {
+ Slog.e(TAG, "Could not find client info for message " + msg.replyTo);
+ replyFailed(msg, WifiScanner.REASON_INVALID_LISTENER, null);
+ return;
+ }
+
+ int validCommands[] = {
+ WifiScanner.CMD_SCAN,
+ WifiScanner.CMD_START_BACKGROUND_SCAN,
+ WifiScanner.CMD_STOP_BACKGROUND_SCAN,
+ WifiScanner.CMD_SET_HOTLIST,
+ WifiScanner.CMD_RESET_HOTLIST,
+ WifiScanner.CMD_CONFIGURE_WIFI_CHANGE,
+ WifiScanner.CMD_START_TRACKING_CHANGE,
+ WifiScanner.CMD_STOP_TRACKING_CHANGE };
+
+ for(int cmd : validCommands) {
+ if (cmd == msg.what) {
+ mStateMachine.sendMessage(Message.obtain(msg));
+ return;
+ }
+ }
+
+ replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, null);
+ }
+ }
+
+ private static final int BASE = Protocol.BASE_WIFI_SCANNER_SERVICE;
+
+ private static final int CMD_SCAN_RESULTS_AVAILABLE = BASE + 0;
+ private static final int CMD_FULL_SCAN_RESULTS = BASE + 1;
+ private static final int CMD_HOTLIST_AP_FOUND = BASE + 2;
+ private static final int CMD_HOTLIST_AP_LOST = BASE + 3;
+ private static final int CMD_WIFI_CHANGE_DETECTED = BASE + 4;
+ private static final int CMD_WIFI_CHANGES_STABILIZED = BASE + 5;
+ private static final int CMD_DRIVER_LOADED = BASE + 6;
+ private static final int CMD_DRIVER_UNLOADED = BASE + 7;
+
+ private Context mContext;
+ private WifiScanningStateMachine mStateMachine;
+ private ClientHandler mClientHandler;
+ private WifiNative mWifiNative;
+
+ WifiScanningServiceImpl() { }
+
+ WifiScanningServiceImpl(Context context) {
+ mContext = context;
+ }
+
+ public void startService(Context context) {
+ mContext = context;
+
+ HandlerThread thread = new HandlerThread("WifiScanningService");
+ thread.start();
+
+ mClientHandler = new ClientHandler(thread.getLooper());
+ mStateMachine = new WifiScanningStateMachine(thread.getLooper());
+ mWifiChangeStateMachine = new WifiChangeStateMachine(thread.getLooper());
+
+ mWifiNative = new WifiNative("");
+
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int state = intent.getIntExtra(
+ WifiManager.EXTRA_SCAN_AVAILABLE, WifiManager.WIFI_STATE_DISABLED);
+ if (DBG) Log.d(TAG, "SCAN_AVAILABLE : " + state);
+ if (state == WifiManager.WIFI_STATE_ENABLED) {
+ mStateMachine.sendMessage(CMD_DRIVER_LOADED);
+ } else if (state == WifiManager.WIFI_STATE_DISABLED) {
+ mStateMachine.sendMessage(CMD_DRIVER_UNLOADED);
+ }
+ }
+ }, new IntentFilter(WifiManager.WIFI_SCAN_AVAILABLE));
+
+ mStateMachine.start();
+ mWifiChangeStateMachine.start();
+ }
+
+ class WifiScanningStateMachine extends StateMachine implements WifiNative.ScanEventHandler,
+ WifiNative.HotlistEventHandler, WifiNative.SignificantWifiChangeEventHandler {
+
+ private final DefaultState mDefaultState = new DefaultState();
+ private final StartedState mStartedState = new StartedState();
+
+ public WifiScanningStateMachine(Looper looper) {
+ super(TAG, looper);
+
+ setLogRecSize(512);
+ setLogOnlyTransitions(false);
+ // setDbg(DBG);
+
+ addState(mDefaultState);
+ addState(mStartedState, mDefaultState);
+
+ setInitialState(mDefaultState);
+ }
+
+ @Override
+ public void onScanResultsAvailable() {
+ if (DBG) Log.d(TAG, "onScanResultAvailable event received");
+ sendMessage(CMD_SCAN_RESULTS_AVAILABLE);
+ }
+
+ @Override
+ public void onFullScanResult(ScanResult result,
+ WifiScanner.InformationElement informationElements[]) {
+ if (DBG) Log.d(TAG, "Full scanresult received");
+ FullScanResult fullScanResult = new FullScanResult();
+ fullScanResult.result = result;
+ fullScanResult.informationElements = informationElements;
+ sendMessage(CMD_SCAN_RESULTS_AVAILABLE, 0, 0, fullScanResult);
+ }
+
+ @Override
+ public void onHotlistApFound(ScanResult[] results) {
+ if (DBG) Log.d(TAG, "HotlistApFound event received");
+ sendMessage(CMD_HOTLIST_AP_FOUND, 0, 0, results);
+ }
+
+ @Override
+ public void onChangesFound(ScanResult[] results) {
+ if (DBG) Log.d(TAG, "onWifiChangesFound event received");
+ sendMessage(CMD_WIFI_CHANGE_DETECTED, 0, 0, results);
+ }
+
+ class DefaultState extends State {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, "DefaultState");
+ }
+ @Override
+ public boolean processMessage(Message msg) {
+
+ if (DBG) Log.d(TAG, "DefaultState got" + msg);
+
+ ClientInfo ci = mClients.get(msg.replyTo);
+
+ switch (msg.what) {
+ case CMD_DRIVER_LOADED:
+ if (mWifiNative.startHal() && mWifiNative.getInterfaces() != 0) {
+ WifiNative.ScanCapabilities capabilities =
+ new WifiNative.ScanCapabilities();
+ if (mWifiNative.getScanCapabilities(capabilities)) {
+ transitionTo(mStartedState);
+ } else {
+ loge("could not get scan capabilities");
+ }
+ } else {
+ loge("could not start HAL");
+ }
+ break;
+ case WifiScanner.CMD_SCAN:
+ case WifiScanner.CMD_START_BACKGROUND_SCAN:
+ case WifiScanner.CMD_STOP_BACKGROUND_SCAN:
+ case WifiScanner.CMD_SET_HOTLIST:
+ case WifiScanner.CMD_RESET_HOTLIST:
+ case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE:
+ case WifiScanner.CMD_START_TRACKING_CHANGE:
+ case WifiScanner.CMD_STOP_TRACKING_CHANGE:
+ replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, null);
+ break;
+
+ case CMD_SCAN_RESULTS_AVAILABLE:
+ if (DBG) log("ignored scan results available event");
+ break;
+
+ default:
+ break;
+ }
+
+ return HANDLED;
+ }
+ }
+
+ class StartedState extends State {
+
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, "StartedState");
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+
+ if (DBG) Log.d(TAG, "StartedState got" + msg);
+
+ ClientInfo ci = mClients.get(msg.replyTo);
+
+ switch (msg.what) {
+ case CMD_DRIVER_UNLOADED:
+ transitionTo(mDefaultState);
+ break;
+ case WifiScanner.CMD_SCAN:
+ replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, null);
+ break;
+ case WifiScanner.CMD_START_BACKGROUND_SCAN:
+ addScanRequest(ci, msg.arg2, (ScanSettings) msg.obj);
+ replySucceeded(msg, null);
+ break;
+ case WifiScanner.CMD_STOP_BACKGROUND_SCAN:
+ removeScanRequest(ci, msg.arg2);
+ break;
+ case WifiScanner.CMD_GET_SCAN_RESULTS:
+ getScanResults(ci, msg.arg2);
+ break;
+ case WifiScanner.CMD_SET_HOTLIST:
+ setHotlist(ci, msg.arg2, (WifiScanner.HotlistSettings) msg.obj);
+ replySucceeded(msg, null);
+ break;
+ case WifiScanner.CMD_RESET_HOTLIST:
+ resetHotlist(ci, msg.arg2);
+ break;
+ case WifiScanner.CMD_START_TRACKING_CHANGE:
+ trackWifiChanges(ci, msg.arg2);
+ replySucceeded(msg, null);
+ break;
+ case WifiScanner.CMD_STOP_TRACKING_CHANGE:
+ untrackWifiChanges(ci, msg.arg2);
+ break;
+ case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE:
+ replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, null);
+ break;
+ case CMD_SCAN_RESULTS_AVAILABLE: {
+ ScanResult[] results = mWifiNative.getScanResults();
+ Collection<ClientInfo> clients = mClients.values();
+ for (ClientInfo ci2 : clients) {
+ ci2.reportScanResults(results);
+ }
+ }
+ break;
+ case CMD_HOTLIST_AP_FOUND: {
+ ScanResult[] results = (ScanResult[])msg.obj;
+ if (DBG) Log.d(TAG, "Found " + results.length + " results");
+ Collection<ClientInfo> clients = mClients.values();
+ for (ClientInfo ci2 : clients) {
+ ci2.reportHotlistResults(results);
+ }
+ }
+ break;
+ case CMD_WIFI_CHANGE_DETECTED: {
+ ScanResult[] results = (ScanResult[])msg.obj;
+ reportWifiChanged(results);
+ }
+ break;
+ case CMD_WIFI_CHANGES_STABILIZED: {
+ ScanResult[] results = (ScanResult[])msg.obj;
+ reportWifiStabilized(results);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+
+ return HANDLED;
+ }
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ super.dump(fd, pw, args);
+ pw.println("number of clients : " + mClients.size());
+ pw.println();
+ }
+
+ }
+
+ /* client management */
+ HashMap<Messenger, ClientInfo> mClients = new HashMap<Messenger, ClientInfo>();
+
+ private class ClientInfo {
+ private static final int MAX_LIMIT = 16;
+ private final AsyncChannel mChannel;
+ private final Messenger mMessenger;
+
+ ClientInfo(AsyncChannel c, Messenger m) {
+ mChannel = c;
+ mMessenger = m;
+ if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("mChannel ").append(mChannel).append("\n");
+ sb.append("mMessenger ").append(mMessenger).append("\n");
+
+ Iterator<Map.Entry<Integer, ScanSettings>> it = mScanSettings.entrySet().iterator();
+ for (; it.hasNext(); ) {
+ Map.Entry<Integer, ScanSettings> entry = it.next();
+ sb.append("[ScanId ").append(entry.getKey()).append("\n");
+ sb.append("ScanSettings ").append(entry.getValue()).append("\n");
+ sb.append("]");
+ }
+
+ return sb.toString();
+ }
+
+ HashMap<Integer, ScanSettings> mScanSettings = new HashMap<Integer, ScanSettings>(4);
+ void addScanRequest(ScanSettings settings, int id) {
+ mScanSettings.put(id, settings);
+ }
+
+ void removeScanRequest(int id) {
+ mScanSettings.remove(id);
+ }
+
+ Collection<ScanSettings> getScanSettings() {
+ return mScanSettings.values();
+ }
+
+ void reportScanResults(ScanResult[] results) {
+ Iterator<Integer> it = mScanSettings.keySet().iterator();
+ while (it.hasNext()) {
+ int handler = it.next();
+ reportScanResults(results, handler);
+ }
+ }
+
+ void reportScanResults(ScanResult[] results, int handler) {
+ ScanSettings settings = mScanSettings.get(handler);
+
+ // check the channels this client asked for ..
+ int num_results = 0;
+ for (ScanResult result : results) {
+ for (WifiScanner.ChannelSpec channelSpec : settings.channels) {
+ if (channelSpec.frequency == result.frequency) {
+ num_results++;
+ break;
+ }
+ }
+ }
+
+ if (num_results == 0) {
+ // nothing to report
+ return;
+ }
+
+ ScanResult results2[] = new ScanResult[num_results];
+ int index = 0;
+ for (ScanResult result : results) {
+ for (WifiScanner.ChannelSpec channelSpec : settings.channels) {
+ if (channelSpec.frequency == result.frequency) {
+ WifiSsid wifiSsid = WifiSsid.createFromAsciiEncoded(result.SSID);
+ ScanResult newResult = new ScanResult(wifiSsid, result.BSSID, "",
+ result.level, result.frequency, result.timestamp);
+ results2[index] = newResult;
+ index++;
+ break;
+ }
+ }
+ }
+
+ deliverScanResults(handler, results2);
+ }
+
+ void deliverScanResults(int handler, ScanResult results[]) {
+ WifiScanner.ParcelableScanResults parcelableScanResults =
+ new WifiScanner.ParcelableScanResults(results);
+ mChannel.sendMessage(WifiScanner.CMD_SCAN_RESULT, 0, handler, parcelableScanResults);
+ }
+
+ HashMap<Integer, WifiScanner.HotlistSettings> mHotlistSettings =
+ new HashMap<Integer, WifiScanner.HotlistSettings>();
+
+ void addHostlistSettings(WifiScanner.HotlistSettings settings, int handler) {
+ mHotlistSettings.put(handler, settings);
+ }
+
+ void removeHostlistSettings(int handler) {
+ mHotlistSettings.remove(handler);
+ }
+
+ Collection<WifiScanner.HotlistSettings> getHotlistSettings() {
+ return mHotlistSettings.values();
+ }
+
+ void reportHotlistResults(ScanResult[] results) {
+ Iterator<Map.Entry<Integer, WifiScanner.HotlistSettings>> it =
+ mHotlistSettings.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<Integer, WifiScanner.HotlistSettings> entry = it.next();
+ int handler = entry.getKey();
+ WifiScanner.HotlistSettings settings = entry.getValue();
+ int num_results = 0;
+ for (ScanResult result : results) {
+ for (WifiScanner.HotspotInfo hotspotInfo : settings.hotspotInfos) {
+ if (result.BSSID.equalsIgnoreCase(hotspotInfo.bssid)) {
+ num_results++;
+ break;
+ }
+ }
+ }
+
+ if (num_results == 0) {
+ // nothing to report
+ return;
+ }
+
+ ScanResult results2[] = new ScanResult[num_results];
+ int index = 0;
+ for (ScanResult result : results) {
+ for (WifiScanner.HotspotInfo hotspotInfo : settings.hotspotInfos) {
+ if (result.BSSID.equalsIgnoreCase(hotspotInfo.bssid)) {
+ results2[index] = result;
+ index++;
+ }
+ }
+
+ }
+
+ WifiScanner.ParcelableScanResults parcelableScanResults =
+ new WifiScanner.ParcelableScanResults(results2);
+
+ mChannel.sendMessage(WifiScanner.CMD_AP_FOUND, 0, handler, parcelableScanResults);
+ }
+ }
+
+ HashSet<Integer> mSignificantWifiHandlers = new HashSet<Integer>();
+ void addSignificantWifiChange(int handler) {
+ mSignificantWifiHandlers.add(handler);
+ }
+
+ void removeSignificantWifiChange(int handler) {
+ mSignificantWifiHandlers.remove(handler);
+ }
+
+ Collection<Integer> getWifiChangeHandlers() {
+ return mSignificantWifiHandlers;
+ }
+
+ void reportWifiChanged(ScanResult[] results) {
+ WifiScanner.ParcelableScanResults parcelableScanResults =
+ new WifiScanner.ParcelableScanResults(results);
+ Iterator<Integer> it = mSignificantWifiHandlers.iterator();
+ while (it.hasNext()) {
+ int handler = it.next();
+ mChannel.sendMessage(WifiScanner.CMD_WIFI_CHANGE_DETECTED,
+ 0, handler, parcelableScanResults);
+ }
+ }
+
+ void reportWifiStabilized(ScanResult[] results) {
+ WifiScanner.ParcelableScanResults parcelableScanResults =
+ new WifiScanner.ParcelableScanResults(results);
+ Iterator<Integer> it = mSignificantWifiHandlers.iterator();
+ while (it.hasNext()) {
+ int handler = it.next();
+ mChannel.sendMessage(WifiScanner.CMD_WIFI_CHANGES_STABILIZED,
+ 0, handler, parcelableScanResults);
+ }
+ }
+ }
+
+ void replySucceeded(Message msg, Object obj) {
+ if (msg.replyTo != null) {
+ Message reply = Message.obtain();
+ reply.what = WifiScanner.CMD_OP_SUCCEEDED;
+ reply.arg2 = msg.arg2;
+ reply.obj = obj;
+ try {
+ msg.replyTo.send(reply);
+ } catch (RemoteException e) {
+ // There's not much we can do if reply can't be sent!
+ }
+ } else {
+ // locally generated message; doesn't need a reply!
+ }
+ }
+
+ void replyFailed(Message msg, int reason, Object obj) {
+ Message reply = Message.obtain();
+ reply.what = WifiScanner.CMD_OP_FAILED;
+ reply.arg1 = reason;
+ reply.arg2 = msg.arg2;
+ reply.obj = obj;
+ try {
+ msg.replyTo.send(reply);
+ } catch (RemoteException e) {
+ // There's not much we can do if reply can't be sent!
+ }
+ }
+
+ private static class SettingsComputer {
+
+ private static class TimeBucket {
+ int periodInSecond;
+ int periodMinInSecond;
+ int periodMaxInSecond;
+
+ TimeBucket(int p, int min, int max) {
+ periodInSecond = p;
+ periodMinInSecond = min;
+ periodMaxInSecond = max;
+ }
+ }
+
+ private static final TimeBucket[] mTimeBuckets = new TimeBucket[] {
+ new TimeBucket( 5, 0, 10 ),
+ new TimeBucket( 10, 10, 25 ),
+ new TimeBucket( 30, 25, 55 ),
+ new TimeBucket( 60, 55, 100),
+ new TimeBucket( 120, 100, 240),
+ new TimeBucket( 300, 240, 500),
+ new TimeBucket( 600, 500, 1500),
+ new TimeBucket( 1800, 1500, WifiScanner.MAX_SCAN_PERIOD_MS) };
+
+ private static final int MAX_BUCKETS = 8;
+ private static final int MAX_CHANNELS = 8;
+
+ private WifiNative.ScanSettings mSettings;
+ {
+ mSettings = new WifiNative.ScanSettings();
+ mSettings.max_ap_per_scan = 10;
+ mSettings.base_period_ms = 5000;
+ mSettings.report_threshold = 80;
+
+ mSettings.buckets = new WifiNative.BucketSettings[MAX_BUCKETS];
+ for (int i = 0; i < mSettings.buckets.length; i++) {
+ WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings();
+ bucketSettings.bucket = i;
+ bucketSettings.report_events = 0;
+ bucketSettings.channels = new WifiNative.ChannelSettings[MAX_CHANNELS];
+ bucketSettings.num_channels = 0;
+ for (int j = 0; j < bucketSettings.channels.length; j++) {
+ WifiNative.ChannelSettings channelSettings = new WifiNative.ChannelSettings();
+ bucketSettings.channels[j] = channelSettings;
+ }
+ mSettings.buckets[i] = bucketSettings;
+ }
+ }
+
+ HashMap<Integer, Integer> mChannelToBucketMap = new HashMap<Integer, Integer>();
+
+ private int getBestBucket(WifiScanner.ScanSettings settings) {
+
+ // check to see if any of the channels are being scanned already
+ // and find the smallest bucket index (it represents the quickest
+ // period of scan)
+
+ WifiScanner.ChannelSpec channels[] = settings.channels;
+ if (channels == null) {
+ // set channels based on band
+ channels = getChannelsForBand(settings.band);
+ }
+
+ if (channels == null) {
+ // still no channels; then there's nothing to scan
+ Log.e(TAG, "No channels to scan!!");
+ return -1;
+ }
+
+ int mostFrequentBucketIndex = -1;
+
+ for (WifiScanner.ChannelSpec desiredChannelSpec : channels) {
+ if (mChannelToBucketMap.containsKey(desiredChannelSpec.frequency)) {
+ int bucket = mChannelToBucketMap.get(desiredChannelSpec.frequency);
+ if (bucket < mostFrequentBucketIndex) {
+ mostFrequentBucketIndex = bucket;
+ }
+ }
+ }
+
+ int bestBucketIndex = -1; // best by period
+ for (int i = 0; i < mTimeBuckets.length; i++) {
+ TimeBucket bucket = mTimeBuckets[i];
+ if (bucket.periodMinInSecond * 1000 <= settings.periodInMs
+ && settings.periodInMs < bucket.periodMaxInSecond * 1000) {
+ // we set the time period to this
+ bestBucketIndex = i;
+ break;
+ }
+ }
+
+ if (mostFrequentBucketIndex != -1) {
+ if (mostFrequentBucketIndex < bestBucketIndex) {
+ Log.d(TAG, "returning bucket number " + mostFrequentBucketIndex);
+ return mostFrequentBucketIndex;
+ } else {
+ for (WifiScanner.ChannelSpec desiredChannelSpec : channels) {
+ mChannelToBucketMap.put(desiredChannelSpec.frequency, bestBucketIndex);
+ }
+ Log.d(TAG, "returning bucket number " + bestBucketIndex);
+ return bestBucketIndex;
+ }
+ } else if (bestBucketIndex != -1) {
+ return bestBucketIndex;
+ }
+
+ Log.e(TAG, "Could not find suitable bucket for period " + settings.periodInMs);
+ return -1;
+ }
+
+ void prepChannelMap(WifiScanner.ScanSettings settings) {
+ getBestBucket(settings);
+ }
+
+ void addScanRequestToBucket(WifiScanner.ScanSettings settings) {
+
+ int bucketIndex = getBestBucket(settings);
+ if (bucketIndex == -1) {
+ Log.e(TAG, "Ignoring invalid settings");
+ return;
+ }
+
+ WifiScanner.ChannelSpec desiredChannels[] = settings.channels;
+ if (desiredChannels == null) {
+ // set channels based on band
+ desiredChannels = getChannelsForBand(settings.band);
+ if (desiredChannels == null) {
+ // still no channels; then there's nothing to scan
+ Log.e(TAG, "No channels to scan!!");
+ return;
+ }
+ }
+
+ // merge the channel lists for these buckets
+ Log.d(TAG, "merging " + desiredChannels.length + " channels "
+ + " for period " + settings.periodInMs);
+
+ WifiNative.BucketSettings bucket = mSettings.buckets[bucketIndex];
+ boolean added = (bucket.num_channels == 0)
+ && (bucket.band == WifiScanner.WIFI_BAND_UNSPECIFIED);
+ Log.d(TAG, "existing " + bucket.num_channels + " channels ");
+
+ if (settings.band != WifiScanner.WIFI_BAND_UNSPECIFIED
+ || (bucket.num_channels + desiredChannels.length) > bucket.channels.length) {
+ // can't accommodate all channels; switch to specifying band
+ bucket.num_channels = 0;
+ bucket.band = getBandFromChannels(bucket.channels)
+ | getBandFromChannels(desiredChannels);
+ } else {
+ for (WifiScanner.ChannelSpec desiredChannelSpec : desiredChannels) {
+
+ Log.d(TAG, "desired channel " + desiredChannelSpec.frequency);
+
+ boolean found = false;
+ for (WifiNative.ChannelSettings existingChannelSpec : bucket.channels) {
+ if (desiredChannelSpec.frequency == existingChannelSpec.frequency) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ WifiNative.ChannelSettings channelSettings = bucket.channels[bucket.num_channels];
+ channelSettings.frequency = desiredChannelSpec.frequency;
+ bucket.num_channels++;
+ mChannelToBucketMap.put(bucketIndex, channelSettings.frequency);
+ } else {
+ if (DBG) Log.d(TAG, "Already scanning channel " + desiredChannelSpec.frequency);
+ }
+ }
+ }
+
+ if (bucket.report_events < settings.reportEvents) {
+ bucket.report_events = settings.reportEvents;
+ }
+
+ if (added) {
+ bucket.period_ms = mTimeBuckets[bucketIndex].periodInSecond * 1000;
+ mSettings.num_buckets++;
+ }
+ }
+
+ public WifiNative.ScanSettings getComputedSettings() {
+ return mSettings;
+ }
+
+ public void compressBuckets() {
+ int num_buckets = 0;
+ for (int i = 0; i < mSettings.buckets.length; i++) {
+ if (mSettings.buckets[i].num_channels != 0
+ || mSettings.buckets[i].band != WifiScanner.WIFI_BAND_UNSPECIFIED) {
+ mSettings.buckets[num_buckets] = mSettings.buckets[i];
+ num_buckets++;
+ }
+ }
+ // remove unused buckets
+ for (int i = num_buckets; i < mSettings.buckets.length; i++) {
+ mSettings.buckets[i] = null;
+ }
+
+ mSettings.num_buckets = num_buckets;
+ if (num_buckets != 0) {
+ mSettings.base_period_ms = mSettings.buckets[0].period_ms;
+ }
+ }
+ }
+
+ void resetBuckets() {
+ SettingsComputer c = new SettingsComputer();
+ Collection<ClientInfo> clients = mClients.values();
+ for (ClientInfo ci : clients) {
+ Collection<ScanSettings> settings = ci.getScanSettings();
+ for (ScanSettings s : settings) {
+ c.prepChannelMap(s);
+ }
+ }
+
+ for (ClientInfo ci : clients) {
+ Collection<ScanSettings> settings = ci.getScanSettings();
+ for (ScanSettings s : settings) {
+ c.addScanRequestToBucket(s);
+ }
+ }
+
+ c.compressBuckets();
+
+ WifiNative.ScanSettings s = c.getComputedSettings();
+ if (s.num_buckets == 0) {
+ if (DBG) Log.d(TAG, "Stopping scan because there are no buckets");
+ mWifiNative.stopScan();
+ } else {
+ if (mWifiNative.startScan(s, mStateMachine)) {
+ if (DBG) Log.d(TAG, "Successfully started scan of " + s.num_buckets + " buckets");
+ } else {
+ if (DBG) Log.d(TAG, "Failed to start scan of " + s.num_buckets + " buckets");
+ }
+ }
+ }
+
+ void addScanRequest(ClientInfo ci, int handler, ScanSettings settings) {
+ ci.addScanRequest(settings, handler);
+ resetBuckets();
+ }
+
+ void removeScanRequest(ClientInfo ci, int handler) {
+ ci.removeScanRequest(handler);
+ resetBuckets();
+ }
+
+ void getScanResults(ClientInfo ci, int handler) {
+ ScanResult results[] = mWifiNative.getScanResults();
+ ci.reportScanResults(results, handler);
+ }
+
+ void resetHotlist() {
+ Collection<ClientInfo> clients = mClients.values();
+ int num_hotlist_ap = 0;
+
+ for (ClientInfo ci : clients) {
+ Collection<WifiScanner.HotlistSettings> c = ci.getHotlistSettings();
+ for (WifiScanner.HotlistSettings s : c) {
+ num_hotlist_ap += s.hotspotInfos.length;
+ }
+ }
+
+ if (num_hotlist_ap == 0) {
+ mWifiNative.resetHotlist();
+ } else {
+ WifiScanner.HotspotInfo hotspotInfos[] = new WifiScanner.HotspotInfo[num_hotlist_ap];
+ int index = 0;
+ for (ClientInfo ci : clients) {
+ Collection<WifiScanner.HotlistSettings> settings = ci.getHotlistSettings();
+ for (WifiScanner.HotlistSettings s : settings) {
+ for (int i = 0; i < s.hotspotInfos.length; i++, index++) {
+ hotspotInfos[index] = s.hotspotInfos[i];
+ }
+ }
+ }
+
+ WifiScanner.HotlistSettings settings = new WifiScanner.HotlistSettings();
+ settings.hotspotInfos = hotspotInfos;
+ settings.apLostThreshold = 3;
+ mWifiNative.setHotlist(settings, mStateMachine);
+ }
+ }
+
+ void setHotlist(ClientInfo ci, int handler, WifiScanner.HotlistSettings settings) {
+ ci.addHostlistSettings(settings, handler);
+ resetHotlist();
+ }
+
+ void resetHotlist(ClientInfo ci, int handler) {
+ ci.removeHostlistSettings(handler);
+ resetHotlist();
+ }
+
+ WifiChangeStateMachine mWifiChangeStateMachine;
+
+ void trackWifiChanges(ClientInfo ci, int handler) {
+ mWifiChangeStateMachine.enable();
+ ci.addSignificantWifiChange(handler);
+ }
+
+ void untrackWifiChanges(ClientInfo ci, int handler) {
+ ci.removeSignificantWifiChange(handler);
+ Collection<ClientInfo> clients = mClients.values();
+ for (ClientInfo ci2 : clients) {
+ if (ci2.getWifiChangeHandlers().size() != 0) {
+ // there is at least one client watching for
+ // significant changes; so nothing more to do
+ return;
+ }
+ }
+
+ // no more clients looking for significant wifi changes
+ // no need to keep the state machine running; disable it
+ mWifiChangeStateMachine.disable();
+ }
+
+ void reportWifiChanged(ScanResult results[]) {
+ Collection<ClientInfo> clients = mClients.values();
+ for (ClientInfo ci : clients) {
+ ci.reportWifiChanged(results);
+ }
+ }
+
+ void reportWifiStabilized(ScanResult results[]) {
+ Collection<ClientInfo> clients = mClients.values();
+ for (ClientInfo ci : clients) {
+ ci.reportWifiStabilized(results);
+ }
+ }
+
+ class WifiChangeStateMachine extends StateMachine
+ implements WifiNative.SignificantWifiChangeEventHandler {
+
+ private static final String TAG = "WifiChangeStateMachine";
+
+ private static final int WIFI_CHANGE_CMD_NEW_SCAN_RESULTS = 0;
+ private static final int WIFI_CHANGE_CMD_CHANGE_DETECTED = 1;
+ private static final int WIFI_CHANGE_CMD_CHANGE_TIMEOUT = 2;
+ private static final int WIFI_CHANGE_CMD_ENABLE = 3;
+ private static final int WIFI_CHANGE_CMD_DISABLE = 4;
+
+ private static final int MAX_APS_TO_TRACK = 3;
+ private static final int MOVING_SCAN_PERIOD_MS = 10000;
+ private static final int STATIONARY_SCAN_PERIOD_MS = 5000;
+ private static final int MOVING_STATE_TIMEOUT_MS = 30000;
+
+ State mDefaultState = new DefaultState();
+ State mStationaryState = new StationaryState();
+ State mMovingState = new MovingState();
+
+ private static final String ACTION_TIMEOUT =
+ "com.android.server.WifiScanningServiceImpl.action.TIMEOUT";
+ AlarmManager mAlarmManager;
+ PendingIntent mTimeoutIntent;
+ ScanResult mCurrentHotspots[];
+
+ WifiChangeStateMachine(Looper looper) {
+ super("SignificantChangeStateMachine", looper);
+
+ mClients.put(null, mClientInfo);
+
+ addState(mDefaultState);
+ addState(mStationaryState, mDefaultState);
+ addState(mMovingState, mDefaultState);
+
+ setInitialState(mDefaultState);
+ }
+
+ public void enable() {
+ if (mAlarmManager == null) {
+ mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ }
+
+ if (mTimeoutIntent == null) {
+ Intent intent = new Intent(ACTION_TIMEOUT, null);
+ mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ sendMessage(WIFI_CHANGE_CMD_CHANGE_TIMEOUT);
+ }
+ }, new IntentFilter(ACTION_TIMEOUT));
+ }
+
+ sendMessage(WIFI_CHANGE_CMD_ENABLE);
+ }
+
+ public void disable() {
+ sendMessage(WIFI_CHANGE_CMD_DISABLE);
+ }
+
+ class DefaultState extends State {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, "Entering IdleState");
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ if (DBG) Log.d(TAG, "DefaultState state got " + msg);
+ switch (msg.what) {
+ case WIFI_CHANGE_CMD_ENABLE :
+ transitionTo(mMovingState);
+ break;
+ case WIFI_CHANGE_CMD_DISABLE:
+ // nothing to do
+ break;
+ case WIFI_CHANGE_CMD_NEW_SCAN_RESULTS:
+ // nothing to do
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+
+ }
+
+ class StationaryState extends State {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, "Entering StationaryState");
+ reportWifiStabilized(mCurrentHotspots);
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ if (DBG) Log.d(TAG, "Stationary state got " + msg);
+ switch (msg.what) {
+ case WIFI_CHANGE_CMD_ENABLE :
+ // do nothing
+ break;
+ case WIFI_CHANGE_CMD_CHANGE_DETECTED:
+ if (DBG) Log.d(TAG, "Got wifi change detected");
+ reportWifiChanged((ScanResult[])msg.obj);
+ transitionTo(mMovingState);
+ break;
+ case WIFI_CHANGE_CMD_DISABLE:
+ mCurrentHotspots = null;
+ removeScanRequest();
+ untrackSignificantWifiChange();
+ transitionTo(mDefaultState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class MovingState extends State {
+ boolean wifiChangeDetected = false;
+
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, "Entering MovingState");
+ WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings();
+ settings.band = WifiScanner.WIFI_BAND_UNSPECIFIED;
+ /* TODO: Currently no driver allows scanning with band; hence this workaround */
+ settings.channels = getChannelsForBand(WifiScanner.WIFI_BAND_24_GHZ);
+ settings.periodInMs = MOVING_SCAN_PERIOD_MS;
+ settings.reportEvents = 1;
+ addScanRequest(settings);
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ if (DBG) Log.d(TAG, "MovingState state got " + msg);
+ switch (msg.what) {
+ case WIFI_CHANGE_CMD_ENABLE :
+ // do nothing
+ break;
+ case WIFI_CHANGE_CMD_DISABLE:
+ mCurrentHotspots = null;
+ removeScanRequest();
+ untrackSignificantWifiChange();
+ transitionTo(mDefaultState);
+ break;
+ case WIFI_CHANGE_CMD_NEW_SCAN_RESULTS:
+ if (DBG) Log.d(TAG, "Got scan results");
+ removeScanRequest();
+ reconfigureScan((ScanResult[])msg.obj, STATIONARY_SCAN_PERIOD_MS);
+ wifiChangeDetected = false;
+ mAlarmManager.setExact(AlarmManager.RTC_WAKEUP,
+ System.currentTimeMillis() + MOVING_STATE_TIMEOUT_MS,
+ mTimeoutIntent);
+ break;
+ case WIFI_CHANGE_CMD_CHANGE_DETECTED:
+ mAlarmManager.cancel(mTimeoutIntent);
+ reportWifiChanged((ScanResult[])msg.obj);
+ wifiChangeDetected = true;
+ break;
+ case WIFI_CHANGE_CMD_CHANGE_TIMEOUT:
+ if (DBG) Log.d(TAG, "Got timeout event");
+ if (wifiChangeDetected == false) {
+ transitionTo(mStationaryState);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+
+ @Override
+ public void exit() {
+ mAlarmManager.cancel(mTimeoutIntent);
+ }
+ }
+
+ void reconfigureScan(ScanResult[] results, int period) {
+ // find brightest APs and set them as sentinels
+
+ // remove duplicate BSSIDs
+ HashMap<String, ScanResult> bssidToScanResult = new HashMap<String, ScanResult>();
+ for (ScanResult result : results) {
+ ScanResult saved = bssidToScanResult.get(result.BSSID);
+ if (saved == null) {
+ bssidToScanResult.put(result.BSSID, result);
+ } else if (saved.level > result.level) {
+ bssidToScanResult.put(result.BSSID, result);
+ }
+ }
+
+ // find brightest BSSIDs
+ ScanResult brightest[] = new ScanResult[MAX_APS_TO_TRACK];
+ Collection<ScanResult> results2 = bssidToScanResult.values();
+ for (ScanResult result : results2) {
+ for (int j = 0; j < brightest.length; j++) {
+ if (brightest[j] == null
+ || (brightest[j].level < result.level)) {
+ for (int k = brightest.length; k > (j + 1); k--) {
+ brightest[k - 1] = brightest[k - 2];
+ }
+ brightest[j] = result;
+ break;
+ }
+ }
+ }
+
+ // Get channels to scan for
+ ArrayList<Integer> channels = new ArrayList<Integer>();
+ for (int i = 0; i < brightest.length; i++) {
+ boolean found = false;
+ for (int j = i + 1; j < brightest.length; j++) {
+ if (brightest[j].frequency == brightest[i].frequency) {
+ found = true;
+ }
+ }
+ if (!found) {
+ channels.add(brightest[i].frequency);
+ }
+ }
+
+ if (DBG) Log.d(TAG, "Found " + channels.size() + " channels");
+
+ // set scanning schedule
+ WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings();
+ settings.band = WifiScanner.WIFI_BAND_UNSPECIFIED;
+ settings.channels = new WifiScanner.ChannelSpec[channels.size()];
+ for (int i = 0; i < channels.size(); i++) {
+ settings.channels[i] = new WifiScanner.ChannelSpec(channels.get(i));
+ }
+
+ settings.periodInMs = period;
+ addScanRequest(settings);
+
+ WifiScanner.WifiChangeSettings settings2 = new WifiScanner.WifiChangeSettings();
+ settings2.rssiSampleSize = 3;
+ settings2.lostApSampleSize = 3;
+ settings2.unchangedSampleSize = 3;
+ settings2.minApsBreachingThreshold = 3;
+ settings2.hotspotInfos = new WifiScanner.HotspotInfo[brightest.length];
+
+ for (int i = 0; i < brightest.length; i++) {
+ WifiScanner.HotspotInfo hotspotInfo = new WifiScanner.HotspotInfo();
+ hotspotInfo.bssid = brightest[i].BSSID;
+ int threshold = (100 + brightest[i].level) / 32 + 2;
+ hotspotInfo.low = brightest[i].level - threshold;
+ hotspotInfo.high = brightest[i].level + threshold;
+ settings2.hotspotInfos[i] = hotspotInfo;
+
+ if (DBG) Log.d(TAG, "Setting bssid=" + hotspotInfo.bssid + ", " +
+ "low=" + hotspotInfo.low + ", high=" + hotspotInfo.high);
+ }
+
+ trackSignificantWifiChange(settings2);
+ mCurrentHotspots = brightest;
+ }
+
+ class ClientInfoLocal extends ClientInfo {
+ ClientInfoLocal() {
+ super(null, null);
+ }
+ @Override
+ void deliverScanResults(int handler, ScanResult results[]) {
+ if (DBG) Log.d(TAG, "Delivering messages directly");
+ sendMessage(WIFI_CHANGE_CMD_NEW_SCAN_RESULTS, 0, 0, results);
+ }
+ }
+
+ @Override
+ public void onChangesFound(ScanResult results[]) {
+ sendMessage(WIFI_CHANGE_CMD_CHANGE_DETECTED, 0, 0, results);
+ }
+
+ ClientInfo mClientInfo = new ClientInfoLocal();
+ private static final int SCAN_COMMAND_ID = 1;
+
+ void addScanRequest(WifiScanner.ScanSettings settings) {
+ if (DBG) Log.d(TAG, "Starting scans");
+ Message msg = Message.obtain();
+ msg.what = WifiScanner.CMD_START_BACKGROUND_SCAN;
+ msg.arg2 = SCAN_COMMAND_ID;
+ msg.obj = settings;
+ mClientHandler.sendMessage(msg);
+ }
+
+ void removeScanRequest() {
+ if (DBG) Log.d(TAG, "Stopping scans");
+ Message msg = Message.obtain();
+ msg.what = WifiScanner.CMD_STOP_BACKGROUND_SCAN;
+ msg.arg2 = SCAN_COMMAND_ID;
+ mClientHandler.sendMessage(msg);
+ }
+
+ void trackSignificantWifiChange(WifiScanner.WifiChangeSettings settings) {
+ mWifiNative.trackSignificantWifiChange(settings, this);
+ }
+
+ void untrackSignificantWifiChange() {
+ mWifiNative.untrackSignificantWifiChange();
+ }
+
+ }
+
+ private static WifiScanner.ChannelSpec[] getChannelsForBand(int band) {
+
+ /* TODO: We need to query this from the chip */
+
+ if (band == WifiScanner.WIFI_BAND_24_GHZ) {
+ return new WifiScanner.ChannelSpec[] {
+ new WifiScanner.ChannelSpec(2412),
+ new WifiScanner.ChannelSpec(2437),
+ new WifiScanner.ChannelSpec(2462)
+ };
+ } else if (band == WifiScanner.WIFI_BAND_5_GHZ
+ || band == WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS) {
+ return new WifiScanner.ChannelSpec[] {
+ new WifiScanner.ChannelSpec(5180),
+ new WifiScanner.ChannelSpec(5200),
+ new WifiScanner.ChannelSpec(5220),
+ new WifiScanner.ChannelSpec(5745),
+ new WifiScanner.ChannelSpec(5765),
+ new WifiScanner.ChannelSpec(5785),
+ new WifiScanner.ChannelSpec(5805),
+ new WifiScanner.ChannelSpec(5825)
+ };
+ } else if (band == WifiScanner.WIFI_BAND_BOTH
+ || band == WifiScanner.WIFI_BAND_BOTH_WITH_DFS) {
+ return new WifiScanner.ChannelSpec[] {
+ new WifiScanner.ChannelSpec(2412),
+ new WifiScanner.ChannelSpec(2437),
+ new WifiScanner.ChannelSpec(2462),
+ new WifiScanner.ChannelSpec(5180),
+ new WifiScanner.ChannelSpec(5200),
+ new WifiScanner.ChannelSpec(5220),
+ new WifiScanner.ChannelSpec(5745),
+ new WifiScanner.ChannelSpec(5765),
+ new WifiScanner.ChannelSpec(5785),
+ new WifiScanner.ChannelSpec(5805),
+ new WifiScanner.ChannelSpec(5825)
+ };
+ } else {
+ return null;
+ }
+ }
+
+ private static int getBandFromChannels(WifiScanner.ChannelSpec[] channels) {
+ int band = WifiScanner.WIFI_BAND_UNSPECIFIED;
+ for (WifiScanner.ChannelSpec channel : channels) {
+ if (2400 <= channel.frequency && channel.frequency < 2500) {
+ band |= WifiScanner.WIFI_BAND_24_GHZ;
+ } else if (5100 <= channel.frequency && channel.frequency < 6000) {
+ band |= WifiScanner.WIFI_BAND_5_GHZ;
+ } else {
+ /* TODO: Add DFS Range */
+ }
+ }
+ return band;
+ }
+ private static int getBandFromChannels(WifiNative.ChannelSettings[] channels) {
+ int band = WifiScanner.WIFI_BAND_UNSPECIFIED;
+ for (WifiNative.ChannelSettings channel : channels) {
+ if (2400 <= channel.frequency && channel.frequency < 2500) {
+ band |= WifiScanner.WIFI_BAND_24_GHZ;
+ } else if (5100 <= channel.frequency && channel.frequency < 6000) {
+ band |= WifiScanner.WIFI_BAND_5_GHZ;
+ } else {
+ /* TODO: Add DFS Range */
+ }
+ }
+ return band;
+ }
+
+}
diff --git a/service/java/com/android/server/wifi/WifiService.java b/service/java/com/android/server/wifi/WifiService.java
index 8caa3eb..70e2c1d 100644
--- a/service/java/com/android/server/wifi/WifiService.java
+++ b/service/java/com/android/server/wifi/WifiService.java
@@ -17,9 +17,13 @@
package com.android.server.wifi;
import android.content.Context;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiScanner;
import android.util.Log;
import com.android.server.SystemService;
+import java.util.List;
+
public final class WifiService extends SystemService {
private static final String TAG = "WifiService";
diff --git a/service/jni/com_android_server_wifi_WifiNative.cpp b/service/jni/com_android_server_wifi_WifiNative.cpp
index e635d62..caae355 100644
--- a/service/jni/com_android_server_wifi_WifiNative.cpp
+++ b/service/jni/com_android_server_wifi_WifiNative.cpp
@@ -22,6 +22,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
#include <utils/String16.h>
+#include <ctype.h>
#include "wifi.h"
#include "wifi_hal.h"
@@ -165,27 +166,6 @@
return (wifi_interface_handle) getLongArrayField(env, obj, WifiIfaceHandleVarName, index);
}
-static void saveEventMethodIds(JNIEnv *env, jobject obj) {
-
- jclass cls = (env)->GetObjectClass(obj);
- if (cls == NULL) {
- ALOGE("Error in accessing class");
- return;
- } else {
- ALOGD("cls = %p", cls);
- }
-
- jmethodID method = env->GetMethodID(cls, "onScanResults",
- "(I[Lcom/android/server/wifi/WifiNative$ScanResult;)V");
-
- if (method == NULL) {
- ALOGE("Error in getting method ID");
- return;
- } else {
- ALOGD("method = %p", method);
- }
-}
-
static jboolean android_net_wifi_startHal(JNIEnv* env, jobject obj) {
ALOGD("In wifi start Hal");
wifi_handle halHandle = getWifiHandle(env, obj);
@@ -279,9 +259,209 @@
ALOGD("onScanResultsAvailable called, vm = %p, obj = %p, env = %p", mVM, mObj, env);
- /*
+ reportEvent(env, mObj, "onScanResultsAvailable", "(I)V", id);
+}
- jclass clsScanResult = (env)->FindClass("com/android/server/wifi/WifiNative$ScanResult");
+static jboolean android_net_wifi_startScan(
+ JNIEnv *env, jobject obj, jint iface, jint id, jobject settings) {
+
+ wifi_interface_handle handle = getIfaceHandle(env, obj, iface);
+ ALOGD("starting scan on interface[%d] = %p", iface, handle);
+
+ wifi_scan_cmd_params params;
+ memset(¶ms, 0, sizeof(params));
+
+ params.base_period = getIntField(env, settings, "base_period_ms");
+ params.max_ap_per_scan = getIntField(env, settings, "max_ap_per_scan");
+ params.report_threshold = getIntField(env, settings, "report_threshold");
+
+ ALOGD("Initialized common fields %d, %d, %d", params.base_period,
+ params.max_ap_per_scan, params.report_threshold);
+
+ const char *bucket_array_type = "[Lcom/android/server/wifi/WifiNative$BucketSettings;";
+ const char *channel_array_type = "[Lcom/android/server/wifi/WifiNative$ChannelSettings;";
+
+ jobjectArray buckets = (jobjectArray)getObjectField(env, settings, "buckets", bucket_array_type);
+ params.num_buckets = getIntField(env, settings, "num_buckets");
+
+ ALOGD("Initialized num_buckets to %d", params.num_buckets);
+
+ for (int i = 0; i < params.num_buckets; i++) {
+ jobject bucket = getObjectArrayField(env, settings, "buckets", bucket_array_type, i);
+
+ params.buckets[i].bucket = getIntField(env, bucket, "bucket");
+ params.buckets[i].band = (wifi_band) getIntField(env, bucket, "band");
+ params.buckets[i].period = getIntField(env, bucket, "period_ms");
+
+ ALOGD("Initialized common bucket fields %d:%d:%d", params.buckets[i].bucket,
+ params.buckets[i].band, params.buckets[i].period);
+
+ int report_events = getIntField(env, bucket, "report_events");
+ params.buckets[i].report_events = report_events;
+
+ ALOGD("Initialized report events to %d", params.buckets[i].report_events);
+
+ jobjectArray channels = (jobjectArray)getObjectField(
+ env, bucket, "channels", channel_array_type);
+
+ params.buckets[i].num_channels = getIntField(env, bucket, "num_channels");
+ ALOGD("Initialized num_channels to %d", params.buckets[i].num_channels);
+
+ for (int j = 0; j < params.buckets[i].num_channels; j++) {
+ jobject channel = getObjectArrayField(env, bucket, "channels", channel_array_type, j);
+
+ params.buckets[i].channels[j].channel = getIntField(env, channel, "frequency");
+ params.buckets[i].channels[j].dwellTimeMs = getIntField(env, channel, "dwell_time_ms");
+
+ bool passive = getBoolField(env, channel, "passive");
+ params.buckets[i].channels[j].passive = (passive ? 1 : 0);
+
+ ALOGD("Initialized channel %d", params.buckets[i].channels[j].channel);
+ }
+ }
+
+ ALOGD("Initialized all fields");
+
+ wifi_scan_result_handler handler;
+ memset(&handler, 0, sizeof(handler));
+ handler.on_scan_results_available = &onScanResultsAvailable;
+
+ return wifi_start_gscan(id, handle, params, handler) == WIFI_SUCCESS;
+}
+
+static jboolean android_net_wifi_stopScan(JNIEnv *env, jobject obj, jint iface, jint id) {
+ wifi_interface_handle handle = getIfaceHandle(env, obj, iface);
+ ALOGD("stopping scan on interface[%d] = %p", iface, handle);
+
+ return wifi_stop_gscan(id, handle) == WIFI_SUCCESS;
+}
+
+static jobject android_net_wifi_getScanResults(
+ JNIEnv *env, jobject obj, jint iface, jboolean flush) {
+
+ wifi_scan_result results[256];
+ int num_results = 256;
+
+ wifi_interface_handle handle = getIfaceHandle(env, obj, iface);
+ ALOGD("getting scan results on interface[%d] = %p", iface, handle);
+
+ int result = wifi_get_cached_gscan_results(handle, 1, results, &num_results);
+ if (result == WIFI_SUCCESS) {
+ jclass clsScanResult = (env)->FindClass("android/net/wifi/ScanResult");
+ if (clsScanResult == NULL) {
+ ALOGE("Error in accessing class");
+ return NULL;
+ }
+
+ jobjectArray scanResults = env->NewObjectArray(num_results, clsScanResult, NULL);
+ if (scanResults == NULL) {
+ ALOGE("Error in allocating array");
+ return NULL;
+ }
+
+ for (int i = 0; i < num_results; i++) {
+
+ jobject scanResult = createObject(env, "android/net/wifi/ScanResult");
+ if (scanResult == NULL) {
+ ALOGE("Error in creating scan result");
+ return NULL;
+ }
+
+ setStringField(env, scanResult, "SSID", results[i].ssid);
+
+ char bssid[32];
+ sprintf(bssid, "%02x:%02x:%02x:%02x:%02x:%02x", results[i].bssid[0],
+ results[i].bssid[1], results[i].bssid[2], results[i].bssid[3],
+ results[i].bssid[4], results[i].bssid[5]);
+
+ setStringField(env, scanResult, "BSSID", bssid);
+
+ setIntField(env, scanResult, "level", results[i].rssi);
+ setIntField(env, scanResult, "frequency", results[i].channel);
+ setLongField(env, scanResult, "timestamp", results[i].ts);
+
+ env->SetObjectArrayElement(scanResults, i, scanResult);
+ env->DeleteLocalRef(scanResult);
+ }
+
+ return scanResults;
+ } else {
+ return NULL;
+ }
+}
+
+
+static jboolean android_net_wifi_getScanCapabilities(
+ JNIEnv *env, jobject obj, jint iface, jobject capabilities) {
+
+ wifi_interface_handle handle = getIfaceHandle(env, obj, iface);
+ ALOGD("getting scan capabilities on interface[%d] = %p", iface, handle);
+
+ wifi_gscan_capabilities c;
+ memset(&c, 0, sizeof(c));
+ int result = wifi_get_gscan_capabilities(handle, &c);
+ if (result != WIFI_SUCCESS) {
+ ALOGD("failed to get capabilities : %d", result);
+ return JNI_FALSE;
+ }
+
+ setIntField(env, capabilities, "max_scan_cache_size", c.max_scan_cache_size);
+ setIntField(env, capabilities, "max_scan_buckets", c.max_scan_buckets);
+ setIntField(env, capabilities, "max_ap_cache_per_scan", c.max_ap_cache_per_scan);
+ setIntField(env, capabilities, "max_rssi_sample_size", c.max_rssi_sample_size);
+ setIntField(env, capabilities, "max_scan_reporting_threshold", c.max_scan_reporting_threshold);
+ setIntField(env, capabilities, "max_hotlist_aps", c.max_hotlist_aps);
+ setIntField(env, capabilities, "max_significant_wifi_change_aps",
+ c.max_significant_wifi_change_aps);
+
+ return JNI_TRUE;
+}
+
+
+static byte parseHexChar(char ch) {
+ if (isdigit(ch))
+ return ch - '0';
+ else if ('A' <= ch && ch <= 'F')
+ return ch - 'A' + 10;
+ else if ('a' <= ch && ch <= 'f')
+ return ch - 'a' + 10;
+ else {
+ ALOGE("invalid character in bssid %c", ch);
+ return 0;
+ }
+}
+
+static byte parseHexByte(const char * &str) {
+ byte b = parseHexChar(str[0]);
+ if (str[1] == ':' || str[1] == '\0') {
+ str += 2;
+ return b;
+ } else {
+ b = b << 4 | parseHexChar(str[1]);
+ str += 3;
+ return b;
+ }
+}
+
+static void parseMacAddress(const char *str, mac_addr addr) {
+ addr[0] = parseHexByte(str);
+ addr[1] = parseHexByte(str);
+ addr[2] = parseHexByte(str);
+ addr[3] = parseHexByte(str);
+ addr[4] = parseHexByte(str);
+ addr[5] = parseHexByte(str);
+}
+
+static void onHotlistApFound(wifi_request_id id,
+ unsigned num_results, wifi_scan_result *results) {
+
+ JNIEnv *env = NULL;
+ mVM->AttachCurrentThread(&env, NULL);
+
+ ALOGD("onHotlistApFound called, vm = %p, obj = %p, env = %p, num_results = %d",
+ mVM, mObj, env, num_results);
+
+ jclass clsScanResult = (env)->FindClass("android/net/wifi/ScanResult");
if (clsScanResult == NULL) {
ALOGE("Error in accessing class");
return;
@@ -295,7 +475,7 @@
for (unsigned i = 0; i < num_results; i++) {
- jobject scanResult = createObject(env, "com/android/server/wifi/WifiNative$ScanResult");
+ jobject scanResult = createObject(env, "android/net/wifi/ScanResult");
if (scanResult == NULL) {
ALOGE("Error in creating scan result");
return;
@@ -304,45 +484,209 @@
setStringField(env, scanResult, "SSID", results[i].ssid);
char bssid[32];
- sprintf(bssid, "%0x:%0x:%0x:%0x:%0x:%0x", results[i].bssid[0], results[i].bssid[1],
+ sprintf(bssid, "%02x:%02x:%02x:%02x:%02x:%02x", results[i].bssid[0], results[i].bssid[1],
results[i].bssid[2], results[i].bssid[3], results[i].bssid[4], results[i].bssid[5]);
setStringField(env, scanResult, "BSSID", bssid);
setIntField(env, scanResult, "level", results[i].rssi);
- setLongField(env, scanResult, "timestamp", results[i].ts);
setIntField(env, scanResult, "frequency", results[i].channel);
+ setLongField(env, scanResult, "timestamp", results[i].ts);
+
+ env->SetObjectArrayElement(scanResults, i, scanResult);
+
+ ALOGD("Found AP %32s %s", results[i].ssid, bssid);
+ }
+
+ reportEvent(env, mObj, "onHotlistApFound", "(I[Landroid/net/wifi/ScanResult;)V",
+ id, scanResults);
+}
+
+static jboolean android_net_wifi_setHotlist(
+ JNIEnv *env, jobject obj, jint iface, jint id, jobject ap) {
+
+ wifi_interface_handle handle = getIfaceHandle(env, obj, iface);
+ ALOGD("setting hotlist on interface[%d] = %p", iface, handle);
+
+ wifi_bssid_hotlist_params params;
+ memset(¶ms, 0, sizeof(params));
+
+ jobjectArray array = (jobjectArray) getObjectField(env, ap,
+ "hotspotInfos", "[Landroid/net/wifi/WifiScanner$HotspotInfo;");
+ params.num = env->GetArrayLength(array);
+
+ if (params.num == 0) {
+ ALOGE("Error in accesing array");
+ return false;
+ }
+
+ for (int i = 0; i < params.num; i++) {
+ jobject objAp = env->GetObjectArrayElement(array, i);
+
+ jstring macAddrString = (jstring) getObjectField(
+ env, objAp, "bssid", "Ljava/lang/String;");
+ if (macAddrString == NULL) {
+ ALOGE("Error getting bssid field");
+ return false;
+ }
+
+ const char *bssid = env->GetStringUTFChars(macAddrString, NULL);
+ if (bssid == NULL) {
+ ALOGE("Error getting bssid");
+ return false;
+ }
+ parseMacAddress(bssid, params.bssids[i].bssid);
+
+ mac_addr addr;
+ memcpy(addr, params.bssids[i].bssid, sizeof(mac_addr));
+
+ char bssidOut[32];
+ sprintf(bssidOut, "%0x:%0x:%0x:%0x:%0x:%0x", addr[0], addr[1],
+ addr[2], addr[3], addr[4], addr[5]);
+
+ ALOGD("Added bssid %s", bssidOut);
+
+ params.bssids[i].low = getIntField(env, objAp, "low");
+ params.bssids[i].high = getIntField(env, objAp, "high");
+ }
+
+ wifi_hotlist_ap_found_handler handler;
+ memset(&handler, 0, sizeof(handler));
+
+ handler.on_hotlist_ap_found = &onHotlistApFound;
+ return wifi_set_bssid_hotlist(id, handle, params, handler) == WIFI_SUCCESS;
+}
+
+static jboolean android_net_wifi_resetHotlist(
+ JNIEnv *env, jobject obj, jint iface, jint id) {
+
+ wifi_interface_handle handle = getIfaceHandle(env, obj, iface);
+ ALOGD("resetting hotlist on interface[%d] = %p", iface, handle);
+
+ return wifi_reset_bssid_hotlist(id, handle) == WIFI_SUCCESS;
+}
+
+void onSignificantWifiChange(wifi_request_id id, unsigned num_results, wifi_scan_result *results) {
+ JNIEnv *env = NULL;
+ mVM->AttachCurrentThread(&env, NULL);
+
+ ALOGD("onSignificantWifiChange called, vm = %p, obj = %p, env = %p", mVM, mObj, env);
+
+ jclass clsScanResult = (env)->FindClass("android/net/wifi/ScanResult");
+ if (clsScanResult == NULL) {
+ ALOGE("Error in accessing class");
+ return;
+ }
+
+ jobjectArray scanResults = env->NewObjectArray(num_results, clsScanResult, NULL);
+ if (scanResults == NULL) {
+ ALOGE("Error in allocating array");
+ return;
+ }
+
+ for (unsigned i = 0; i < num_results; i++) {
+
+ jobject scanResult = createObject(env, "android/net/wifi/ScanResult");
+ if (scanResult == NULL) {
+ ALOGE("Error in creating scan result");
+ return;
+ }
+
+ setStringField(env, scanResult, "SSID", results[i].ssid);
+
+ char bssid[32];
+ sprintf(bssid, "%02x:%02x:%02x:%02x:%02x:%02x", results[i].bssid[0], results[i].bssid[1],
+ results[i].bssid[2], results[i].bssid[3], results[i].bssid[4], results[i].bssid[5]);
+
+ setStringField(env, scanResult, "BSSID", bssid);
+
+ setIntField(env, scanResult, "level", results[i].rssi);
+ setIntField(env, scanResult, "frequency", results[i].channel);
+ setLongField(env, scanResult, "timestamp", results[i].ts);
env->SetObjectArrayElement(scanResults, i, scanResult);
}
- reportEvent(env, mObj, "onScanResults",
- "(I[Lcom/android/server/wifi/WifiNative$ScanResult;)V", id, scanResults);
- */
+ reportEvent(env, mObj, "onSignificantWifiChange", "(I[Landroid/net/wifi/ScanResult;)V",
+ id, scanResults);
+
}
-static jboolean android_net_wifi_startScan(JNIEnv *env, jobject obj, jint iface, jint id) {
+static jboolean android_net_wifi_trackSignificantWifiChange(
+ JNIEnv *env, jobject obj, jint iface, jint id, jobject settings) {
wifi_interface_handle handle = getIfaceHandle(env, obj, iface);
- ALOGD("starting scan on interface[%d] = %p", iface, handle);
+ ALOGD("tracking significant wifi change on interface[%d] = %p", iface, handle);
- wifi_scan_cmd_params params;
+ wifi_significant_change_params params;
memset(¶ms, 0, sizeof(params));
- wifi_scan_result_handler handler;
+ params.rssi_sample_size = getIntField(env, settings, "rssiSampleSize");
+ params.lost_ap_sample_size = getIntField(env, settings, "lostApSampleSize");
+ params.min_breaching = getIntField(env, settings, "minApsBreachingThreshold");
+
+ const char *hotspot_info_array_type = "[Landroid/net/wifi/WifiScanner$HotspotInfo;";
+ jobjectArray hotspots = (jobjectArray)getObjectField(
+ env, settings, "hotspotInfos", hotspot_info_array_type);
+ params.num = env->GetArrayLength(hotspots);
+
+ if (params.num == 0) {
+ ALOGE("Error in accesing array");
+ return false;
+ }
+
+ ALOGD("Initialized common fields %d, %d, %d, %d", params.rssi_sample_size,
+ params.lost_ap_sample_size, params.min_breaching, params.num);
+
+ for (int i = 0; i < params.num; i++) {
+ jobject objAp = env->GetObjectArrayElement(hotspots, i);
+
+ jstring macAddrString = (jstring) getObjectField(
+ env, objAp, "bssid", "Ljava/lang/String;");
+ if (macAddrString == NULL) {
+ ALOGE("Error getting bssid field");
+ return false;
+ }
+
+ const char *bssid = env->GetStringUTFChars(macAddrString, NULL);
+ if (bssid == NULL) {
+ ALOGE("Error getting bssid");
+ return false;
+ }
+
+ mac_addr addr;
+ parseMacAddress(bssid, addr);
+ memcpy(params.bssids[i].bssid, addr, sizeof(mac_addr));
+
+ char bssidOut[32];
+ sprintf(bssidOut, "%0x:%0x:%0x:%0x:%0x:%0x", addr[0], addr[1],
+ addr[2], addr[3], addr[4], addr[5]);
+
+ params.bssids[i].low = getIntField(env, objAp, "low");
+ params.bssids[i].high = getIntField(env, objAp, "high");
+
+ ALOGD("Added bssid %s, [%04d, %04d]", bssidOut, params.bssids[i].low, params.bssids[i].high);
+ }
+
+ ALOGD("Added %d bssids", params.num);
+
+ wifi_significant_change_handler handler;
memset(&handler, 0, sizeof(handler));
- handler.on_scan_results_available = &onScanResultsAvailable;
- return wifi_start_gscan(id, handle, params, handler) == WIFI_SUCCESS;
+ handler.on_significant_change = &onSignificantWifiChange;
+ return wifi_set_significant_change_handler(id, handle, params, handler) == WIFI_SUCCESS;
}
-static jboolean android_net_wifi_stopScan(JNIEnv *env, jobject obj, jint iface, jint id) {
+static jboolean android_net_wifi_untrackSignificantWifiChange(
+ JNIEnv *env, jobject obj, jint iface, jint id) {
+
wifi_interface_handle handle = getIfaceHandle(env, obj, iface);
- ALOGD("stopping scan on interface[%d] = %p", iface, handle);
+ ALOGD("resetting significant wifi change on interface[%d] = %p", iface, handle);
- return wifi_stop_gscan(id, handle);
+ return wifi_reset_significant_change_handler(id, handle) == WIFI_SUCCESS;
}
+
// ----------------------------------------------------------------------------
/*
@@ -369,8 +713,23 @@
{ "waitForHalEventNative", "()V", (void*) android_net_wifi_waitForHalEvents },
{ "getInterfacesNative", "()I", (void*) android_net_wifi_getInterfaces},
{ "getInterfaceNameNative", "(I)Ljava/lang/String;", (void*) android_net_wifi_getInterfaceName},
- { "startScanNative", "(II)Z", (void*) android_net_wifi_startScan},
- { "stopScanNative", "(II)Z", (void*) android_net_wifi_stopScan}
+ { "getScanCapabilitiesNative", "(ILcom/android/server/wifi/WifiNative$ScanCapabilities;)Z",
+ (void *) android_net_wifi_getScanCapabilities},
+ { "startScanNative", "(IILcom/android/server/wifi/WifiNative$ScanSettings;)Z",
+ (void*) android_net_wifi_startScan},
+ { "stopScanNative", "(II)Z", (void*) android_net_wifi_stopScan},
+ { "getScanResultsNative", "(IZ)[Landroid/net/wifi/ScanResult;",
+ (void *) android_net_wifi_getScanResults},
+
+ { "setHotlistNative", "(IILandroid/net/wifi/WifiScanner$HotlistSettings;)Z",
+ (void*) android_net_wifi_setHotlist},
+ { "resetHotlistNative", "(II)Z", (void*) android_net_wifi_resetHotlist},
+
+
+ { "trackSignificantWifiChangeNative", "(IILandroid/net/wifi/WifiScanner$WifiChangeSettings;)Z",
+ (void*) android_net_wifi_trackSignificantWifiChange},
+ { "untrackSignificantWifiChangeNative", "(II)Z",
+ (void*) android_net_wifi_untrackSignificantWifiChange}
};
int register_android_net_wifi_WifiNative(JNIEnv* env) {
diff --git a/service/jni/jni_helper.cpp b/service/jni/jni_helper.cpp
index 54e9023..81bf0e6 100644
--- a/service/jni/jni_helper.cpp
+++ b/service/jni/jni_helper.cpp
@@ -47,6 +47,34 @@
(env)->ThrowNew(exClass, message);
}
+jboolean getBoolField(JNIEnv *env, jobject obj, const char *name)
+{
+ jclass cls = (env)->GetObjectClass(obj);
+ jfieldID field = (env)->GetFieldID(cls, name, "Z");
+ if (field == 0) {
+ THROW(env, "Error in accessing field");
+ return 0;
+ }
+
+ jboolean value = (env)->GetBooleanField(obj, field);
+ env->DeleteLocalRef(cls);
+ return value;
+}
+
+jint getIntField(JNIEnv *env, jobject obj, const char *name)
+{
+ jclass cls = (env)->GetObjectClass(obj);
+ jfieldID field = (env)->GetFieldID(cls, name, "I");
+ if (field == 0) {
+ THROW(env, "Error in accessing field");
+ return 0;
+ }
+
+ jint value = (env)->GetIntField(obj, field);
+ env->DeleteLocalRef(cls);
+ return value;
+}
+
jlong getLongField(JNIEnv *env, jobject obj, const char *name)
{
jclass cls = (env)->GetObjectClass(obj);
@@ -57,6 +85,21 @@
}
jlong value = (env)->GetLongField(obj, field);
+ env->DeleteLocalRef(cls);
+ return value;
+}
+
+jobject getObjectField(JNIEnv *env, jobject obj, const char *name, const char *type)
+{
+ jclass cls = (env)->GetObjectClass(obj);
+ jfieldID field = (env)->GetFieldID(cls, name, type);
+ if (field == 0) {
+ THROW(env, "Error in accessing field");
+ return 0;
+ }
+
+ jobject value = (env)->GetObjectField(obj, field);
+ env->DeleteLocalRef(cls);
return value;
}
@@ -78,9 +121,34 @@
jlong value = elem[index];
(env)->ReleaseLongArrayElements(array, elem, 0);
+
+ env->DeleteLocalRef(array);
+ env->DeleteLocalRef(cls);
+
return value;
}
+jobject getObjectArrayField(JNIEnv *env, jobject obj, const char *name, const char *type, int index)
+{
+ jclass cls = (env)->GetObjectClass(obj);
+ jfieldID field = (env)->GetFieldID(cls, name, type);
+ if (field == 0) {
+ THROW(env, "Error in accessing field definition");
+ return 0;
+ }
+
+ jobjectArray array = (jobjectArray)(env)->GetObjectField(obj, field);
+ jobject elem = (env)->GetObjectArrayElement(array, index);
+ if (elem == NULL) {
+ THROW(env, "Error in accessing index element");
+ return 0;
+ }
+
+ env->DeleteLocalRef(array);
+ env->DeleteLocalRef(cls);
+ return elem;
+}
+
void setIntField(JNIEnv *env, jobject obj, const char *name, jint value)
{
jclass cls = (env)->GetObjectClass(obj);
@@ -96,6 +164,7 @@
}
(env)->SetIntField(obj, field, value);
+ env->DeleteLocalRef(cls);
}
void setLongField(JNIEnv *env, jobject obj, const char *name, jlong value)
@@ -113,6 +182,7 @@
}
(env)->SetLongField(obj, field, value);
+ env->DeleteLocalRef(cls);
}
void setLongArrayField(JNIEnv *env, jobject obj, const char *name, jlongArray value)
@@ -135,6 +205,8 @@
(env)->SetObjectField(obj, field, value);
ALOGD("array field set");
+
+ env->DeleteLocalRef(cls);
}
void setLongArrayElement(JNIEnv *env, jobject obj, const char *name, int index, jlong value)
@@ -170,7 +242,9 @@
}
elem[index] = value;
- (env)->ReleaseLongArrayElements(array, elem, 0);
+ env->ReleaseLongArrayElements(array, elem, 0);
+ env->DeleteLocalRef(array);
+ env->DeleteLocalRef(cls);
}
void setObjectField(JNIEnv *env, jobject obj, const char *name, const char *type, jobject value)
@@ -188,11 +262,11 @@
}
(env)->SetObjectField(obj, field, value);
+ env->DeleteLocalRef(cls);
}
void setStringField(JNIEnv *env, jobject obj, const char *name, const char *value)
{
-
jstring str = env->NewStringUTF(value);
if (str == NULL) {
@@ -201,6 +275,7 @@
}
setObjectField(env, obj, name, "Ljava/lang/String;", str);
+ env->DeleteLocalRef(str);
}
void reportEvent(JNIEnv *env, jobject obj, const char *method, const char *signature, ...)
@@ -222,6 +297,8 @@
env->CallVoidMethodV(obj, methodID, params);
va_end(params);
+
+ env->DeleteLocalRef(cls);
}
jobject createObject(JNIEnv *env, const char *className)
@@ -243,6 +320,7 @@
return NULL;
}
+ env->DeleteLocalRef(cls);
return obj;
}
diff --git a/service/jni/jni_helper.h b/service/jni/jni_helper.h
index e61ee0a..c2678f6 100644
--- a/service/jni/jni_helper.h
+++ b/service/jni/jni_helper.h
@@ -4,8 +4,12 @@
/* JNI Helpers for wifi_hal to WifiNative bridge implementation */
void throwException( JNIEnv *env, const char *message, int line );
+jboolean getBoolField(JNIEnv *env, jobject obj, const char *name);
+jint getIntField(JNIEnv *env, jobject obj, const char *name);
jlong getLongField(JNIEnv *env, jobject obj, const char *name);
+jobject getObjectField(JNIEnv *env, jobject obj, const char *name, const char *type);
jlong getLongArrayField(JNIEnv *env, jobject obj, const char *name, int index);
+jobject getObjectArrayField(JNIEnv *env, jobject obj, const char *name, const char *type, int index);
void setIntField(JNIEnv *env, jobject obj, const char *name, jint value);
void setLongField(JNIEnv *env, jobject obj, const char *name, jlong value);
void setLongArrayField(JNIEnv *env, jobject obj, const char *name, jlongArray value);