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)
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>
<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" />
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;
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;
/* Tracks the persisted states for wi-fi & airplane mode */
final WifiSettingsStore mSettingsStore;
+ final boolean mBatchedScanSupported;
+
/**
* Asynchronous channel to WifiStateMachine
*/
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() {
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");
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>();
--- /dev/null
+/**
+ * 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;
--- /dev/null
+/*
+ * 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];
+ }
+ };
+}
--- /dev/null
+/**
+ * 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;
--- /dev/null
+/*
+ * 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];
+ }
+ };
+}
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;
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();
}
* 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,
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) {
level = source.level;
frequency = source.frequency;
timestamp = source.timestamp;
+ distanceCm = source.distanceCm;
+ distanceSdCm = source.distanceSdCm;
}
}
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();
}
dest.writeInt(level);
dest.writeInt(frequency);
dest.writeLong(timestamp);
+ dest.writeInt(distanceCm);
+ dest.writeInt(distanceSdCm);
}
/** Implement the Parcelable interface {@hide} */
in.readString(),
in.readInt(),
in.readInt(),
- in.readLong()
+ in.readLong(),
+ in.readInt(),
+ in.readInt()
);
}
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;
@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
*/
}
/**
+ * 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}.
*/
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");
}
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;
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);
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;
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.
*/
private final IBatteryStats mBatteryStats;
+ private BatchedScanSettings mBatchedScanSettings = null;
+
+
public WifiStateMachine(Context context, String wlanInterface) {
super("WifiStateMachine");
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);
},
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() {
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)) {
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:
mWifiNative.stopFilteringMulticastV4Packets();
}
+ if (mBatchedScanSettings != null) {
+ startBatchedScan();
+ }
+
if (mOperationalMode != CONNECT_MODE) {
mWifiNative.disconnect();
transitionTo(mScanModeState);
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);
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);