diff options
author | Robert Greenwalt <rgreenwalt@google.com> | 2013-08-01 18:24:13 -0700 |
---|---|---|
committer | Robert Greenwalt <rgreenwalt@google.com> | 2013-08-13 14:37:37 -0700 |
commit | 0451d59ba2d768e7653752028910f00a6c96e64e (patch) | |
tree | e9ca4d4d77074b6223ddf07d761fcd679bc1fa6c | |
parent | 7a605df3137ee571dec855761c0cb15b28513d26 (diff) | |
download | frameworks_base-0451d59ba2d768e7653752028910f00a6c96e64e.zip frameworks_base-0451d59ba2d768e7653752028910f00a6c96e64e.tar.gz frameworks_base-0451d59ba2d768e7653752028910f00a6c96e64e.tar.bz2 |
Add support for batched wifi scans.
bug:9301872
Change-Id: I5a7edfdbd2b78a65119d11acad491eae350c0870
-rw-r--r-- | Android.mk | 2 | ||||
-rw-r--r-- | core/res/res/values/config.xml | 3 | ||||
-rwxr-xr-x | core/res/res/values/symbols.xml | 3 | ||||
-rw-r--r-- | services/java/com/android/server/wifi/WifiService.java | 164 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/BatchedScanResult.aidl | 19 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/BatchedScanResult.java | 94 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/BatchedScanSettings.aidl | 19 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/BatchedScanSettings.java | 226 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/IWifiManager.aidl | 12 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/ScanResult.java | 49 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/WifiManager.java | 62 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/WifiNative.java | 34 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/WifiStateMachine.java | 337 |
13 files changed, 994 insertions, 30 deletions
@@ -384,6 +384,8 @@ aidl_files := \ frameworks/base/telephony/java/android/telephony/ServiceState.aidl \ frameworks/base/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \ frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl \ + frameworks/base/wifi/java/android/net/wifi/BatchedScanSettings.aidl \ + frameworks/base/wifi/java/android/net/wifi/BatchedScanResult.aidl \ gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/framework.aidl $(gen): PRIVATE_SRC_FILES := $(aidl_files) diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index b9840e2..8af360c 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -353,6 +353,9 @@ Default value is 2 minutes. --> <integer translatable="false" name="config_wifi_driver_stop_delay">120000</integer> + <!-- Wifi driver supports batched scan --> + <bool translatable="false" name="config_wifi_batched_scan_supported">false</bool> + <!-- Flag indicating whether the we should enable the automatic brightness in Settings. Software implementation will be used if config_hardware_auto_brightness_available is not set --> <bool name="config_automatic_brightness_available">false</bool> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 0bfed1b..634913c 100755 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -281,7 +281,8 @@ <java-symbol type="bool" name="config_speed_up_audio_on_mt_calls" /> <java-symbol type="bool" name="config_useFixedVolume" /> <java-symbol type="bool" name="config_forceDefaultOrientation" /> - + <java-symbol type="bool" name="config_wifi_batched_scan_supported" /> + <java-symbol type="integer" name="config_cursorWindowSize" /> <java-symbol type="integer" name="config_extraFreeKbytesAdjust" /> <java-symbol type="integer" name="config_extraFreeKbytesAbsolute" /> diff --git a/services/java/com/android/server/wifi/WifiService.java b/services/java/com/android/server/wifi/WifiService.java index 6e0e055..db030f1 100644 --- a/services/java/com/android/server/wifi/WifiService.java +++ b/services/java/com/android/server/wifi/WifiService.java @@ -25,18 +25,20 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.database.ContentObserver; +import android.net.DhcpInfo; +import android.net.DhcpResults; +import android.net.LinkAddress; +import android.net.NetworkUtils; +import android.net.RouteInfo; import android.net.wifi.IWifiManager; import android.net.wifi.ScanResult; +import android.net.wifi.BatchedScanResult; +import android.net.wifi.BatchedScanSettings; +import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiStateMachine; -import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiWatchdogStateMachine; -import android.net.DhcpInfo; -import android.net.DhcpResults; -import android.net.LinkAddress; -import android.net.NetworkUtils; -import android.net.RouteInfo; import android.os.Binder; import android.os.Handler; import android.os.Messenger; @@ -63,6 +65,7 @@ import java.io.PrintWriter; import java.net.InetAddress; import java.net.Inet4Address; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -121,6 +124,8 @@ public final class WifiService extends IWifiManager.Stub { /* Tracks the persisted states for wi-fi & airplane mode */ final WifiSettingsStore mSettingsStore; + final boolean mBatchedScanSupported; + /** * Asynchronous channel to WifiStateMachine */ @@ -246,6 +251,9 @@ public final class WifiService extends IWifiManager.Stub { mWifiController = new WifiController(mContext, this, wifiThread.getLooper()); mWifiController.start(); + mBatchedScanSupported = mContext.getResources().getBoolean( + R.bool.config_wifi_batched_scan_supported); + registerForScanModeChange(); mContext.registerReceiver( new BroadcastReceiver() { @@ -314,6 +322,142 @@ public final class WifiService extends IWifiManager.Stub { mWifiStateMachine.startScan(Binder.getCallingUid(), workSource); } + private class BatchedScanRequest extends DeathRecipient { + BatchedScanSettings settings; + int uid; + + BatchedScanRequest(BatchedScanSettings settings, IBinder binder, int uid) { + super(0, null, binder, null); + this.settings = settings; + this.uid = uid; + } + public void binderDied() { + stopBatchedScan(settings, mBinder); + } + public String toString() { + return "BatchedScanRequest{settings=" + settings + ", binder=" + mBinder + "}"; + } + } + + private final List<BatchedScanRequest> mBatchedScanners = new ArrayList<BatchedScanRequest>(); + + public boolean isBatchedScanSupported() { + return mBatchedScanSupported; + } + + /** + * see {@link android.net.wifi.WifiManager#requestBatchedScan()} + */ + public boolean requestBatchedScan(BatchedScanSettings requested, IBinder binder) { + enforceChangePermission(); + if (mBatchedScanSupported == false) return false; + requested = new BatchedScanSettings(requested); + if (requested.isInvalid()) return false; + BatchedScanRequest r = new BatchedScanRequest(requested, binder, Binder.getCallingUid()); + synchronized(mBatchedScanners) { + mBatchedScanners.add(r); + resolveBatchedScannersLocked(); + } + return true; + } + + public List<BatchedScanResult> getBatchedScanResults(String callingPackage) { + enforceAccessPermission(); + if (mBatchedScanSupported == false) return new ArrayList<BatchedScanResult>(); + int userId = UserHandle.getCallingUserId(); + int uid = Binder.getCallingUid(); + long ident = Binder.clearCallingIdentity(); + try { + if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage) + != AppOpsManager.MODE_ALLOWED) { + return new ArrayList<BatchedScanResult>(); + } + int currentUser = ActivityManager.getCurrentUser(); + if (userId != currentUser) { + return new ArrayList<BatchedScanResult>(); + } else { + return mWifiStateMachine.syncGetBatchedScanResultsList(); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + + public void stopBatchedScan(BatchedScanSettings settings, IBinder binder) { + enforceChangePermission(); + if (mBatchedScanSupported == false) return; + synchronized(mBatchedScanners) { + BatchedScanRequest found = null; + for (BatchedScanRequest r : mBatchedScanners) { + if (r.mBinder.equals(binder) && r.settings.equals(settings)) { + found = r; + break; + } + } + if (found != null) { + mBatchedScanners.remove(found); + resolveBatchedScannersLocked(); + } + } + } + + private void resolveBatchedScannersLocked() { + BatchedScanSettings setting = new BatchedScanSettings(); + setting.scanIntervalSec = BatchedScanSettings.DEFAULT_INTERVAL_SEC; + int responsibleUid = 0; + setting.channelSet = new ArrayList<String>(); + + if (mBatchedScanners.size() == 0) { + mWifiStateMachine.setBatchedScanSettings(null, 0); + return; + } + + for (BatchedScanRequest r : mBatchedScanners) { + BatchedScanSettings s = r.settings; + if (s.maxScansPerBatch != BatchedScanSettings.UNSPECIFIED && + s.maxScansPerBatch < setting.maxScansPerBatch) { + setting.maxScansPerBatch = s.maxScansPerBatch; + responsibleUid = r.uid; + } + if (s.maxApPerScan != BatchedScanSettings.UNSPECIFIED && + s.maxApPerScan > setting.maxApPerScan) { + setting.maxApPerScan = s.maxApPerScan; + } + if (s.scanIntervalSec != BatchedScanSettings.UNSPECIFIED && + s.scanIntervalSec < setting.scanIntervalSec) { + setting.scanIntervalSec = s.scanIntervalSec; + responsibleUid = r.uid; + } + if (s.maxApForDistance != BatchedScanSettings.UNSPECIFIED && + s.maxApForDistance > setting.maxApForDistance) { + setting.maxApForDistance = s.maxApForDistance; + } + if (s.channelSet != null) { + for (String i : s.channelSet) { + if (setting.channelSet.contains(i) == false) setting.channelSet.add(i); + } + } + } + if (setting.channelSet.size() == 0) setting.channelSet = null; + if (setting.scanIntervalSec < BatchedScanSettings.MIN_INTERVAL_SEC) { + setting.scanIntervalSec = BatchedScanSettings.MIN_INTERVAL_SEC; + } + if (setting.maxScansPerBatch == BatchedScanSettings.UNSPECIFIED) { + setting.maxScansPerBatch = BatchedScanSettings.DEFAULT_SCANS_PER_BATCH; + } + if (setting.maxApPerScan == BatchedScanSettings.UNSPECIFIED) { + setting.maxApPerScan = BatchedScanSettings.DEFAULT_AP_PER_SCAN; + } + if (setting.scanIntervalSec == BatchedScanSettings.UNSPECIFIED) { + setting.scanIntervalSec = BatchedScanSettings.DEFAULT_INTERVAL_SEC; + } + if (setting.maxApForDistance == BatchedScanSettings.UNSPECIFIED) { + setting.maxApForDistance = BatchedScanSettings.DEFAULT_AP_FOR_DISTANCE; + } + mWifiStateMachine.setBatchedScanSettings(setting, responsibleUid); + } + private void enforceAccessPermission() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, "WifiService"); @@ -569,11 +713,11 @@ public final class WifiService extends IWifiManager.Stub { int userId = UserHandle.getCallingUserId(); int uid = Binder.getCallingUid(); long ident = Binder.clearCallingIdentity(); - if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage) - != AppOpsManager.MODE_ALLOWED) { - return new ArrayList<ScanResult>(); - } try { + if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage) + != AppOpsManager.MODE_ALLOWED) { + return new ArrayList<ScanResult>(); + } int currentUser = ActivityManager.getCurrentUser(); if (userId != currentUser) { return new ArrayList<ScanResult>(); diff --git a/wifi/java/android/net/wifi/BatchedScanResult.aidl b/wifi/java/android/net/wifi/BatchedScanResult.aidl new file mode 100644 index 0000000..a70bc0a --- /dev/null +++ b/wifi/java/android/net/wifi/BatchedScanResult.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2013, 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; + +parcelable BatchedScanResult; diff --git a/wifi/java/android/net/wifi/BatchedScanResult.java b/wifi/java/android/net/wifi/BatchedScanResult.java new file mode 100644 index 0000000..eb4e027 --- /dev/null +++ b/wifi/java/android/net/wifi/BatchedScanResult.java @@ -0,0 +1,94 @@ +/* + * 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.os.Parcelable; +import android.os.Parcel; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Describes the Results of a batched set of wifi scans where the firmware performs many + * scans and stores the timestamped results without waking the main processor each time. + * @hide pending review + */ +public class BatchedScanResult implements Parcelable { + private static final String TAG = "BatchedScanResult"; + + /** Inidcates this scan was interrupted and may only have partial results. */ + public boolean truncated; + + /** The result of this particular scan. */ + public final List<ScanResult> scanResults = new ArrayList<ScanResult>(); + + + public BatchedScanResult() { + } + + public BatchedScanResult(BatchedScanResult source) { + truncated = source.truncated; + for (ScanResult s : source.scanResults) scanResults.add(new ScanResult(s)); + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + + sb.append("BatchedScanResult: "). + append("truncated: ").append(String.valueOf(truncated)). + append("scanResults: ["); + for (ScanResult s : scanResults) { + sb.append(" <").append(s.toString()).append("> "); + } + sb.append(" ]"); + return sb.toString(); + } + + /** Implement the Parcelable interface {@hide} */ + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface {@hide} */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(truncated ? 1 : 0); + dest.writeInt(scanResults.size()); + for (ScanResult s : scanResults) { + s.writeToParcel(dest, flags); + } + } + + /** Implement the Parcelable interface {@hide} */ + public static final Creator<BatchedScanResult> CREATOR = + new Creator<BatchedScanResult>() { + public BatchedScanResult createFromParcel(Parcel in) { + BatchedScanResult result = new BatchedScanResult(); + result.truncated = (in.readInt() == 1); + int count = in.readInt(); + while (count-- > 0) { + result.scanResults.add(ScanResult.CREATOR.createFromParcel(in)); + } + return result; + } + + public BatchedScanResult[] newArray(int size) { + return new BatchedScanResult[size]; + } + }; +} diff --git a/wifi/java/android/net/wifi/BatchedScanSettings.aidl b/wifi/java/android/net/wifi/BatchedScanSettings.aidl new file mode 100644 index 0000000..8cfc508 --- /dev/null +++ b/wifi/java/android/net/wifi/BatchedScanSettings.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2013, 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; + +parcelable BatchedScanSettings; diff --git a/wifi/java/android/net/wifi/BatchedScanSettings.java b/wifi/java/android/net/wifi/BatchedScanSettings.java new file mode 100644 index 0000000..82945d6 --- /dev/null +++ b/wifi/java/android/net/wifi/BatchedScanSettings.java @@ -0,0 +1,226 @@ +/* + * 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.os.Parcelable; +import android.os.Parcel; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * Describes the settings for batched wifi scans where the firmware performs many + * scans and stores the timestamped results without waking the main processor each time. + * This can give information over time with minimal battery impact. + * @hide pending review + */ +public class BatchedScanSettings implements Parcelable { + private static final String TAG = "BatchedScanSettings"; + + /** Used to indicate no preference for an int value */ + public final static int UNSPECIFIED = Integer.MAX_VALUE; + + // TODO - make MIN/mAX dynamic and gservices adjustable. + public final static int MIN_SCANS_PER_BATCH = 2; + public final static int MAX_SCANS_PER_BATCH = 255; + public final static int DEFAULT_SCANS_PER_BATCH = MAX_SCANS_PER_BATCH; + + public final static int MIN_AP_PER_SCAN = 2; + public final static int MAX_AP_PER_SCAN = 255; + public final static int DEFAULT_AP_PER_SCAN = 16; + + public final static int MIN_INTERVAL_SEC = 0; + public final static int MAX_INTERVAL_SEC = 3600; + public final static int DEFAULT_INTERVAL_SEC = 30; + + public final static int MIN_AP_FOR_DISTANCE = 0; + public final static int MAX_AP_FOR_DISTANCE = MAX_AP_PER_SCAN; + public final static int DEFAULT_AP_FOR_DISTANCE = 0; + + + /** The expected number of scans per batch. Note that the firmware may drop scans + * leading to fewer scans during the normal batch scan duration. This value need not + * be specified (may be set to {@link UNSPECIFIED}) by the application and we will try + * to scan as many times as the firmware can support. If another app requests fewer + * scans per batch we will attempt to honor that. + */ + public int maxScansPerBatch; + + /** The maximum desired AP listed per scan. Fewer AP may be returned if that's all + * that the driver detected. If another application requests more AP per scan that + * will take precedence. The if more channels are detected than we request, the APs + * with the lowest signal strength will be dropped. + */ + public int maxApPerScan; + + /** The channels used in the scan. If all channels should be used, {@code null} may be + * specified. If another application requests more channels or all channels, that + * will take precedence. + */ + public Collection<String> channelSet; + + /** The time between the start of two sequential scans, in seconds. If another + * application requests more frequent scans, that will take precedence. If this + * value is less than the duration of a scan, the next scan should start immediately. + */ + public int scanIntervalSec; + + /** The number of the best (strongest signal) APs for which the firmware will + * attempt to get distance information (RTT). Not all firmware supports this + * feature, so it may be ignored. If another application requests a greater + * number, that will take precedence. + */ + public int maxApForDistance; + + public BatchedScanSettings() { + clear(); + } + + public void clear() { + maxScansPerBatch = UNSPECIFIED; + maxApPerScan = UNSPECIFIED; + channelSet = null; + scanIntervalSec = UNSPECIFIED; + maxApForDistance = UNSPECIFIED; + } + + public BatchedScanSettings(BatchedScanSettings source) { + maxScansPerBatch = source.maxScansPerBatch; + maxApPerScan = source.maxApPerScan; + if (source.channelSet != null) { + channelSet = new ArrayList(source.channelSet); + } + scanIntervalSec = source.scanIntervalSec; + maxApForDistance = source.maxApForDistance; + } + + private boolean channelSetIsValid() { + if (channelSet == null || channelSet.isEmpty()) return true; + for (String channel : channelSet) { + try { + int i = Integer.parseInt(channel); + if (i > 0 && i < 197) continue; + } catch (NumberFormatException e) {} + if (channel.equals("A") || channel.equals("B")) continue; + return false; + } + return true; + } + /** @hide */ + public boolean isInvalid() { + if (maxScansPerBatch != UNSPECIFIED && (maxScansPerBatch < MIN_SCANS_PER_BATCH || + maxScansPerBatch > MAX_SCANS_PER_BATCH)) return true; + if (maxApPerScan != UNSPECIFIED && (maxApPerScan < MIN_AP_PER_SCAN || + maxApPerScan > MAX_AP_PER_SCAN)) return true; + if (channelSetIsValid() == false) return true; + if (scanIntervalSec != UNSPECIFIED && (scanIntervalSec < MIN_INTERVAL_SEC || + scanIntervalSec > MAX_INTERVAL_SEC)) return true; + if (maxApForDistance != UNSPECIFIED && (maxApForDistance < MIN_AP_FOR_DISTANCE || + maxApForDistance > MAX_AP_FOR_DISTANCE)) return true; + return false; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof BatchedScanSettings == false) return false; + BatchedScanSettings o = (BatchedScanSettings)obj; + if (maxScansPerBatch != o.maxScansPerBatch || + maxApPerScan != o.maxApPerScan || + scanIntervalSec != o.scanIntervalSec || + maxApForDistance != o.maxApForDistance) return false; + if (channelSet == null) { + return (o.channelSet == null); + } + return channelSet.equals(o.channelSet); + } + + @Override + public int hashCode() { + return maxScansPerBatch + + (maxApPerScan * 3) + + (scanIntervalSec * 5) + + (maxApForDistance * 7) + + (channelSet.hashCode() * 11); + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + String none = "<none>"; + + sb.append("BatchScanSettings [maxScansPerBatch: "). + append(maxScansPerBatch == UNSPECIFIED ? none : maxScansPerBatch). + append(", maxApPerScan: ").append(maxApPerScan == UNSPECIFIED? none : maxApPerScan). + append(", scanIntervalSec: "). + append(scanIntervalSec == UNSPECIFIED ? none : scanIntervalSec). + append(", maxApForDistance: "). + append(maxApForDistance == UNSPECIFIED ? none : maxApForDistance). + append(", channelSet: "); + if (channelSet == null) { + sb.append("ALL"); + } else { + sb.append("<"); + for (String channel : channelSet) { + sb.append(" " + channel); + } + sb.append(">"); + } + sb.append("]"); + return sb.toString(); + } + + /** Implement the Parcelable interface {@hide} */ + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface {@hide} */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(maxScansPerBatch); + dest.writeInt(maxApPerScan); + dest.writeInt(scanIntervalSec); + dest.writeInt(maxApForDistance); + dest.writeInt(channelSet == null ? 0 : channelSet.size()); + if (channelSet != null) { + for (String channel : channelSet) dest.writeString(channel); + } + } + + /** Implement the Parcelable interface {@hide} */ + public static final Creator<BatchedScanSettings> CREATOR = + new Creator<BatchedScanSettings>() { + public BatchedScanSettings createFromParcel(Parcel in) { + BatchedScanSettings settings = new BatchedScanSettings(); + settings.maxScansPerBatch = in.readInt(); + settings.maxApPerScan = in.readInt(); + settings.scanIntervalSec = in.readInt(); + settings.maxApForDistance = in.readInt(); + int channelCount = in.readInt(); + if (channelCount > 0) { + settings.channelSet = new ArrayList(channelCount); + while (channelCount-- > 0) { + settings.channelSet.add(in.readString()); + } + } + return settings; + } + + public BatchedScanSettings[] newArray(int size) { + return new BatchedScanSettings[size]; + } + }; +} diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 8103e84..c8cf323 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -16,8 +16,10 @@ package android.net.wifi; -import android.net.wifi.WifiInfo; +import android.net.wifi.BatchedScanResult; +import android.net.wifi.BatchedScanSettings; import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiInfo; import android.net.wifi.ScanResult; import android.net.DhcpInfo; @@ -114,5 +116,13 @@ interface IWifiManager void enableTdls(String remoteIPAddress, boolean enable); void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable); + + boolean requestBatchedScan(in BatchedScanSettings requested, IBinder binder); + + void stopBatchedScan(in BatchedScanSettings requested, IBinder binder); + + List<BatchedScanResult> getBatchedScanResults(String callingPackage); + + boolean isBatchedScanSupported(); } diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java index 9977419..12729d2 100644 --- a/wifi/java/android/net/wifi/ScanResult.java +++ b/wifi/java/android/net/wifi/ScanResult.java @@ -54,7 +54,26 @@ public class ScanResult implements Parcelable { * Time Synchronization Function (tsf) timestamp in microseconds when * this result was last seen. */ - public long timestamp; + public long timestamp; + + /** + * The approximate distance to the AP in centimeter, if available. Else + * {@link UNSPECIFIED}. + * {@hide} + */ + public int distanceCm; + + /** + * The standard deviation of the distance to the AP, if available. + * Else {@link UNSPECIFIED}. + * {@hide} + */ + public int distanceSdCm; + + /** + * {@hide} + */ + public final static int UNSPECIFIED = -1; /** {@hide} */ public ScanResult(WifiSsid wifiSsid, String BSSID, String caps, int level, int frequency, @@ -66,8 +85,23 @@ public class ScanResult implements Parcelable { this.level = level; this.frequency = frequency; this.timestamp = tsf; + this.distanceCm = UNSPECIFIED; + this.distanceSdCm = UNSPECIFIED; } + /** {@hide} */ + public ScanResult(WifiSsid wifiSsid, String BSSID, String caps, int level, int frequency, + long tsf, int distCm, int distSdCm) { + this.wifiSsid = wifiSsid; + this.SSID = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE; + this.BSSID = BSSID; + this.capabilities = caps; + this.level = level; + this.frequency = frequency; + this.timestamp = tsf; + this.distanceCm = distCm; + this.distanceSdCm = distSdCm; + } /** copy constructor {@hide} */ public ScanResult(ScanResult source) { @@ -79,6 +113,8 @@ public class ScanResult implements Parcelable { level = source.level; frequency = source.frequency; timestamp = source.timestamp; + distanceCm = source.distanceCm; + distanceSdCm = source.distanceSdCm; } } @@ -100,6 +136,11 @@ public class ScanResult implements Parcelable { append(", timestamp: "). append(timestamp); + sb.append(", distance: ").append((distanceCm != UNSPECIFIED ? distanceCm : "?")). + append("(cm)"); + sb.append(", distanceSd: ").append((distanceSdCm != UNSPECIFIED ? distanceSdCm : "?")). + append("(cm)"); + return sb.toString(); } @@ -121,6 +162,8 @@ public class ScanResult implements Parcelable { dest.writeInt(level); dest.writeInt(frequency); dest.writeLong(timestamp); + dest.writeInt(distanceCm); + dest.writeInt(distanceSdCm); } /** Implement the Parcelable interface {@hide} */ @@ -137,7 +180,9 @@ public class ScanResult implements Parcelable { in.readString(), in.readInt(), in.readInt(), - in.readLong() + in.readLong(), + in.readInt(), + in.readInt() ); } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 6793710..01ca378 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -35,6 +35,7 @@ import android.util.SparseArray; import java.net.InetAddress; import java.util.concurrent.CountDownLatch; +import com.android.internal.R; import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; @@ -365,6 +366,14 @@ public class WifiManager { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS"; /** + * A batch of access point scans has been completed and the results areavailable. + * Call {@link #getBatchedScanResults()} to obtain the results. + * @hide pending review + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String BATCHED_SCAN_RESULTS_AVAILABLE_ACTION = + "android.net.wifi.BATCHED_RESULTS"; + /** * The RSSI (signal strength) has changed. * @see #EXTRA_NEW_RSSI */ @@ -778,6 +787,59 @@ public class WifiManager { } /** + * Request a batched scan for access points. To end your requested batched scan, + * call stopBatchedScan with the same Settings. + * + * If there are mulitple requests for batched scans, the more demanding settings will + * take precidence. + * + * @param requested {@link BatchedScanSettings} the scan settings requested. + * @return false on known error + * @hide + */ + public boolean requestBatchedScan(BatchedScanSettings requested) { + try { + return mService.requestBatchedScan(requested, new Binder()); + } catch (RemoteException e) { return false; } + } + + /** + * Check if the Batched Scan feature is supported. + * + * @return false if not supported. + * @hide + */ + public boolean isBatchedScanSupported() { + try { + return mService.isBatchedScanSupported(); + } catch (RemoteException e) { return false; } + } + + /** + * End a requested batch scan for this applicaiton. Note that batched scan may + * still occur if other apps are using them. + * @hide + */ + public void stopBatchedScan(BatchedScanSettings requested) { + try { + mService.stopBatchedScan(requested, new Binder()); + } catch (RemoteException e) {} + } + + /** + * Retrieve the latest batched scan result. This should be called immediately after + * {@link BATCHED_SCAN_RESULTS_AVAILABLE_ACTION} is received. + * @hide + */ + public List<BatchedScanResult> getBatchedScanResults() { + try { + return mService.getBatchedScanResults(mContext.getBasePackageName()); + } catch (RemoteException e) { + return null; + } + } + + /** * Return dynamic information about the current Wi-Fi connection, if any is active. * @return the Wi-Fi information, contained in {@link WifiInfo}. */ diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java index b1dd2ce..3dae225 100644 --- a/wifi/java/android/net/wifi/WifiNative.java +++ b/wifi/java/android/net/wifi/WifiNative.java @@ -216,6 +216,40 @@ public class WifiNative { return doStringCommand("BSS RANGE=" + sid + "- MASK=0x21987"); } + /** + * Format of command + * DRIVER WLS_BATCHING SET SCAN_FRQ=x BESTN=y CHANNEL=<z, w, t> RTT=s + * where x is an ascii representation of an integer number of seconds between scans + * y is an ascii representation of an integer number of the max AP to remember per scan + * z, w, t represent a 1..n size list of channel numbers and/or 'A', 'B' values + * indicating entire ranges of channels + * s is an ascii representation of an integer number of highest-strength AP + * for which we'd like approximate distance reported + * + * The return value is an ascii integer representing a guess of the number of scans + * the firmware can remember before it runs out of buffer space or -1 on error + */ + public String setBatchedScanSettings(BatchedScanSettings settings) { + if (settings == null) return doStringCommand("DRIVER WLS_BATCHING STOP"); + String cmd = "DRIVER WLS_BATCHING SET SCAN_FRQ=" + settings.scanIntervalSec; + if (settings.maxApPerScan != BatchedScanSettings.UNSPECIFIED) { + cmd += " BESTN " + settings.maxApPerScan; + } + if (settings.channelSet != null && !settings.channelSet.isEmpty()) { + cmd += " CHANNEL=<"; + for (String channel : settings.channelSet) cmd += " " + channel; + cmd += ">"; + } + if (settings.maxApForDistance != BatchedScanSettings.UNSPECIFIED) { + cmd += " RTT=" + settings.maxApForDistance; + } + return doStringCommand(cmd); + } + + public String getBatchedScanResults() { + return doStringCommand("DRIVER WLS_BATCHING GET"); + } + public boolean startDriver() { return doBooleanCommand("DRIVER START"); } diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index 4628c91..9815fa2 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -122,6 +122,11 @@ public class WifiStateMachine extends StateMachine { private static final int SCAN_RESULT_CACHE_SIZE = 80; private final LruCache<String, ScanResult> mScanResultCache; + /* Batch scan results */ + private final List<BatchedScanResult> mBatchedScanResults = + new ArrayList<BatchedScanResult>(); + private int mBatchedScanOwnerUid = UNKNOWN_SCAN_SOURCE; + /* Chipset supports background scan */ private final boolean mBackgroundScanSupported; @@ -210,6 +215,7 @@ public class WifiStateMachine extends StateMachine { private AlarmManager mAlarmManager; private PendingIntent mScanIntent; private PendingIntent mDriverStopIntent; + private PendingIntent mBatchedScanIntervalIntent; /* Tracks current frequency mode */ private AtomicInteger mFrequencyBand = new AtomicInteger(WifiManager.WIFI_FREQUENCY_BAND_AUTO); @@ -355,6 +361,13 @@ public class WifiStateMachine extends StateMachine { public static final int CMD_BOOT_COMPLETED = BASE + 134; + /* change the batch scan settings. + * arg1 = responsible UID + * obj = the new settings + */ + public static final int CMD_SET_BATCH_SCAN = BASE + 135; + public static final int CMD_START_NEXT_BATCHED_SCAN = BASE + 136; + public static final int CONNECT_MODE = 1; public static final int SCAN_ONLY_MODE = 2; public static final int SCAN_ONLY_WITH_WIFI_OFF_MODE = 3; @@ -519,6 +532,8 @@ public class WifiStateMachine extends StateMachine { private static final String ACTION_DELAYED_DRIVER_STOP = "com.android.server.WifiManager.action.DELAYED_DRIVER_STOP"; + private static final String ACTION_REFRESH_BATCHED_SCAN = + "com.android.server.WifiManager.action.REFRESH_BATCHED_SCAN"; /** * Keep track of whether WIFI is running. */ @@ -541,6 +556,9 @@ public class WifiStateMachine extends StateMachine { private final IBatteryStats mBatteryStats; + private BatchedScanSettings mBatchedScanSettings = null; + + public WifiStateMachine(Context context, String wlanInterface) { super("WifiStateMachine"); @@ -577,6 +595,9 @@ public class WifiStateMachine extends StateMachine { Intent scanIntent = new Intent(ACTION_START_SCAN, null); mScanIntent = PendingIntent.getBroadcast(mContext, SCAN_REQUEST, scanIntent, 0); + Intent batchedIntent = new Intent(ACTION_REFRESH_BATCHED_SCAN, null); + mBatchedScanIntervalIntent = PendingIntent.getBroadcast(mContext, 0, batchedIntent, 0); + mDefaultFrameworkScanIntervalMs = mContext.getResources().getInteger( R.integer.config_wifi_framework_scan_interval); @@ -614,22 +635,25 @@ public class WifiStateMachine extends StateMachine { }, new IntentFilter(ACTION_START_SCAN)); - IntentFilter screenFilter = new IntentFilter(); - screenFilter.addAction(Intent.ACTION_SCREEN_ON); - screenFilter.addAction(Intent.ACTION_SCREEN_OFF); - BroadcastReceiver screenReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - - if (action.equals(Intent.ACTION_SCREEN_ON)) { - handleScreenStateChanged(true); - } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { - handleScreenStateChanged(false); - } - } - }; - mContext.registerReceiver(screenReceiver, screenFilter); + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_SCREEN_ON); + filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(ACTION_REFRESH_BATCHED_SCAN); + mContext.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + + if (action.equals(Intent.ACTION_SCREEN_ON)) { + handleScreenStateChanged(true); + } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { + handleScreenStateChanged(false); + } else if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) { + startNextBatchedScanAsync(); + } + } + }, filter); mContext.registerReceiver( new BroadcastReceiver() { @@ -738,6 +762,269 @@ public class WifiStateMachine extends StateMachine { sendMessage(CMD_START_SCAN, callingUid, 0, workSource); } + /** + * start or stop batched scanning using the given settings + */ + public void setBatchedScanSettings(BatchedScanSettings settings, int callingUid) { + sendMessage(CMD_SET_BATCH_SCAN, callingUid, 0, settings); + } + + public List<BatchedScanResult> syncGetBatchedScanResultsList() { + synchronized (mBatchedScanResults) { + List<BatchedScanResult> batchedScanList = + new ArrayList<BatchedScanResult>(mBatchedScanResults.size()); + for(BatchedScanResult result: mBatchedScanResults) { + batchedScanList.add(new BatchedScanResult(result)); + } + return batchedScanList; + } + } + + private void startBatchedScan() { + // first grab any existing data + retrieveBatchedScanData(); + + mAlarmManager.cancel(mBatchedScanIntervalIntent); + + String scansExpected = mWifiNative.setBatchedScanSettings(mBatchedScanSettings); + + try { + int expected = Integer.parseInt(scansExpected); + setNextBatchedAlarm(expected); + } catch (NumberFormatException e) { + loge("Exception parsing WifiNative.setBatchedScanSettings response " + e); + } + } + + // called from BroadcastListener + private void startNextBatchedScanAsync() { + sendMessage(CMD_START_NEXT_BATCHED_SCAN); + } + + private void startNextBatchedScan() { + // first grab any existing data + int nextCount = retrieveBatchedScanData(); + + setNextBatchedAlarm(nextCount); + } + + // return true if new/different + private boolean recordBatchedScanSettings(BatchedScanSettings settings) { + if (DBG) log("set batched scan to " + settings); + if (settings != null) { + // TODO - noteBatchedScanStart(message.arg1); + if (settings.equals(mBatchedScanSettings)) return false; + } else { + if (mBatchedScanSettings == null) return false; + // TODO - noteBatchedScanStop(message.arg1); + } + mBatchedScanSettings = settings; + return true; + } + + private void stopBatchedScan() { + mAlarmManager.cancel(mBatchedScanIntervalIntent); + retrieveBatchedScanData(); + mWifiNative.setBatchedScanSettings(null); + } + + private void setNextBatchedAlarm(int scansExpected) { + + if (mBatchedScanSettings == null || scansExpected < 1) return; + + if (mBatchedScanSettings.maxScansPerBatch < scansExpected) { + scansExpected = mBatchedScanSettings.maxScansPerBatch; + } + + int secToFull = mBatchedScanSettings.scanIntervalSec; + secToFull *= scansExpected; + + int debugPeriod = SystemProperties.getInt("wifi.batchedScan.pollPeriod", 0); + if (debugPeriod > 0) secToFull = debugPeriod; + + // set the alarm to do the next poll. We set it a little short as we'd rather + // wake up wearly than miss a scan due to buffer overflow + mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + + ((secToFull - (mBatchedScanSettings.scanIntervalSec / 2)) * 1000), + mBatchedScanIntervalIntent); + } + + /** + * Start reading new scan data + * Data comes in as: + * "scancount=5\n" + * "nextcount=5\n" + * "apcount=3\n" + * "trunc\n" (optional) + * "bssid=...\n" + * "ssid=...\n" + * "freq=...\n" (in Mhz) + * "level=...\n" + * "dist=...\n" (in cm) + * "distsd=...\n" (standard deviation, in cm) + * "====" + * "bssid=...\n" + * etc + * "====" + * "bssid=...\n" + * etc + * "%%%%" + * "apcount=2\n" + * "bssid=...\n" + * etc + * "%%%% + * etc + * "----" + */ + private int retrieveBatchedScanData() { + String rawData = mWifiNative.getBatchedScanResults(); + if (rawData == null) { + loge("Unexpected null BatchedScanResults"); + return 0; + } + + int nextCount = 0; + int scanCount = 0; + final String END_OF_SCAN = "===="; + final String END_OF_BATCH = "%%%%"; + final String END_OF_BATCHES = "----"; + final String SCANCOUNT = "scancount="; + final String NEXTCOUNT = "nextcount="; + final String TRUNCATED = "trunc"; + final String APCOUNT = "apcount="; + final String AGE = "age="; + final String DIST = "dist="; + final String DISTSD = "distsd="; + + String splitData[] = rawData.split("\n"); + int n = 0; + if (splitData[n].startsWith(SCANCOUNT)) { + try { + scanCount = Integer.parseInt(splitData[n++].substring(SCANCOUNT.length())); + } catch (NumberFormatException e) {} + } + if (scanCount == 0) { + loge("scanCount not found"); + return 0; + } + if (splitData[n].startsWith(NEXTCOUNT)) { + try { + nextCount = Integer.parseInt(splitData[n++].substring(NEXTCOUNT.length())); + } catch (NumberFormatException e) {} + } + if (nextCount == 0) { + loge("nextCount not found"); + return 0; + } + + final Intent intent = new Intent(WifiManager.BATCHED_SCAN_RESULTS_AVAILABLE_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + + synchronized (mBatchedScanResults) { + mBatchedScanResults.clear(); + BatchedScanResult batchedScanResult = new BatchedScanResult(); + + String bssid = null; + WifiSsid wifiSsid = null; + int level = 0; + int freq = 0; + int dist, distSd; + long tsf = 0; + dist = distSd = ScanResult.UNSPECIFIED; + long now = System.currentTimeMillis(); + + while (true) { + while (n < splitData.length) { + if (splitData[n].equals(END_OF_BATCHES)) { + if (++n != splitData.length) { + loge("didn't consume " + (splitData.length-n)); + } + if (mBatchedScanResults.size() > 0) { + mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } + return nextCount; + } + if ((splitData[n].equals(END_OF_SCAN)) || splitData[n].equals(END_OF_BATCH)) { + if (bssid != null) { + batchedScanResult.scanResults.add(new ScanResult( + wifiSsid, bssid, "", level, freq, tsf, dist, distSd)); + wifiSsid = null; + bssid = null; + level = 0; + freq = 0; + tsf = 0; + dist = distSd = ScanResult.UNSPECIFIED; + } + if (splitData[n].equals(END_OF_BATCH)) { + if (batchedScanResult.scanResults.size() != 0) { + mBatchedScanResults.add(batchedScanResult); + batchedScanResult = new BatchedScanResult(); + } else { + logd("Found empty batch"); + } + } + n++; + } else if (splitData[n].equals(BSSID_STR)) { + bssid = splitData[n++].substring(BSSID_STR.length()); + } else if (splitData[n].equals(FREQ_STR)) { + try { + freq = Integer.parseInt(splitData[n++].substring(FREQ_STR.length())); + } catch (NumberFormatException e) { + loge("Invalid freqency: " + splitData[n-1]); + freq = 0; + } + } else if (splitData[n].equals(AGE)) { + try { + tsf = now - Long.parseLong(splitData[n++].substring(AGE.length())); + } catch (NumberFormatException e) { + loge("Invalid timestamp: " + splitData[n-1]); + tsf = 0; + } + } else if (splitData[n].equals(SSID_STR)) { + wifiSsid = WifiSsid.createFromAsciiEncoded( + splitData[n++].substring(SSID_STR.length())); + } else if (splitData[n].equals(LEVEL_STR)) { + try { + level = Integer.parseInt(splitData[n++].substring(LEVEL_STR.length())); + if (level > 0) level -= 256; + } catch (NumberFormatException e) { + loge("Invalid level: " + splitData[n-1]); + level = 0; + } + } else if (splitData[n].equals(DIST)) { + try { + dist = Integer.parseInt(splitData[n++].substring(DIST.length())); + } catch (NumberFormatException e) { + loge("Invalid distance: " + splitData[n-1]); + dist = ScanResult.UNSPECIFIED; + } + } else if (splitData[n].equals(DISTSD)) { + try { + distSd = Integer.parseInt(splitData[n++].substring(DISTSD.length())); + } catch (NumberFormatException e) { + loge("Invalid distanceSd: " + splitData[n-1]); + distSd = ScanResult.UNSPECIFIED; + } + } + } + rawData = mWifiNative.getBatchedScanResults(); + if (rawData == null) { + loge("Unexpected null BatchedScanResults"); + return nextCount; + } + splitData = rawData.split("\n"); + if (splitData.length == 0 || splitData[0].equals("ok")) { + loge("batch scan results just ended!"); + if (mBatchedScanResults.size() > 0) { + mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } + return nextCount; + } + n = 0; + } + } + } + // If workSource is not null, blame is given to it, otherwise blame is given to callingUid. private void noteScanStart(int callingUid, WorkSource workSource) { if (mScanWorkSource == null && (callingUid != UNKNOWN_SCAN_SOURCE || workSource != null)) { @@ -1979,6 +2266,12 @@ public class WifiStateMachine extends StateMachine { sendMessageAtFrontOfQueue(CMD_SET_COUNTRY_CODE, countryCode); } break; + case CMD_SET_BATCH_SCAN: + recordBatchedScanSettings((BatchedScanSettings)message.obj); + break; + case CMD_START_NEXT_BATCHED_SCAN: + startNextBatchedScan(); + break; /* Discard */ case CMD_START_SCAN: case CMD_START_SUPPLICANT: @@ -2472,6 +2765,10 @@ public class WifiStateMachine extends StateMachine { mWifiNative.stopFilteringMulticastV4Packets(); } + if (mBatchedScanSettings != null) { + startBatchedScan(); + } + if (mOperationalMode != CONNECT_MODE) { mWifiNative.disconnect(); transitionTo(mScanModeState); @@ -2513,6 +2810,10 @@ public class WifiStateMachine extends StateMachine { noteScanStart(message.arg1, (WorkSource) message.obj); startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP); break; + case CMD_SET_BATCH_SCAN: + recordBatchedScanSettings((BatchedScanSettings)message.obj); + startBatchedScan(); + break; case CMD_SET_COUNTRY_CODE: String country = (String) message.obj; if (DBG) log("set country code " + country); @@ -2641,6 +2942,10 @@ public class WifiStateMachine extends StateMachine { updateBatteryWorkSource(null); mScanResults = new ArrayList<ScanResult>(); + if (mBatchedScanSettings != null) { + stopBatchedScan(); + } + final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED); |