resolve merge conflicts of 849c5c7 to mnc-dev
This resovles the merge conflict for ag/1514448/
After Android M, this function uses num_bssid instead of num_ap.
Both are prone to stack overflow attacks.
Bug: 31856351
Test: compile, unit tests, manual test
Change-Id: I194850a4c79ddf478d98e750f65b24e82d99ebc0
diff --git a/service/Android.mk b/service/Android.mk
old mode 100755
new mode 100644
index fd7b41c..f325782
--- a/service/Android.mk
+++ b/service/Android.mk
@@ -16,16 +16,14 @@
ifneq ($(TARGET_BUILD_PDK), true)
-# Make HAL stub library
+# Make HAL stub library 1
# ============================================================
include $(CLEAR_VARS)
LOCAL_REQUIRED_MODULES :=
-LOCAL_CFLAGS += -Wno-unused-parameter -Wno-int-to-pointer-cast
-LOCAL_CFLAGS += -Wno-maybe-uninitialized -Wno-parentheses
-LOCAL_CPPFLAGS += -Wno-conversion-null
+LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_C_INCLUDES += \
external/libnl-headers \
@@ -38,6 +36,27 @@
include $(BUILD_STATIC_LIBRARY)
+# Make HAL stub library 2
+# ============================================================
+
+include $(CLEAR_VARS)
+
+LOCAL_REQUIRED_MODULES :=
+
+LOCAL_CFLAGS += -Wno-unused-parameter
+
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH)/jni \
+ external/libnl-headers \
+ $(call include-path-for, libhardware_legacy)/hardware_legacy
+
+LOCAL_SRC_FILES := \
+ lib/wifi_hal_stub.cpp
+
+LOCAL_MODULE := libwifi-hal-stub
+
+include $(BUILD_STATIC_LIBRARY)
+
# set correct hal library path
# ============================================================
LIB_WIFI_HAL := libwifi-hal
@@ -45,9 +64,7 @@
ifeq ($(BOARD_WLAN_DEVICE), bcmdhd)
LIB_WIFI_HAL := libwifi-hal-bcm
else ifeq ($(BOARD_WLAN_DEVICE), qcwcn)
- # this is commented because none of the nexus devices
- # that sport Qualcomm's wifi have support for HAL
- # LIB_WIFI_HAL := libwifi-hal-qcom
+ LIB_WIFI_HAL := libwifi-hal-qcom
else ifeq ($(BOARD_WLAN_DEVICE), mrvl)
# this is commented because none of the nexus devices
# that sport Marvell's wifi have support for HAL
@@ -57,46 +74,13 @@
LIB_WIFI_HAL := libwifi-hal-mt66xx
endif
-# Build the HalUtil
-# ============================================================
-
-include $(CLEAR_VARS)
-
-LOCAL_REQUIRED_MODULES := libandroid_runtime libhardware_legacy
-
-LOCAL_CFLAGS += -Wno-unused-parameter -Wno-int-to-pointer-cast
-LOCAL_CFLAGS += -Wno-maybe-uninitialized -Wno-parentheses
-LOCAL_CPPFLAGS += -Wno-conversion-null
-
-LOCAL_C_INCLUDES += \
- $(call include-path-for, libhardware)/hardware \
- $(call include-path-for, libhardware_legacy)/hardware_legacy \
- libcore/include
-
-LOCAL_SHARED_LIBRARIES += \
- libcutils \
- libnl \
- libandroid_runtime \
- libutils
-
-LOCAL_STATIC_LIBRARIES += $(LIB_WIFI_HAL)
-
-LOCAL_SRC_FILES := \
- tools/halutil/halutil.cpp
-
-LOCAL_MODULE := halutil
-
-include $(BUILD_EXECUTABLE)
-
# Make the JNI part
# ============================================================
include $(CLEAR_VARS)
LOCAL_REQUIRED_MODULES := libandroid_runtime libhardware_legacy
-LOCAL_CFLAGS += -Wno-unused-parameter -Wno-int-to-pointer-cast
-LOCAL_CFLAGS += -Wno-maybe-uninitialized -Wno-parentheses
-LOCAL_CPPFLAGS += -Wno-conversion-null
+LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
@@ -111,8 +95,10 @@
libhardware \
libhardware_legacy \
libandroid_runtime \
- libnl
+ libnl \
+ libdl
+LOCAL_STATIC_LIBRARIES += libwifi-hal-stub
LOCAL_STATIC_LIBRARIES += $(LIB_WIFI_HAL)
LOCAL_SRC_FILES := \
@@ -135,7 +121,7 @@
LOCAL_JNI_SHARED_LIBRARIES := libandroid_runtime
LOCAL_JAVA_LIBRARIES := bouncycastle conscrypt services
-LOCAL_STATIC_JAVA_LIBRARIES := ksoap2
+LOCAL_STATIC_JAVA_LIBRARIES := ksoap2 android-support-v4
LOCAL_REQUIRED_MODULES := services
LOCAL_MODULE_TAGS :=
LOCAL_MODULE := wifi-service
diff --git a/service/java/com/android/server/wifi/ConfigurationMap.java b/service/java/com/android/server/wifi/ConfigurationMap.java
new file mode 100644
index 0000000..0fb56ea
--- /dev/null
+++ b/service/java/com/android/server/wifi/ConfigurationMap.java
@@ -0,0 +1,146 @@
+package com.android.server.wifi;
+
+import android.net.wifi.WifiConfiguration;
+import android.util.Log;
+
+import com.android.server.wifi.hotspot2.Utils;
+import com.android.server.wifi.hotspot2.pps.HomeSP;
+
+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 ConfigurationMap {
+ private final Map<Integer, WifiConfiguration> mPerID = new HashMap<>();
+ private final Map<Integer, WifiConfiguration> mPerConfigKey = new HashMap<>();
+ private final Map<String, Integer> mPerFQDN = new HashMap<>();
+
+ // RW methods:
+ public WifiConfiguration put(int netid, WifiConfiguration config) {
+ WifiConfiguration current = mPerID.put(netid, config);
+ mPerConfigKey.put(config.configKey().hashCode(), config); // This is ridiculous...
+ if (config.FQDN != null && config.FQDN.length() > 0) {
+ mPerFQDN.put(config.FQDN, netid);
+ }
+ return current;
+ }
+
+ public void populatePasspointData(Collection<HomeSP> homeSPs, WifiNative wifiNative) {
+ mPerFQDN.clear();
+
+ for (HomeSP homeSp : homeSPs) {
+ String fqdn = homeSp.getFQDN();
+ Log.d(WifiConfigStore.TAG, "Looking for " + fqdn);
+ for (WifiConfiguration config : mPerID.values()) {
+ Log.d(WifiConfigStore.TAG, "Testing " + config.SSID);
+
+ String id_str = Utils.unquote(wifiNative.getNetworkVariable(
+ config.networkId, WifiConfigStore.idStringVarName));
+ if (id_str != null && id_str.equals(fqdn) && config.enterpriseConfig != null) {
+ Log.d(WifiConfigStore.TAG, "Matched " + id_str + " with " + config.networkId);
+ config.FQDN = fqdn;
+ config.providerFriendlyName = homeSp.getFriendlyName();
+
+ HashSet<Long> roamingConsortiumIds = homeSp.getRoamingConsortiums();
+ config.roamingConsortiumIds = new long[roamingConsortiumIds.size()];
+ int i = 0;
+ for (long id : roamingConsortiumIds) {
+ config.roamingConsortiumIds[i] = id;
+ i++;
+ }
+ IMSIParameter imsiParameter = homeSp.getCredential().getImsi();
+ config.enterpriseConfig.setPlmn(
+ imsiParameter != null ? imsiParameter.toString() : null);
+ config.enterpriseConfig.setRealm(homeSp.getCredential().getRealm());
+ mPerFQDN.put(fqdn, config.networkId);
+ }
+ }
+ }
+
+ Log.d(WifiConfigStore.TAG, "loaded " + mPerFQDN.size() + " passpoint configs");
+ }
+
+ public WifiConfiguration remove(int netID) {
+ WifiConfiguration config = mPerID.remove(netID);
+ if (config == null) {
+ return null;
+ }
+ mPerConfigKey.remove(config.configKey().hashCode());
+
+ Iterator<Map.Entry<String, Integer>> entries = mPerFQDN.entrySet().iterator();
+ while (entries.hasNext()) {
+ if (entries.next().getValue() == netID) {
+ entries.remove();
+ break;
+ }
+ }
+ return config;
+ }
+
+ public void clear() {
+ mPerID.clear();
+ mPerConfigKey.clear();
+ mPerFQDN.clear();
+ }
+
+ // RO methods:
+ public WifiConfiguration get(int netid) {
+ return mPerID.get(netid);
+ }
+
+ public int size() {
+ return mPerID.size();
+ }
+
+ public boolean isEmpty() {
+ return mPerID.size() == 0;
+ }
+
+ public WifiConfiguration getByFQDN(String fqdn) {
+ Integer id = mPerFQDN.get(fqdn);
+ return id != null ? mPerID.get(id) : null;
+ }
+
+ public WifiConfiguration getByConfigKey(String key) {
+ if (key == null) {
+ return null;
+ }
+ for (WifiConfiguration config : mPerID.values()) {
+ if (config.configKey().equals(key)) {
+ return config;
+ }
+ }
+ return null;
+ }
+
+ public WifiConfiguration getByConfigKeyID(int id) {
+ return mPerConfigKey.get(id);
+ }
+
+ public Collection<WifiConfiguration> getEnabledNetworks() {
+ List<WifiConfiguration> list = new ArrayList<>();
+ for (WifiConfiguration config : mPerID.values()) {
+ if (config.status != WifiConfiguration.Status.DISABLED) {
+ list.add(config);
+ }
+ }
+ return list;
+ }
+
+ public WifiConfiguration getEphemeral(String ssid) {
+ for (WifiConfiguration config : mPerID.values()) {
+ if (ssid.equals(config.SSID) && config.ephemeral) {
+ return config;
+ }
+ }
+ return null;
+ }
+
+ public Collection<WifiConfiguration> values() {
+ return mPerID.values();
+ }
+}
diff --git a/service/java/com/android/server/wifi/IMSIParameter.java b/service/java/com/android/server/wifi/IMSIParameter.java
new file mode 100644
index 0000000..deea870
--- /dev/null
+++ b/service/java/com/android/server/wifi/IMSIParameter.java
@@ -0,0 +1,102 @@
+package com.android.server.wifi;
+
+import java.io.IOException;
+
+public class IMSIParameter {
+ private final String mImsi;
+ private final boolean mPrefix;
+
+ public IMSIParameter(String imsi, boolean prefix) {
+ mImsi = imsi;
+ mPrefix = prefix;
+ }
+
+ public IMSIParameter(String imsi) throws IOException {
+ if (imsi == null || imsi.length() == 0) {
+ throw new IOException("Bad IMSI: '" + imsi + "'");
+ }
+
+ int nonDigit;
+ char stopChar = '\0';
+ for (nonDigit = 0; nonDigit < imsi.length(); nonDigit++) {
+ stopChar = imsi.charAt(nonDigit);
+ if (stopChar < '0' || stopChar > '9') {
+ break;
+ }
+ }
+
+ if (nonDigit == imsi.length()) {
+ mImsi = imsi;
+ mPrefix = false;
+ }
+ else if (nonDigit == imsi.length()-1 && stopChar == '*') {
+ mImsi = imsi.substring(0, nonDigit);
+ mPrefix = true;
+ }
+ else {
+ throw new IOException("Bad IMSI: '" + imsi + "'");
+ }
+ }
+
+ public boolean matches(String fullIMSI) {
+ if (mPrefix) {
+ return mImsi.regionMatches(false, 0, fullIMSI, 0, mImsi.length());
+ }
+ else {
+ return mImsi.equals(fullIMSI);
+ }
+ }
+
+ public boolean matchesMccMnc(String mccMnc) {
+ if (mPrefix) {
+ // For a prefix match, the entire prefix must match the mcc+mnc
+ return mImsi.regionMatches(false, 0, mccMnc, 0, mImsi.length());
+ }
+ else {
+ // For regular match, the entire length of mcc+mnc must match this IMSI
+ return mImsi.regionMatches(false, 0, mccMnc, 0, mccMnc.length());
+ }
+ }
+
+ public boolean isPrefix() {
+ return mPrefix;
+ }
+
+ public String getImsi() {
+ return mImsi;
+ }
+
+ public int prefixLength() {
+ return mImsi.length();
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ else if (thatObject == null || getClass() != thatObject.getClass()) {
+ return false;
+ }
+
+ IMSIParameter that = (IMSIParameter) thatObject;
+ return mPrefix == that.mPrefix && mImsi.equals(that.mImsi);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mImsi != null ? mImsi.hashCode() : 0;
+ result = 31 * result + (mPrefix ? 1 : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ if (mPrefix) {
+ return mImsi + '*';
+ }
+ else {
+ return mImsi;
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/RttService.java b/service/java/com/android/server/wifi/RttService.java
index c447e28..cc7f549 100644
--- a/service/java/com/android/server/wifi/RttService.java
+++ b/service/java/com/android/server/wifi/RttService.java
@@ -28,8 +28,9 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
+import android.Manifest;
-class RttService extends SystemService {
+public final class RttService extends SystemService {
public static final boolean DBG = true;
@@ -144,6 +145,16 @@
Integer key;
ClientInfo ci;
RttManager.RttParams[] params;
+
+ @Override
+ public String toString() {
+ String str = getClass().getName() + "@" + Integer.toHexString(hashCode());
+ if(this.key != null) {
+ return str + " key: " + this.key;
+ } else {
+ return str + " key: " + " , null";
+ }
+ }
}
private class ClientInfo {
@@ -198,12 +209,14 @@
}
void reportAborted(int key) {
- mChannel.sendMessage(RttManager.CMD_OP_ABORTED, key);
- mRequests.remove(key);
+ mChannel.sendMessage(RttManager.CMD_OP_ABORTED, 0, key);
+ //All Queued RTT request will be cleaned
+ cleanup();
}
void cleanup() {
mRequests.clear();
+ mRequestQueue.clear();
}
}
@@ -271,20 +284,32 @@
transitionTo(mRequestPendingState);
break;
case RttManager.CMD_OP_START_RANGING: {
- RttManager.ParcelableRttParams params =
- (RttManager.ParcelableRttParams)msg.obj;
- if (params == null) {
- replyFailed(msg,
- RttManager.REASON_INVALID_REQUEST, "No params");
- } else if (ci.addRttRequest(msg.arg2, params) == false) {
- replyFailed(msg,
- RttManager.REASON_INVALID_REQUEST, "Unspecified");
- } else {
- sendMessage(CMD_ISSUE_NEXT_REQUEST);
- }
+ //check permission
+ if(DBG) Log.d(TAG, "UID is: " + msg.sendingUid);
+ if (!enforcePermissionCheck(msg)) {
+ Log.e(TAG, "UID: " + msg.sendingUid + " has no" +
+ " LOCATION_HARDWARE Permission");
+ break;
}
+
+ RttManager.ParcelableRttParams params =
+ (RttManager.ParcelableRttParams)msg.obj;
+ if (params == null) {
+ replyFailed(msg,
+ RttManager.REASON_INVALID_REQUEST, "No params");
+ } else if (ci.addRttRequest(msg.arg2, params) == false) {
+ replyFailed(msg,
+ RttManager.REASON_INVALID_REQUEST, "Unspecified");
+ } else {
+ sendMessage(CMD_ISSUE_NEXT_REQUEST);
+ }
+ }
break;
case RttManager.CMD_OP_STOP_RANGING:
+ if(!enforcePermissionCheck(msg)) {
+ break;
+ }
+
for (Iterator<RttRequest> it = mRequestQueue.iterator();
it.hasNext(); ) {
RttRequest request = it.next();
@@ -312,6 +337,7 @@
case CMD_DRIVER_UNLOADED:
if (mOutstandingRequest != null) {
WifiNative.cancelRtt(mOutstandingRequest.params);
+ if (DBG) Log.d(TAG, "abort key: " + mOutstandingRequest.key);
mOutstandingRequest.ci.reportAborted(mOutstandingRequest.key);
mOutstandingRequest = null;
}
@@ -323,23 +349,38 @@
if (mOutstandingRequest == null) {
transitionTo(mEnabledState);
}
+ if(mOutstandingRequest != null) {
+ if (DBG) Log.d(TAG, "new mOutstandingRequest.key is: " +
+ mOutstandingRequest.key);
+ } else {
+ if (DBG) Log.d(TAG,
+ "CMD_ISSUE_NEXT_REQUEST: mOutstandingRequest =null ");
+ }
} else {
/* just wait; we'll issue next request after
* current one is finished */
- if (DBG) Log.d(TAG, "Ignoring CMD_ISSUE_NEXT_REQUEST");
+ if (DBG) Log.d(TAG, "Current mOutstandingRequest.key is: " +
+ mOutstandingRequest.key);
+ if (DBG) Log.d(TAG, "Ignoring CMD_ISSUE_NEXT_REQUEST");
}
break;
case CMD_RTT_RESPONSE:
- if (DBG) Log.d(TAG, "Received an RTT response");
+ if (DBG) Log.d(TAG, "Received an RTT response from: " + msg.arg2);
mOutstandingRequest.ci.reportResult(
mOutstandingRequest, (RttManager.RttResult[])msg.obj);
mOutstandingRequest = null;
sendMessage(CMD_ISSUE_NEXT_REQUEST);
break;
case RttManager.CMD_OP_STOP_RANGING:
+ if(!enforcePermissionCheck(msg)) {
+ Log.e(TAG, "UID: " + msg.sendingUid + " has no " +
+ "LOCATION_HARDWARE Permission");
+ break;
+ }
+
if (mOutstandingRequest != null
&& msg.arg2 == mOutstandingRequest.key) {
- if (DBG) Log.d(TAG, "Cancelling ongoing RTT");
+ if (DBG) Log.d(TAG, "Cancelling ongoing RTT of: " + msg.arg2);
WifiNative.cancelRtt(mOutstandingRequest.params);
mOutstandingRequest.ci.reportAborted(mOutstandingRequest.key);
mOutstandingRequest = null;
@@ -390,6 +431,17 @@
}
}
+ boolean enforcePermissionCheck(Message msg) {
+ try {
+ mContext.enforcePermission(Manifest.permission.LOCATION_HARDWARE,
+ -1, msg.sendingUid, "LocationRTT");
+ } catch (SecurityException e) {
+ replyFailed(msg,RttManager.REASON_PERMISSION_DENIED, "No params");
+ return false;
+ }
+ return true;
+ }
+
private WifiNative.RttEventHandler mEventHandler = new WifiNative.RttEventHandler() {
@Override
public void onRttResults(RttManager.RttResult[] result) {
@@ -401,12 +453,15 @@
RttRequest request = null;
while (mRequestQueue.isEmpty() == false) {
request = mRequestQueue.remove();
- if (WifiNative.requestRtt(request.params, mEventHandler)) {
- if (DBG) Log.d(TAG, "Issued next RTT request");
- return request;
- } else {
- request.ci.reportFailed(request,
- RttManager.REASON_UNSPECIFIED, "Failed to start");
+ if(request != null) {
+ if (WifiNative.requestRtt(request.params, mEventHandler)) {
+ if (DBG) Log.d(TAG, "Issued next RTT request with key: " + request.key);
+ return request;
+ } else {
+ Log.e(TAG, "Fail to issue key at native layer");
+ request.ci.reportFailed(request,
+ RttManager.REASON_UNSPECIFIED, "Failed to start");
+ }
}
}
@@ -414,6 +469,10 @@
if (DBG) Log.d(TAG, "No more requests left");
return null;
}
+ @Override
+ public RttManager.RttCapabilities getRttCapabilities() {
+ return WifiNative.getRttCapabilities();
+ }
}
private static final String TAG = "RttService";
@@ -442,4 +501,6 @@
mImpl.startService(getContext());
}
}
+
+
}
diff --git a/service/java/com/android/server/wifi/SIMAccessor.java b/service/java/com/android/server/wifi/SIMAccessor.java
new file mode 100644
index 0000000..e446436
--- /dev/null
+++ b/service/java/com/android/server/wifi/SIMAccessor.java
@@ -0,0 +1,32 @@
+package com.android.server.wifi;
+
+import android.content.Context;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SIMAccessor {
+ private final TelephonyManager mTelephonyManager;
+ private final SubscriptionManager mSubscriptionManager;
+
+ public SIMAccessor(Context context) {
+ mTelephonyManager = TelephonyManager.from(context);
+ mSubscriptionManager = SubscriptionManager.from(context);
+ }
+
+ public List<String> getMatchingImsis(IMSIParameter mccMnc) {
+ if (mccMnc == null) {
+ return null;
+ }
+ List<String> imsis = new ArrayList<>();
+ for (int subId : mSubscriptionManager.getActiveSubscriptionIdList()) {
+ String imsi = mTelephonyManager.getSubscriberId(subId);
+ if (mccMnc.matches(imsi)) {
+ imsis.add(imsi);
+ }
+ }
+ return imsis.isEmpty() ? null : imsis;
+ }
+}
diff --git a/service/java/com/android/server/wifi/ScanDetail.java b/service/java/com/android/server/wifi/ScanDetail.java
new file mode 100644
index 0000000..868a192
--- /dev/null
+++ b/service/java/com/android/server/wifi/ScanDetail.java
@@ -0,0 +1,157 @@
+package com.android.server.wifi;
+
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiSsid;
+
+import com.android.server.wifi.anqp.ANQPElement;
+import com.android.server.wifi.anqp.Constants;
+import com.android.server.wifi.anqp.HSFriendlyNameElement;
+import com.android.server.wifi.anqp.VenueNameElement;
+import com.android.server.wifi.hotspot2.NetworkDetail;
+import com.android.server.wifi.hotspot2.PasspointMatch;
+import com.android.server.wifi.hotspot2.PasspointMatchInfo;
+import com.android.server.wifi.hotspot2.Utils;
+import com.android.server.wifi.hotspot2.pps.HomeSP;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class ScanDetail {
+ private final ScanResult mScanResult;
+ private volatile NetworkDetail mNetworkDetail;
+ private final Map<HomeSP, PasspointMatch> mMatches;
+ private long mSeen = 0;
+
+ public ScanDetail(NetworkDetail networkDetail, WifiSsid wifiSsid, String BSSID,
+ String caps, int level, int frequency, long tsf) {
+ mNetworkDetail = networkDetail;
+ mScanResult = new ScanResult(wifiSsid, BSSID, caps, level, frequency, tsf );
+ mSeen = System.currentTimeMillis();
+ //mScanResult.seen = mSeen;
+ mScanResult.channelWidth = networkDetail.getChannelWidth();
+ mScanResult.centerFreq0 = networkDetail.getCenterfreq0();
+ mScanResult.centerFreq1 = networkDetail.getCenterfreq1();
+ if (networkDetail.is80211McResponderSupport())
+ mScanResult.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
+ mMatches = null;
+ }
+
+ public ScanDetail(WifiSsid wifiSsid, String BSSID, String caps, int level, int frequency,
+ long tsf, long seen) {
+ mNetworkDetail = null;
+ mScanResult = new ScanResult(wifiSsid, BSSID, caps, level, frequency, tsf );
+ mSeen = seen;
+ //mScanResult.seen = mSeen;
+ mScanResult.channelWidth = 0;
+ mScanResult.centerFreq0 = 0;
+ mScanResult.centerFreq1 = 0;
+ mScanResult.flags = 0;
+ mMatches = null;
+ }
+
+ private ScanDetail(ScanResult scanResult, NetworkDetail networkDetail,
+ Map<HomeSP, PasspointMatch> matches) {
+ mScanResult = scanResult;
+ mNetworkDetail = networkDetail;
+ mMatches = matches;
+ }
+
+ public void updateResults(NetworkDetail networkDetail, int level, WifiSsid wssid, String ssid,
+ String flags, int freq, long tsf) {
+ mScanResult.level = level;
+ mScanResult.wifiSsid = wssid;
+ // Keep existing API
+ mScanResult.SSID = ssid;
+ mScanResult.capabilities = flags;
+ mScanResult.frequency = freq;
+ mScanResult.timestamp = tsf;
+ mSeen = System.currentTimeMillis();
+ //mScanResult.seen = mSeen;
+ mScanResult.channelWidth = networkDetail.getChannelWidth();
+ mScanResult.centerFreq0 = networkDetail.getCenterfreq0();
+ mScanResult.centerFreq1 = networkDetail.getCenterfreq1();
+ if (networkDetail.is80211McResponderSupport())
+ mScanResult.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
+ if (networkDetail.isInterworking())
+ mScanResult.setFlag(ScanResult.FLAG_PASSPOINT_NETWORK);
+ }
+
+ public void propagateANQPInfo(Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
+ if (anqpElements.isEmpty()) {
+ return;
+ }
+ mNetworkDetail = mNetworkDetail.complete(anqpElements);
+ HSFriendlyNameElement fne = (HSFriendlyNameElement)anqpElements.get(
+ Constants.ANQPElementType.HSFriendlyName);
+ // !!! Match with language
+ if (fne != null && !fne.getNames().isEmpty()) {
+ mScanResult.venueName = fne.getNames().get(0).getText();
+ } else {
+ VenueNameElement vne =
+ (((VenueNameElement)anqpElements.get(Constants.ANQPElementType.ANQPVenueName)));
+ if (vne != null && !vne.getNames().isEmpty()) {
+ mScanResult.venueName = vne.getNames().get(0).getText();
+ }
+ }
+ }
+
+ public ScanResult getScanResult() {
+ return mScanResult;
+ }
+
+ public NetworkDetail getNetworkDetail() {
+ return mNetworkDetail;
+ }
+
+ public String getSSID() {
+ return mNetworkDetail == null ? mScanResult.SSID : mNetworkDetail.getSSID();
+ }
+
+ public String getBSSIDString() {
+ return mNetworkDetail == null ? mScanResult.BSSID : mNetworkDetail.getBSSIDString();
+ }
+
+ public String toKeyString() {
+ NetworkDetail networkDetail = mNetworkDetail;
+ if (networkDetail != null) {
+ return networkDetail.toKeyString();
+ }
+ else {
+ return String.format("'%s':%012x", mScanResult.BSSID, Utils.parseMac(mScanResult.BSSID));
+ }
+ }
+
+ public long getSeen() {
+ return mSeen;
+ }
+
+ public long setSeen() {
+ mSeen = System.currentTimeMillis();
+ mScanResult.seen = mSeen;
+ return mSeen;
+ }
+
+ public List<PasspointMatchInfo> getMatchList() {
+ if (mMatches == null || mMatches.isEmpty()) {
+ return null;
+ }
+
+ List<PasspointMatchInfo> list = new ArrayList<>();
+ for (Map.Entry<HomeSP, PasspointMatch> entry : mMatches.entrySet()) {
+ new PasspointMatchInfo(entry.getValue(), this, entry.getKey());
+ }
+ return list;
+ }
+
+ @Override
+ public String toString() {
+ try {
+ return String.format("'%s'/%012x",
+ mScanResult.SSID, Utils.parseMac(mScanResult.BSSID));
+ }
+ catch (IllegalArgumentException iae) {
+ return String.format("'%s'/----", mScanResult.BSSID);
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/ScanDetailCache.java b/service/java/com/android/server/wifi/ScanDetailCache.java
new file mode 100644
index 0000000..d246c55
--- /dev/null
+++ b/service/java/com/android/server/wifi/ScanDetailCache.java
@@ -0,0 +1,300 @@
+
+package com.android.server.wifi;
+
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.util.Log;
+import android.os.SystemClock;
+
+import com.android.server.wifi.ScanDetail;
+import com.android.server.wifi.hotspot2.PasspointMatch;
+import com.android.server.wifi.hotspot2.PasspointMatchInfo;
+import com.android.server.wifi.hotspot2.pps.HomeSP;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+class ScanDetailCache {
+
+ private static final String TAG = "ScanDetailCache";
+
+ private WifiConfiguration mConfig;
+ private HashMap<String, ScanDetail> mMap;
+ private HashMap<String, PasspointMatchInfo> mPasspointMatches;
+ private static boolean DBG=false;
+ ScanDetailCache(WifiConfiguration config) {
+ mConfig = config;
+ mMap = new HashMap();
+ mPasspointMatches = new HashMap();
+ }
+
+ void put(ScanDetail scanDetail) {
+ put(scanDetail, null, null);
+ }
+
+ void put(ScanDetail scanDetail, PasspointMatch match, HomeSP homeSp) {
+
+ mMap.put(scanDetail.getBSSIDString(), scanDetail);
+
+ if (match != null && homeSp != null) {
+ mPasspointMatches.put(scanDetail.getBSSIDString(),
+ new PasspointMatchInfo(match, scanDetail, homeSp));
+ }
+ }
+
+ ScanResult get(String bssid) {
+ ScanDetail scanDetail = getScanDetail(bssid);
+ return scanDetail == null ? null : scanDetail.getScanResult();
+ }
+
+ ScanDetail getScanDetail(String bssid) {
+ return mMap.get(bssid);
+ }
+
+ void remove(String bssid) {
+ mMap.remove(bssid);
+ }
+
+ int size() {
+ return mMap.size();
+ }
+
+ boolean isEmpty() {
+ return size() == 0;
+ }
+
+ ScanDetail getFirst() {
+ Iterator<ScanDetail> it = mMap.values().iterator();
+ return it.hasNext() ? it.next() : null;
+ }
+
+ Collection<String> keySet() {
+ return mMap.keySet();
+ }
+
+ Collection<ScanDetail> values() {
+ return mMap.values();
+ }
+
+ public void trim(int num) {
+ int currentSize = mMap.size();
+ if (currentSize <= num) {
+ return; // Nothing to trim
+ }
+ ArrayList<ScanDetail> list = new ArrayList<ScanDetail>(mMap.values());
+ if (list.size() != 0) {
+ // Sort by descending timestamp
+ Collections.sort(list, new Comparator() {
+ public int compare(Object o1, Object o2) {
+ ScanDetail a = (ScanDetail) o1;
+ ScanDetail b = (ScanDetail) o2;
+ if (a.getSeen() > b.getSeen()) {
+ return 1;
+ }
+ if (a.getSeen() < b.getSeen()) {
+ return -1;
+ }
+ return a.getBSSIDString().compareTo(b.getBSSIDString());
+ }
+ });
+ }
+ for (int i = 0; i < currentSize - num ; i++) {
+ // Remove oldest results from scan cache
+ ScanDetail result = list.get(i);
+ mMap.remove(result.getBSSIDString());
+ mPasspointMatches.remove(result.getBSSIDString());
+ }
+ }
+
+ /* @hide */
+ private ArrayList<ScanDetail> sort() {
+ ArrayList<ScanDetail> list = new ArrayList<ScanDetail>(mMap.values());
+ if (list.size() != 0) {
+ Collections.sort(list, new Comparator() {
+ public int compare(Object o1, Object o2) {
+ ScanResult a = ((ScanDetail)o1).getScanResult();
+ ScanResult b = ((ScanDetail)o2).getScanResult();
+ if (a.numIpConfigFailures > b.numIpConfigFailures) {
+ return 1;
+ }
+ if (a.numIpConfigFailures < b.numIpConfigFailures) {
+ return -1;
+ }
+ if (a.seen > b.seen) {
+ return -1;
+ }
+ if (a.seen < b.seen) {
+ return 1;
+ }
+ if (a.level > b.level) {
+ return -1;
+ }
+ if (a.level < b.level) {
+ return 1;
+ }
+ return a.BSSID.compareTo(b.BSSID);
+ }
+ });
+ }
+ return list;
+ }
+
+ public WifiConfiguration.Visibility getVisibilityByRssi(long age) {
+ WifiConfiguration.Visibility status = new WifiConfiguration.Visibility();
+
+ long now_ms = System.currentTimeMillis();
+ long now_elapsed_ms = SystemClock.elapsedRealtime();
+ for(ScanDetail scanDetail : values()) {
+ ScanResult result = scanDetail.getScanResult();
+ if (scanDetail.getSeen() == 0)
+ continue;
+
+ if (result.is5GHz()) {
+ //strictly speaking: [4915, 5825]
+ //number of known BSSID on 5GHz band
+ status.num5 = status.num5 + 1;
+ } else if (result.is24GHz()) {
+ //strictly speaking: [2412, 2482]
+ //number of known BSSID on 2.4Ghz band
+ status.num24 = status.num24 + 1;
+ }
+
+ if (result.timestamp != 0) {
+ if (DBG) {
+ Log.e("getVisibilityByRssi", " considering " + result.SSID + " " + result.BSSID
+ + " elapsed=" + now_elapsed_ms + " timestamp=" + result.timestamp
+ + " age = " + age);
+ }
+ if ((now_elapsed_ms - (result.timestamp/1000)) > age) continue;
+ } else {
+ // This check the time at which we have received the scan result from supplicant
+ if ((now_ms - result.seen) > age) continue;
+ }
+
+ if (result.is5GHz()) {
+ if (result.level > status.rssi5) {
+ status.rssi5 = result.level;
+ status.age5 = result.seen;
+ status.BSSID5 = result.BSSID;
+ }
+ } else if (result.is24GHz()) {
+ if (result.level > status.rssi24) {
+ status.rssi24 = result.level;
+ status.age24 = result.seen;
+ status.BSSID24 = result.BSSID;
+ }
+ }
+ }
+
+ return status;
+ }
+
+ public WifiConfiguration.Visibility getVisibilityByPasspointMatch(long age) {
+
+ long now_ms = System.currentTimeMillis();
+ PasspointMatchInfo pmiBest24 = null, pmiBest5 = null;
+
+ for(PasspointMatchInfo pmi : mPasspointMatches.values()) {
+ ScanDetail scanDetail = pmi.getScanDetail();
+ if (scanDetail == null) continue;
+ ScanResult result = scanDetail.getScanResult();
+ if (result == null) continue;
+
+ if (scanDetail.getSeen() == 0)
+ continue;
+
+ if ((now_ms - result.seen) > age) continue;
+
+ if (result.is5GHz()) {
+ if (pmiBest5 == null || pmiBest5.compareTo(pmi) < 0) {
+ pmiBest5 = pmi;
+ }
+ } else if (result.is24GHz()) {
+ if (pmiBest24 == null || pmiBest24.compareTo(pmi) < 0) {
+ pmiBest24 = pmi;
+ }
+ }
+ }
+
+ WifiConfiguration.Visibility status = new WifiConfiguration.Visibility();
+ String logMsg = "Visiblity by passpoint match returned ";
+ if (pmiBest5 != null) {
+ ScanResult result = pmiBest5.getScanDetail().getScanResult();
+ status.rssi5 = result.level;
+ status.age5 = result.seen;
+ status.BSSID5 = result.BSSID;
+ logMsg += "5 GHz BSSID of " + result.BSSID;
+ }
+ if (pmiBest24 != null) {
+ ScanResult result = pmiBest24.getScanDetail().getScanResult();
+ status.rssi24 = result.level;
+ status.age24 = result.seen;
+ status.BSSID24 = result.BSSID;
+ logMsg += "2.4 GHz BSSID of " + result.BSSID;
+ }
+
+ Log.d(TAG, logMsg);
+
+ return status;
+ }
+
+ public WifiConfiguration.Visibility getVisibility(long age) {
+ if (mConfig.isPasspoint()) {
+ return getVisibilityByPasspointMatch(age);
+ } else {
+ return getVisibilityByRssi(age);
+ }
+ }
+
+
+
+ @Override
+ public String toString() {
+ StringBuilder sbuf = new StringBuilder();
+ sbuf.append("Scan Cache: ").append('\n');
+
+ ArrayList<ScanDetail> list = sort();
+ long now_ms = System.currentTimeMillis();
+ if (list.size() > 0) {
+ for (ScanDetail scanDetail : list) {
+ ScanResult result = scanDetail.getScanResult();
+ long milli = now_ms - scanDetail.getSeen();
+ long ageSec = 0;
+ long ageMin = 0;
+ long ageHour = 0;
+ long ageMilli = 0;
+ long ageDay = 0;
+ if (now_ms > scanDetail.getSeen() && scanDetail.getSeen() > 0) {
+ ageMilli = milli % 1000;
+ ageSec = (milli / 1000) % 60;
+ ageMin = (milli / (60*1000)) % 60;
+ ageHour = (milli / (60*60*1000)) % 24;
+ ageDay = (milli / (24*60*60*1000));
+ }
+ sbuf.append("{").append(result.BSSID).append(",").append(result.frequency);
+ sbuf.append(",").append(String.format("%3d", result.level));
+ if (result.autoJoinStatus > 0) {
+ sbuf.append(",st=").append(result.autoJoinStatus);
+ }
+ if (ageSec > 0 || ageMilli > 0) {
+ sbuf.append(String.format(",%4d.%02d.%02d.%02d.%03dms", ageDay,
+ ageHour, ageMin, ageSec, ageMilli));
+ }
+ if (result.numIpConfigFailures > 0) {
+ sbuf.append(",ipfail=");
+ sbuf.append(result.numIpConfigFailures);
+ }
+ sbuf.append("} ");
+ }
+ sbuf.append('\n');
+ }
+
+ return sbuf.toString();
+ }
+
+}
diff --git a/service/java/com/android/server/wifi/WifiApConfigStore.java b/service/java/com/android/server/wifi/WifiApConfigStore.java
index eb35db8..ed5ddf7 100644
--- a/service/java/com/android/server/wifi/WifiApConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiApConfigStore.java
@@ -37,6 +37,7 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.UUID;
/**
@@ -51,7 +52,7 @@
private static final String AP_CONFIG_FILE = Environment.getDataDirectory() +
"/misc/wifi/softap.conf";
- private static final int AP_CONFIG_FILE_VERSION = 1;
+ private static final int AP_CONFIG_FILE_VERSION = 2;
private State mDefaultState = new DefaultState();
private State mInactiveState = new InactiveState();
@@ -59,6 +60,7 @@
private WifiConfiguration mWifiApConfig = null;
private AsyncChannel mReplyChannel = new AsyncChannel();
+ public ArrayList <Integer> allowed2GChannel = null;
WifiApConfigStore(Context context, Handler target) {
super(TAG, target.getLooper());
@@ -69,6 +71,17 @@
addState(mActiveState, mDefaultState);
setInitialState(mInactiveState);
+ String ap2GChannelListStr = (mContext.getResources().getString(
+ R.string.config_wifi_framework_sap_2G_channel_list));
+ Log.d(TAG, "2G band allowed channels are:" + ap2GChannelListStr);
+
+ if (ap2GChannelListStr != null) {
+ allowed2GChannel = new ArrayList<Integer>();
+ String channelList[] = ap2GChannelListStr.split(",");
+ for (String tmp : channelList) {
+ allowed2GChannel.add(Integer.parseInt(tmp));
+ }
+ }
}
public static WifiApConfigStore makeWifiApConfigStore(Context context, Handler target) {
@@ -100,9 +113,9 @@
public boolean processMessage(Message message) {
switch (message.what) {
case WifiStateMachine.CMD_SET_AP_CONFIG:
- WifiConfiguration config = (WifiConfiguration) message.obj;
+ WifiConfiguration config = (WifiConfiguration)message.obj;
if (config.SSID != null) {
- mWifiApConfig = (WifiConfiguration) message.obj;
+ mWifiApConfig = config;
transitionTo(mActiveState);
} else {
Log.e(TAG, "Try to setup AP config without SSID: " + message);
@@ -150,17 +163,24 @@
AP_CONFIG_FILE)));
int version = in.readInt();
- if (version != 1) {
+ if ((version != 1) && (version != 2)) {
Log.e(TAG, "Bad version on hotspot configuration file, set defaults");
setDefaultApConfiguration();
return;
}
config.SSID = in.readUTF();
+
+ if (version >= 2) {
+ config.apBand = in.readInt();
+ config.apChannel = in.readInt();
+ }
+
int authType = in.readInt();
config.allowedKeyManagement.set(authType);
if (authType != KeyMgmt.NONE) {
config.preSharedKey = in.readUTF();
}
+
mWifiApConfig = config;
} catch (IOException ignore) {
setDefaultApConfiguration();
@@ -185,6 +205,8 @@
out.writeInt(AP_CONFIG_FILE_VERSION);
out.writeUTF(config.SSID);
+ out.writeInt(config.apBand);
+ out.writeInt(config.apChannel);
int authType = config.getAuthType();
out.writeInt(authType);
if(authType != KeyMgmt.NONE) {
diff --git a/service/java/com/android/server/wifi/WifiAutoJoinController.java b/service/java/com/android/server/wifi/WifiAutoJoinController.java
index c7da179..a1a9a82 100644
--- a/service/java/com/android/server/wifi/WifiAutoJoinController.java
+++ b/service/java/com/android/server/wifi/WifiAutoJoinController.java
@@ -20,17 +20,23 @@
import android.net.NetworkKey;
import android.net.NetworkScoreManager;
import android.net.WifiKey;
-import android.net.wifi.*;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
-import android.os.SystemClock;
-import android.provider.Settings;
+import android.net.wifi.WifiConnectionStatistics;
import android.os.Process;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import android.os.SystemClock;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
import java.util.ArrayList;
-import java.util.Iterator;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
/**
@@ -59,12 +65,13 @@
private String mCurrentConfigurationKey = null; //used by autojoin
- private HashMap<String, ScanResult> scanResultCache =
- new HashMap<String, ScanResult>();
+ private final HashMap<String, ScanDetail> scanResultCache = new HashMap<>();
private WifiConnectionStatistics mWifiConnectionStatistics;
- /** Whether to allow connections to untrusted networks. */
+ /**
+ * Whether to allow connections to untrusted networks.
+ */
private boolean mAllowUntrustedConnections = false;
/* For debug purpose only: if the scored override a score */
@@ -75,7 +82,9 @@
// Lose some temporary blacklisting after 30 minutes
private final static long loseBlackListSoftMilli = 1000 * 60 * 30;
- /** @see android.provider.Settings.Global#WIFI_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS */
+ /**
+ * @see android.provider.Settings.Global#WIFI_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS
+ */
private static final long DEFAULT_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS = 1000 * 60; // 1 minute
public static final int AUTO_JOIN_IDLE = 0;
@@ -85,6 +94,8 @@
public static final int HIGH_THRESHOLD_MODIFIER = 5;
+ public static final int MAX_RSSI_DELTA = 50;
+
// Below are AutoJoin wide parameters indicating if we should be aggressive before joining
// weak network. Note that we cannot join weak network that are going to be marked as unanted by
// ConnectivityService because this will trigger link flapping.
@@ -123,7 +134,7 @@
}
void enableVerboseLogging(int verbose) {
- if (verbose > 0 ) {
+ if (verbose > 0) {
DBG = true;
VDBG = true;
} else {
@@ -134,7 +145,6 @@
/**
* Flush out scan results older than mScanResultMaximumAge
- *
*/
private void ageScanResultsOut(int delay) {
if (delay <= 0) {
@@ -146,18 +156,67 @@
+ Integer.valueOf(scanResultCache.size()) + " now " + Long.valueOf(milli));
}
- Iterator<HashMap.Entry<String,ScanResult>> iter = scanResultCache.entrySet().iterator();
+ Iterator<HashMap.Entry<String, ScanDetail>> iter = scanResultCache.entrySet().iterator();
while (iter.hasNext()) {
- HashMap.Entry<String,ScanResult> entry = iter.next();
- ScanResult result = entry.getValue();
-
- if ((result.seen + delay) < milli) {
+ HashMap.Entry<String, ScanDetail> entry = iter.next();
+ ScanDetail scanDetail = entry.getValue();
+ if ((scanDetail.getSeen() + delay) < milli) {
iter.remove();
}
}
}
- int addToScanCache(List<ScanResult> scanList) {
+
+ void averageRssiAndRemoveFromCache(ScanResult result) {
+ // Fetch the previous instance for this result
+ ScanDetail sd = scanResultCache.get(result.BSSID);
+ if (sd != null) {
+ ScanResult sr = sd.getScanResult();
+ if (mWifiConfigStore.scanResultRssiLevelPatchUp != 0
+ && result.level == 0
+ && sr.level < -20) {
+ // A 'zero' RSSI reading is most likely a chip problem which returns
+ // an unknown RSSI, hence ignore it
+ result.level = sr.level;
+ }
+
+ // If there was a previous cache result for this BSSID, average the RSSI values
+ result.averageRssi(sr.level, sr.seen, mScanResultMaximumAge);
+
+ // Remove the previous Scan Result - this is not necessary
+ scanResultCache.remove(result.BSSID);
+ } else if (mWifiConfigStore.scanResultRssiLevelPatchUp != 0 && result.level == 0) {
+ // A 'zero' RSSI reading is most likely a chip problem which returns
+ // an unknown RSSI, hence initialize it to a sane value
+ result.level = mWifiConfigStore.scanResultRssiLevelPatchUp;
+ }
+ }
+
+ void addToUnscoredNetworks(ScanResult result, List<NetworkKey> unknownScanResults) {
+ WifiKey wkey;
+ // Quoted SSIDs are the only one valid at this stage
+ try {
+ wkey = new WifiKey("\"" + result.SSID + "\"", result.BSSID);
+ } catch (IllegalArgumentException e) {
+ logDbg("AutoJoinController: received badly encoded SSID=[" + result.SSID +
+ "] ->skipping this network");
+ wkey = null;
+ }
+ if (wkey != null) {
+ NetworkKey nkey = new NetworkKey(wkey);
+ //if we don't know this scan result then request a score from the scorer
+ unknownScanResults.add(nkey);
+ }
+ if (VDBG) {
+ String cap = "";
+ if (result.capabilities != null)
+ cap = result.capabilities;
+ logDbg(result.SSID + " " + result.BSSID + " rssi="
+ + result.level + " cap " + cap + " tsf " + result.timestamp + " is not scored");
+ }
+ }
+
+ int addToScanCache(List<ScanDetail> scanList) {
int numScanResultsKnown = 0; // Record number of scan results we knew about
WifiConfiguration associatedConfig = null;
boolean didAssociate = false;
@@ -165,56 +224,24 @@
ArrayList<NetworkKey> unknownScanResults = new ArrayList<NetworkKey>();
- for(ScanResult result: scanList) {
+ for (ScanDetail scanDetail : scanList) {
+ ScanResult result = scanDetail.getScanResult();
if (result.SSID == null) continue;
- // Make sure we record the last time we saw this result
- result.seen = System.currentTimeMillis();
-
- // Fetch the previous instance for this result
- ScanResult sr = scanResultCache.get(result.BSSID);
- if (sr != null) {
- if (mWifiConfigStore.scanResultRssiLevelPatchUp != 0
- && result.level == 0
- && sr.level < -20) {
- // A 'zero' RSSI reading is most likely a chip problem which returns
- // an unknown RSSI, hence ignore it
- result.level = sr.level;
- }
-
- // If there was a previous cache result for this BSSID, average the RSSI values
- result.averageRssi(sr.level, sr.seen, mScanResultMaximumAge);
-
- // Remove the previous Scan Result - this is not necessary
- scanResultCache.remove(result.BSSID);
- } else if (mWifiConfigStore.scanResultRssiLevelPatchUp != 0 && result.level == 0) {
- // A 'zero' RSSI reading is most likely a chip problem which returns
- // an unknown RSSI, hence initialize it to a sane value
- result.level = mWifiConfigStore.scanResultRssiLevelPatchUp;
+ if (VDBG) {
+ logDbg(" addToScanCache " + result.SSID + " " + result.BSSID
+ + " tsf=" + result.timestamp
+ + " now= " + now + " uptime=" + SystemClock.uptimeMillis()
+ + " elapsed=" + SystemClock.elapsedRealtime());
}
+ // Make sure we record the last time we saw this result
+ scanDetail.setSeen();
+
+ averageRssiAndRemoveFromCache(result);
+
if (!mNetworkScoreCache.isScoredNetwork(result)) {
- WifiKey wkey;
- // Quoted SSIDs are the only one valid at this stage
- try {
- wkey = new WifiKey("\"" + result.SSID + "\"", result.BSSID);
- } catch (IllegalArgumentException e) {
- logDbg("AutoJoinController: received badly encoded SSID=[" + result.SSID +
- "] ->skipping this network");
- wkey = null;
- }
- if (wkey != null) {
- NetworkKey nkey = new NetworkKey(wkey);
- //if we don't know this scan result then request a score from the scorer
- unknownScanResults.add(nkey);
- }
- if (VDBG) {
- String cap = "";
- if (result.capabilities != null)
- cap = result.capabilities;
- logDbg(result.SSID + " " + result.BSSID + " rssi="
- + result.level + " cap " + cap + " is not scored");
- }
+ addToUnscoredNetworks(result, unknownScanResults);
} else {
if (VDBG) {
String cap = "";
@@ -227,29 +254,15 @@
}
// scanResultCache.put(result.BSSID, new ScanResult(result));
- scanResultCache.put(result.BSSID, result);
+ scanResultCache.put(result.BSSID, scanDetail);
// Add this BSSID to the scanResultCache of a Saved WifiConfiguration
- didAssociate = mWifiConfigStore.updateSavedNetworkHistory(result);
+ didAssociate = mWifiConfigStore.updateSavedNetworkHistory(scanDetail);
// If not successful, try to associate this BSSID to an existing Saved WifiConfiguration
if (!didAssociate) {
// We couldn't associate the scan result to a Saved WifiConfiguration
// Hence it is untrusted
result.untrusted = true;
- associatedConfig = mWifiConfigStore.associateWithConfiguration(result);
- if (associatedConfig != null && associatedConfig.SSID != null) {
- if (VDBG) {
- logDbg("addToScanCache save associated config "
- + associatedConfig.SSID + " with " + result.SSID
- + " status " + associatedConfig.autoJoinStatus
- + " reason " + associatedConfig.disableReason
- + " tsp " + associatedConfig.blackListTimestamp
- + " was " + (now - associatedConfig.blackListTimestamp));
- }
- mWifiStateMachine.sendMessage(
- WifiStateMachine.CMD_AUTO_SAVE_NETWORK, associatedConfig);
- didAssociate = true;
- }
} else {
// If the scan result has been blacklisted fir 18 hours -> unblacklist
if ((now - result.blackListTimestamp) > loseBlackListHardMilli) {
@@ -258,7 +271,7 @@
}
if (didAssociate) {
numScanResultsKnown++;
- result.isAutoJoinCandidate ++;
+ result.isAutoJoinCandidate++;
} else {
result.isAutoJoinCandidate = 0;
}
@@ -279,26 +292,26 @@
void logDbg(String message, boolean stackTrace) {
if (stackTrace) {
- Log.e(TAG, message + " stack:"
+ Log.d(TAG, message + " stack:"
+ Thread.currentThread().getStackTrace()[2].getMethodName() + " - "
+ Thread.currentThread().getStackTrace()[3].getMethodName() + " - "
+ Thread.currentThread().getStackTrace()[4].getMethodName() + " - "
+ Thread.currentThread().getStackTrace()[5].getMethodName());
} else {
- Log.e(TAG, message);
+ Log.d(TAG, message);
}
}
// Called directly from WifiStateMachine
int newSupplicantResults(boolean doAutoJoin) {
int numScanResultsKnown;
- List<ScanResult> scanList = mWifiStateMachine.getScanResultsListNoCopyUnsync();
+ List<ScanDetail> scanList = mWifiStateMachine.getScanResultsListNoCopyUnsync();
numScanResultsKnown = addToScanCache(scanList);
ageScanResultsOut(mScanResultMaximumAge);
if (DBG) {
logDbg("newSupplicantResults size=" + Integer.valueOf(scanResultCache.size())
- + " known=" + numScanResultsKnown + " "
- + doAutoJoin);
+ + " known=" + numScanResultsKnown + " "
+ + doAutoJoin);
}
if (doAutoJoin) {
attemptAutoJoin();
@@ -316,7 +329,7 @@
* results as normal.
*/
void newHalScanResults() {
- List<ScanResult> scanList = null;//mWifiScanner.syncGetScanResultsList();
+ List<ScanDetail> scanList = null;//mWifiScanner.syncGetScanResultsList();
String akm = WifiParser.parse_akm(null, null);
logDbg(akm);
addToScanCache(scanList);
@@ -326,7 +339,7 @@
}
/**
- * network link quality changed, called directly from WifiTrafficPoller,
+ * network link quality changed, called directly from WifiTrafficPoller,
* or by listening to Link Quality intent
*/
void linkQualitySignificantChange() {
@@ -350,7 +363,7 @@
if (currentNetwork == null) {
// Return any absurdly high score, if we are not connected there is no current
// network to...
- return 1000;
+ return 1000;
}
if (candidate.configKey(true).equals(currentNetwork.configKey(true))) {
@@ -375,7 +388,7 @@
// above RSSI/scorer based selection of linked configuration (+/- 50)
// by reducing order by -100
order = order - 100;
- if (VDBG) {
+ if (VDBG) {
logDbg(" ...and prefers -100 " + currentNetwork.configKey()
+ " over " + candidate.configKey()
+ " because it is the last selected -> "
@@ -388,7 +401,7 @@
// above RSSI/scorer based selection of linked configuration (+/- 50)
// by increasing order by +100
order = order + 100;
- if (VDBG) {
+ if (VDBG) {
logDbg(" ...and prefers +100 " + candidate.configKey()
+ " over " + currentNetwork.configKey()
+ " because it is the last selected -> "
@@ -408,7 +421,7 @@
*
* @param netId
* @param userTriggered : if the update come from WiFiManager
- * @param connect : if the update includes a connect
+ * @param connect : if the update includes a connect
*/
public void updateConfigurationHistory(int netId, boolean userTriggered, boolean connect) {
WifiConfiguration selected = mWifiConfigStore.getWifiConfiguration(netId);
@@ -472,30 +485,17 @@
continue;
}
- // Compare RSSI values so as to evaluate the strength of the user preference
- int order = compareWifiConfigurationsRSSI(config, selected, null);
-
- if (order < -30) {
- // Selected configuration is worse than the visible configuration
- // hence register a strong choice so as autojoin cannot override this
- // for instance, the user has select a network
- // with 1 bar over a network with 3 bars...
- choice = 60;
- } else if (order < -20) {
- choice = 50;
- } else if (order < -10) {
- choice = 40;
- } else if (order < 20) {
- // Selected configuration is about same or has a slightly better RSSI
- // hence register a weaker choice, here a difference of at least +/-30 in
- // RSSI comparison triggered by autoJoin will override the choice
- choice = 30;
- } else {
- // Selected configuration is better than the visible configuration
- // hence we do not know if the user prefers this configuration strongly
- choice = 20;
+ // If the selection was made while config was visible with reasonably good RSSI
+ // then register the user preference, else ignore
+ if (config.visibility == null ||
+ (config.visibility.rssi24 < mWifiConfigStore.thresholdBadRssi24.get()
+ && config.visibility.rssi5 < mWifiConfigStore.thresholdBadRssi5.get())
+ ) {
+ continue;
}
+ choice = MAX_RSSI_DELTA + 10; // Make sure the choice overrides the RSSI diff
+
// The selected configuration was preferred over a recently seen config
// hence remember the user's choice:
// add the recently seen config to the selected's connectChoices array
@@ -508,11 +508,6 @@
+ " over " + config.configKey(true)
+ " choice " + Integer.toString(choice));
- Integer currentChoice = selected.connectChoices.get(config.configKey(true));
- if (currentChoice != null) {
- // User has made this choice multiple time in a row, so bump up a lot
- choice += currentChoice.intValue();
- }
// Add the visible config to the selected's connect choice list
selected.connectChoices.put(config.configKey(true), choice);
@@ -525,21 +520,21 @@
config.connectChoices.remove(selected.configKey(true));
if (selected.linkedConfigurations != null) {
- // Remove the selected's linked configuration from the
- // recently seen config's connectChoice list
- for (String key : selected.linkedConfigurations.keySet()) {
- config.connectChoices.remove(key);
- }
+ // Remove the selected's linked configuration from the
+ // recently seen config's connectChoice list
+ for (String key : selected.linkedConfigurations.keySet()) {
+ config.connectChoices.remove(key);
+ }
}
}
}
if (found == false) {
- // We haven't found the configuration that the user just selected in our
- // scan cache.
- // In that case we will need a new scan before attempting to connect to this
- // configuration anyhow and thus we can process the scan results then.
- logDbg("updateConfigurationHistory try to connect to an old network!! : "
- + selected.configKey());
+ // We haven't found the configuration that the user just selected in our
+ // scan cache.
+ // In that case we will need a new scan before attempting to connect to this
+ // configuration anyhow and thus we can process the scan results then.
+ logDbg("updateConfigurationHistory try to connect to an old network!! : "
+ + selected.configKey());
}
if (selected.connectChoices != null) {
@@ -556,39 +551,44 @@
}
}
- int getConnectChoice(WifiConfiguration source, WifiConfiguration target) {
- Integer choice = null;
+ int getConnectChoice(WifiConfiguration source, WifiConfiguration target, boolean strict) {
+ int choice = 0;
if (source == null || target == null) {
return 0;
}
if (source.connectChoices != null
&& source.connectChoices.containsKey(target.configKey(true))) {
- choice = source.connectChoices.get(target.configKey(true));
+ Integer val = source.connectChoices.get(target.configKey(true));
+ if (val != null) {
+ choice = val;
+ }
} else if (source.linkedConfigurations != null) {
for (String key : source.linkedConfigurations.keySet()) {
WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(key);
if (config != null) {
if (config.connectChoices != null) {
- choice = config.connectChoices.get(target.configKey(true));
+ Integer val = config.connectChoices.get(target.configKey(true));
+ if (val != null) {
+ choice = val;
+ }
}
}
}
}
- if (choice == null) {
- //We didn't find the connect choice
- return 0;
- } else {
- if (choice.intValue() < 0) {
- choice = 20; // Compatibility with older files
- }
- return choice.intValue();
+ if (!strict && choice == 0) {
+ // We didn't find the connect choice; fallback to some default choices
+ int sourceScore = getSecurityScore(source);
+ int targetScore = getSecurityScore(target);
+ choice = sourceScore - targetScore;
}
+
+ return choice;
}
- int compareWifiConfigurationsFromVisibility(WifiConfiguration a, int aRssiBoost,
- WifiConfiguration b, int bRssiBoost) {
+ int compareWifiConfigurationsFromVisibility(WifiConfiguration.Visibility a, int aRssiBoost,
+ String dbgA, WifiConfiguration.Visibility b, int bRssiBoost, String dbgB) {
int aRssiBoost5 = 0; // 5GHz RSSI boost to apply for purpose band selection (5GHz pref)
int bRssiBoost5 = 0; // 5GHz RSSI boost to apply for purpose band selection (5GHz pref)
@@ -606,17 +606,17 @@
* This implements band preference where we prefer 5GHz if RSSI5 is good enough, whereas
* we prefer 2.4GHz otherwise.
*/
- aRssiBoost5 = rssiBoostFrom5GHzRssi(a.visibility.rssi5, a.configKey() + "->");
- bRssiBoost5 = rssiBoostFrom5GHzRssi(b.visibility.rssi5, b.configKey() + "->");
+ aRssiBoost5 = rssiBoostFrom5GHzRssi(a.rssi5, dbgA + "->");
+ bRssiBoost5 = rssiBoostFrom5GHzRssi(b.rssi5, dbgB + "->");
// Select which band to use for a
- if (a.visibility.rssi5 + aRssiBoost5 > a.visibility.rssi24) {
+ if (a.rssi5 + aRssiBoost5 > a.rssi24) {
// Prefer a's 5GHz
aPrefers5GHz = true;
}
// Select which band to use for b
- if (b.visibility.rssi5 + bRssiBoost5 > b.visibility.rssi24) {
+ if (b.rssi5 + bRssiBoost5 > b.rssi24) {
// Prefer b's 5GHz
bPrefers5GHz = true;
}
@@ -626,13 +626,13 @@
// If both a and b are on 5GHz then we don't apply the 5GHz RSSI boost to either
// one, but directly compare the RSSI values, this improves stability,
// since the 5GHz RSSI boost can introduce large fluctuations
- aScore = a.visibility.rssi5 + aRssiBoost;
+ aScore = a.rssi5 + aRssiBoost;
} else {
// If only a is on 5GHz, then apply the 5GHz preference boost to a
- aScore = a.visibility.rssi5 + aRssiBoost + aRssiBoost5;
+ aScore = a.rssi5 + aRssiBoost + aRssiBoost5;
}
} else {
- aScore = a.visibility.rssi24 + aRssiBoost;
+ aScore = a.rssi24 + aRssiBoost;
}
if (bPrefers5GHz) {
@@ -640,29 +640,30 @@
// If both a and b are on 5GHz then we don't apply the 5GHz RSSI boost to either
// one, but directly compare the RSSI values, this improves stability,
// since the 5GHz RSSI boost can introduce large fluctuations
- bScore = b.visibility.rssi5 + bRssiBoost;
+ bScore = b.rssi5 + bRssiBoost;
} else {
// If only b is on 5GHz, then apply the 5GHz preference boost to b
- bScore = b.visibility.rssi5 + bRssiBoost + bRssiBoost5;
+ bScore = b.rssi5 + bRssiBoost + bRssiBoost5;
}
} else {
- bScore = b.visibility.rssi24 + bRssiBoost;
+ bScore = b.rssi24 + bRssiBoost;
}
+
if (VDBG) {
- logDbg(" " + a.configKey() + " is5=" + aPrefers5GHz + " score=" + aScore
- + " " + b.configKey() + " is5=" + bPrefers5GHz + " score=" + bScore);
+ logDbg(" " + dbgA + " is5=" + aPrefers5GHz + " score=" + aScore
+ + " " + dbgB + " is5=" + bPrefers5GHz + " score=" + bScore);
}
// Debug only, record RSSI comparison parameters
- if (a.visibility != null) {
- a.visibility.score = aScore;
- a.visibility.currentNetworkBoost = aRssiBoost;
- a.visibility.bandPreferenceBoost = aRssiBoost5;
+ if (a != null) {
+ a.score = aScore;
+ a.currentNetworkBoost = aRssiBoost;
+ a.bandPreferenceBoost = aRssiBoost5;
}
- if (b.visibility != null) {
- b.visibility.score = bScore;
- b.visibility.currentNetworkBoost = bRssiBoost;
- b.visibility.bandPreferenceBoost = bRssiBoost5;
+ if (b != null) {
+ b.score = bScore;
+ b.currentNetworkBoost = bRssiBoost;
+ b.bandPreferenceBoost = bRssiBoost5;
}
// Compare a and b
@@ -686,9 +687,6 @@
int aRssiBoost = 0;
int bRssiBoost = 0;
- int scoreA;
- int scoreB;
-
// Retrieve the visibility
WifiConfiguration.Visibility astatus = a.visibility;
WifiConfiguration.Visibility bstatus = b.visibility;
@@ -707,23 +705,25 @@
}
}
- if (VDBG) {
+ if (VDBG) {
logDbg(" compareWifiConfigurationsRSSI: " + a.configKey()
- + " rssi=" + Integer.toString(astatus.rssi24)
- + "," + Integer.toString(astatus.rssi5)
- + " boost=" + Integer.toString(aRssiBoost)
- + " " + b.configKey() + " rssi="
- + Integer.toString(bstatus.rssi24) + ","
- + Integer.toString(bstatus.rssi5)
- + " boost=" + Integer.toString(bRssiBoost)
+ + " rssi=" + Integer.toString(astatus.rssi24)
+ + "," + Integer.toString(astatus.rssi5)
+ + " boost=" + Integer.toString(aRssiBoost)
+ + " " + b.configKey() + " rssi="
+ + Integer.toString(bstatus.rssi24) + ","
+ + Integer.toString(bstatus.rssi5)
+ + " boost=" + Integer.toString(bRssiBoost)
);
}
- order = compareWifiConfigurationsFromVisibility(a, aRssiBoost, b, bRssiBoost);
+ order = compareWifiConfigurationsFromVisibility(
+ a.visibility, aRssiBoost, a.configKey(),
+ b.visibility, bRssiBoost, b.configKey());
- // Normalize the order to [-50, +50]
- if (order > 50) order = 50;
- else if (order < -50) order = -50;
+ // Normalize the order to [-50, +50] = [ -MAX_RSSI_DELTA, MAX_RSSI_DELTA]
+ if (order > MAX_RSSI_DELTA) order = MAX_RSSI_DELTA;
+ else if (order < -MAX_RSSI_DELTA) order = -MAX_RSSI_DELTA;
if (VDBG) {
String prefer = " = ";
@@ -750,64 +750,85 @@
/**
* b/18490330 only use scorer for untrusted networks
- *
- int compareWifiConfigurationsWithScorer(WifiConfiguration a, WifiConfiguration b) {
-
- boolean aIsActive = false;
- boolean bIsActive = false;
-
- // Apply Hysteresis : boost RSSI of current configuration before
- // looking up the score
- if (null != mCurrentConfigurationKey) {
- if (a.configKey().equals(mCurrentConfigurationKey)) {
- aIsActive = true;
- } else if (b.configKey().equals(mCurrentConfigurationKey)) {
- bIsActive = true;
- }
- }
- int scoreA = getConfigNetworkScore(a, mScanResultAutoJoinAge, aIsActive);
- int scoreB = getConfigNetworkScore(b, mScanResultAutoJoinAge, bIsActive);
-
- // Both configurations need to have a score for the scorer to be used
- // ...and the scores need to be different:-)
- if (scoreA == WifiNetworkScoreCache.INVALID_NETWORK_SCORE
- || scoreB == WifiNetworkScoreCache.INVALID_NETWORK_SCORE) {
- if (VDBG) {
- logDbg(" compareWifiConfigurationsWithScorer no-scores: "
- + a.configKey()
- + " "
- + b.configKey());
- }
- return 0;
- }
-
- if (VDBG) {
- String prefer = " = ";
- if (scoreA < scoreB) {
- prefer = " < ";
- } if (scoreA > scoreB) {
- prefer = " > ";
- }
- logDbg(" compareWifiConfigurationsWithScorer " + a.configKey()
- + " rssi=(" + a.visibility.rssi24
- + "," + a.visibility.rssi5
- + ") num=(" + a.visibility.num24
- + "," + a.visibility.num5 + ")"
- + " sc=" + scoreA
- + prefer + b.configKey()
- + " rssi=(" + b.visibility.rssi24
- + "," + b.visibility.rssi5
- + ") num=(" + b.visibility.num24
- + "," + b.visibility.num5 + ")"
- + " sc=" + scoreB
- + " -> " + Integer.toString(scoreB - scoreA));
- }
-
- // If scoreA > scoreB, the comparison is descending hence the return value is negative
- return scoreB - scoreA;
- }
+ * <p/>
+ * int compareWifiConfigurationsWithScorer(WifiConfiguration a, WifiConfiguration b) {
+ * <p/>
+ * boolean aIsActive = false;
+ * boolean bIsActive = false;
+ * <p/>
+ * // Apply Hysteresis : boost RSSI of current configuration before
+ * // looking up the score
+ * if (null != mCurrentConfigurationKey) {
+ * if (a.configKey().equals(mCurrentConfigurationKey)) {
+ * aIsActive = true;
+ * } else if (b.configKey().equals(mCurrentConfigurationKey)) {
+ * bIsActive = true;
+ * }
+ * }
+ * int scoreA = getConfigNetworkScore(a, mScanResultAutoJoinAge, aIsActive);
+ * int scoreB = getConfigNetworkScore(b, mScanResultAutoJoinAge, bIsActive);
+ * <p/>
+ * // Both configurations need to have a score for the scorer to be used
+ * // ...and the scores need to be different:-)
+ * if (scoreA == WifiNetworkScoreCache.INVALID_NETWORK_SCORE
+ * || scoreB == WifiNetworkScoreCache.INVALID_NETWORK_SCORE) {
+ * if (VDBG) {
+ * logDbg(" compareWifiConfigurationsWithScorer no-scores: "
+ * + a.configKey()
+ * + " "
+ * + b.configKey());
+ * }
+ * return 0;
+ * }
+ * <p/>
+ * if (VDBG) {
+ * String prefer = " = ";
+ * if (scoreA < scoreB) {
+ * prefer = " < ";
+ * } if (scoreA > scoreB) {
+ * prefer = " > ";
+ * }
+ * logDbg(" compareWifiConfigurationsWithScorer " + a.configKey()
+ * + " rssi=(" + a.visibility.rssi24
+ * + "," + a.visibility.rssi5
+ * + ") num=(" + a.visibility.num24
+ * + "," + a.visibility.num5 + ")"
+ * + " sc=" + scoreA
+ * + prefer + b.configKey()
+ * + " rssi=(" + b.visibility.rssi24
+ * + "," + b.visibility.rssi5
+ * + ") num=(" + b.visibility.num24
+ * + "," + b.visibility.num5 + ")"
+ * + " sc=" + scoreB
+ * + " -> " + Integer.toString(scoreB - scoreA));
+ * }
+ * <p/>
+ * // If scoreA > scoreB, the comparison is descending hence the return value is negative
+ * return scoreB - scoreA;
+ * }
*/
+ int getSecurityScore(WifiConfiguration config) {
+
+ if (TextUtils.isEmpty(config.SSID) == false) {
+ if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP)
+ || config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)
+ || config.allowedKeyManagement.get(KeyMgmt.WPA2_PSK)) {
+ /* enterprise or PSK networks get highest score */
+ return 100;
+ } else if (config.allowedKeyManagement.get(KeyMgmt.NONE)) {
+ /* open networks have lowest score */
+ return 33;
+ }
+ } else if (TextUtils.isEmpty(config.FQDN) == false) {
+ /* passpoint networks have medium preference */
+ return 66;
+ }
+
+ /* bad network */
+ return 0;
+ }
+
int compareWifiConfigurations(WifiConfiguration a, WifiConfiguration b) {
int order = 0;
boolean linked = false;
@@ -845,7 +866,7 @@
if (!linked) {
int choice;
- choice = getConnectChoice(a, b);
+ choice = getConnectChoice(a, b, false);
if (choice > 0) {
// a is of higher priority - descending
order = order - choice;
@@ -861,7 +882,7 @@
}
}
- choice = getConnectChoice(b, a);
+ choice = getConnectChoice(b, a, false);
if (choice > 0) {
// a is of lower priority - ascending
order = order + choice;
@@ -948,13 +969,13 @@
return 0;
}
if (rssi
- > mWifiConfigStore.bandPreferenceBoostThreshold5) {
+ > mWifiConfigStore.bandPreferenceBoostThreshold5.get()) {
// Boost by 2 dB for each point
// Start boosting at -65
// Boost by 20 if above -55
// Boost by 40 if abore -45
int boost = mWifiConfigStore.bandPreferenceBoostFactor5
- *(rssi - mWifiConfigStore.bandPreferenceBoostThreshold5);
+ * (rssi - mWifiConfigStore.bandPreferenceBoostThreshold5.get());
if (boost > 50) {
// 50 dB boost allows jumping from 2.4 to 5GHz
// consistently
@@ -967,38 +988,43 @@
}
if (rssi
- < mWifiConfigStore.bandPreferencePenaltyThreshold5) {
+ < mWifiConfigStore.bandPreferencePenaltyThreshold5.get()) {
// penalize if < -75
int boost = mWifiConfigStore.bandPreferencePenaltyFactor5
- *(rssi - mWifiConfigStore.bandPreferencePenaltyThreshold5);
+ * (rssi - mWifiConfigStore.bandPreferencePenaltyThreshold5.get());
return boost;
}
return 0;
}
- /**
- * attemptRoam() function implements the core of the same SSID switching algorithm
- *
- * Run thru all recent scan result of a WifiConfiguration and select the
- * best one.
- */
+
+ /**
+ * attemptRoam() function implements the core of the same SSID switching algorithm
+ * <p/>
+ * Run thru all recent scan result of a WifiConfiguration and select the
+ * best one.
+ */
public ScanResult attemptRoam(ScanResult a,
WifiConfiguration current, int age, String currentBSSID) {
if (current == null) {
- if (VDBG) {
+ if (VDBG) {
logDbg("attemptRoam not associated");
}
return a;
}
- if (current.scanResultCache == null) {
- if (VDBG) {
+
+ ScanDetailCache scanDetailCache =
+ mWifiConfigStore.getScanDetailCache(current);
+
+ if (scanDetailCache == null) {
+ if (VDBG) {
logDbg("attemptRoam no scan cache");
}
return a;
}
- if (current.scanResultCache.size() > 6) {
- if (VDBG) {
+ if (scanDetailCache.size() > 6) {
+ if (VDBG) {
logDbg("attemptRoam scan cache size "
- + current.scanResultCache.size() + " --> bail");
+ + scanDetailCache.size() + " --> bail");
}
// Implement same SSID roaming only for configurations
// that have less than 4 BSSIDs
@@ -1006,7 +1032,7 @@
}
if (current.BSSID != null && !current.BSSID.equals("any")) {
- if (DBG) {
+ if (DBG) {
logDbg("attemptRoam() BSSID is set "
+ current.BSSID + " -> bail");
}
@@ -1017,13 +1043,14 @@
// relative strength of 5 and 2.4 GHz BSSIDs
long nowMs = System.currentTimeMillis();
- for (ScanResult b : current.scanResultCache.values()) {
+ for (ScanDetail sd : scanDetailCache.values()) {
+ ScanResult b = sd.getScanResult();
int bRssiBoost5 = 0;
int aRssiBoost5 = 0;
int bRssiBoost = 0;
int aRssiBoost = 0;
- if ((b.seen == 0) || (b.BSSID == null)
- || ((nowMs - b.seen) > age)
+ if ((sd.getSeen() == 0) || (b.BSSID == null)
+ || ((nowMs - sd.getSeen()) > age)
|| b.autoJoinStatus != ScanResult.ENABLED
|| b.numIpConfigFailures > 8) {
continue;
@@ -1038,10 +1065,10 @@
if (b.numIpConfigFailures < (a.numIpConfigFailures - 1)) {
// Prefer a BSSID that doesn't have less number of Ip config failures
logDbg("attemptRoam: "
- + b.BSSID + " rssi=" + b.level + " ipfail=" +b.numIpConfigFailures
+ + b.BSSID + " rssi=" + b.level + " ipfail=" + b.numIpConfigFailures
+ " freq=" + b.frequency
+ " > "
- + a.BSSID + " rssi=" + a.level + " ipfail=" +a.numIpConfigFailures
+ + a.BSSID + " rssi=" + a.level + " ipfail=" + a.numIpConfigFailures
+ " freq=" + a.frequency);
a = b;
continue;
@@ -1050,14 +1077,14 @@
// Apply hysteresis: we favor the currentBSSID by giving it a boost
if (currentBSSID != null && currentBSSID.equals(b.BSSID)) {
// Reduce the benefit of hysteresis if RSSI <= -75
- if (b.level <= mWifiConfigStore.bandPreferencePenaltyThreshold5) {
+ if (b.level <= mWifiConfigStore.bandPreferencePenaltyThreshold5.get()) {
bRssiBoost = mWifiConfigStore.associatedHysteresisLow;
} else {
bRssiBoost = mWifiConfigStore.associatedHysteresisHigh;
}
}
if (currentBSSID != null && currentBSSID.equals(a.BSSID)) {
- if (a.level <= mWifiConfigStore.bandPreferencePenaltyThreshold5) {
+ if (a.level <= mWifiConfigStore.bandPreferencePenaltyThreshold5.get()) {
// Reduce the benefit of hysteresis if RSSI <= -75
aRssiBoost = mWifiConfigStore.associatedHysteresisLow;
} else {
@@ -1082,9 +1109,9 @@
aRssiBoost5 = rssiBoostFrom5GHzRssi(a.level + aRssiBoost, a.BSSID);
}
- if (VDBG) {
+ if (VDBG) {
String comp = " < ";
- if (b.level + bRssiBoost + bRssiBoost5 > a.level +aRssiBoost + aRssiBoost5) {
+ if (b.level + bRssiBoost + bRssiBoost5 > a.level + aRssiBoost + aRssiBoost5) {
comp = " > ";
}
logDbg("attemptRoam: "
@@ -1097,13 +1124,13 @@
// Compare the RSSIs after applying the hysteresis boost and the 5GHz
// boost if applicable
- if (b.level + bRssiBoost + bRssiBoost5 > a.level +aRssiBoost + aRssiBoost5) {
+ if (b.level + bRssiBoost + bRssiBoost5 > a.level + aRssiBoost + aRssiBoost5) {
// b is the better BSSID
a = b;
}
}
if (a != null) {
- if (VDBG) {
+ if (VDBG) {
StringBuilder sb = new StringBuilder();
sb.append("attemptRoam: " + current.configKey() +
" Found " + a.BSSID + " rssi=" + a.level + " freq=" + a.frequency);
@@ -1119,9 +1146,9 @@
/**
* getNetworkScore()
- *
+ * <p/>
* if scorer is present, get the network score of a WifiConfiguration
- *
+ * <p/>
* Note: this should be merge with setVisibility
*
* @param config
@@ -1136,7 +1163,8 @@
}
return WifiNetworkScoreCache.INVALID_NETWORK_SCORE;
}
- if (config.scanResultCache == null) {
+
+ if (mWifiConfigStore.getScanDetailCache(config) == null) {
if (VDBG) {
logDbg(" getConfigNetworkScore for " + config.configKey()
+ " -> no scan cache");
@@ -1150,8 +1178,9 @@
int startScore = -10000;
// Run thru all cached scan results
- for (ScanResult result : config.scanResultCache.values()) {
- if ((nowMs - result.seen) < age) {
+ for (ScanDetail sd : mWifiConfigStore.getScanDetailCache(config).values()) {
+ ScanResult result = sd.getScanResult();
+ if ((nowMs - sd.getSeen()) < age) {
int sc = mNetworkScoreCache.getNetworkScore(result, isActive);
if (sc > startScore) {
startScore = sc;
@@ -1204,8 +1233,9 @@
// Otherwise, we stop here if the currently selected network has a score. If it doesn't, we
// keep going - it could be that another BSSID is in range (has been seen recently) which
// has a score, even if the one we're immediately connected to doesn't.
- ScanResult currentScanResult = mWifiStateMachine.getCurrentScanResult();
- boolean currentNetworkHasScoreCurve = mNetworkScoreCache.hasScoreCurve(currentScanResult);
+ ScanResult currentScanResult = mWifiStateMachine.getCurrentScanResult();
+ boolean currentNetworkHasScoreCurve = currentScanResult != null
+ && mNetworkScoreCache.hasScoreCurve(currentScanResult);
if (ephemeralOutOfRangeTimeoutMs <= 0 || currentNetworkHasScoreCurve) {
if (DBG) {
if (currentNetworkHasScoreCurve) {
@@ -1218,14 +1248,16 @@
return currentNetworkHasScoreCurve;
}
- if (config.scanResultCache == null || config.scanResultCache.isEmpty()) {
+ if (mWifiConfigStore.getScanDetailCache(config) == null
+ || mWifiConfigStore.getScanDetailCache(config).isEmpty()) {
return false;
}
long currentTimeMs = System.currentTimeMillis();
- for (ScanResult result : config.scanResultCache.values()) {
- if (currentTimeMs > result.seen
- && currentTimeMs - result.seen < ephemeralOutOfRangeTimeoutMs
+ for (ScanDetail sd : mWifiConfigStore.getScanDetailCache(config).values()) {
+ ScanResult result = sd.getScanResult();
+ if (currentTimeMs > sd.getSeen()
+ && currentTimeMs - sd.getSeen() < ephemeralOutOfRangeTimeoutMs
&& mNetworkScoreCache.hasScoreCurve(result)) {
if (DBG) {
logDbg("Found scored BSSID, keeping network: " + result.BSSID);
@@ -1240,6 +1272,163 @@
return false;
}
+ // After WifiStateMachine ask the supplicant to associate or reconnect
+ // we might still obtain scan results from supplicant
+ // however the supplicant state in the mWifiInfo and supplicant state tracker
+ // are updated when we get the supplicant state change message which can be
+ // processed after the SCAN_RESULT message, so at this point the framework doesn't
+ // know that supplicant is ASSOCIATING.
+ // A good fix for this race condition would be for the WifiStateMachine to add
+ // a new transient state where it expects to get the supplicant message indicating
+ // that it started the association process and within which critical operations
+ // like autojoin should be deleted.
+
+ // This transient state would remove the need for the roam Wathchdog which
+ // basically does that.
+
+ // At the moment, we just query the supplicant state synchronously with the
+ // mWifiNative.status() command, which allow us to know that
+ // supplicant has started association process, even though we didnt yet get the
+ // SUPPLICANT_STATE_CHANGE message.
+
+ private static final List<String> ASSOC_STATES = Arrays.asList(
+ "ASSOCIATING",
+ "ASSOCIATED",
+ "FOUR_WAY_HANDSHAKE",
+ "GROUP_KEY_HANDSHAKE");
+
+ private int getNetID(String wpaStatus) {
+ if (VDBG) {
+ logDbg("attemptAutoJoin() status=" + wpaStatus);
+ }
+
+ try {
+ int id = WifiConfiguration.INVALID_NETWORK_ID;
+ String state = null;
+ BufferedReader br = new BufferedReader(new StringReader(wpaStatus));
+ String line;
+ while ((line = br.readLine()) != null) {
+ int split = line.indexOf('=');
+ if (split < 0) {
+ continue;
+ }
+
+ String name = line.substring(0, split);
+ if (name.equals("id")) {
+ try {
+ id = Integer.parseInt(line.substring(split + 1));
+ if (state != null) {
+ break;
+ }
+ } catch (NumberFormatException nfe) {
+ return WifiConfiguration.INVALID_NETWORK_ID;
+ }
+ } else if (name.equals("wpa_state")) {
+ state = line.substring(split + 1);
+ if (ASSOC_STATES.contains(state)) {
+ return WifiConfiguration.INVALID_NETWORK_ID;
+ } else if (id >= 0) {
+ break;
+ }
+ }
+ }
+ return id;
+ } catch (IOException ioe) {
+ return WifiConfiguration.INVALID_NETWORK_ID; // Won't happen
+ }
+ }
+
+ private boolean setCurrentConfigurationKey(WifiConfiguration currentConfig,
+ int supplicantNetId) {
+ if (currentConfig != null) {
+ if (supplicantNetId != currentConfig.networkId
+ // https://b.corp.google.com/issue?id=16484607
+ // mark this condition as an error only if the mismatched networkId are valid
+ && supplicantNetId != WifiConfiguration.INVALID_NETWORK_ID
+ && currentConfig.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
+ logDbg("attemptAutoJoin() ERROR wpa_supplicant out of sync nid="
+ + Integer.toString(supplicantNetId) + " WifiStateMachine="
+ + Integer.toString(currentConfig.networkId));
+ mWifiStateMachine.disconnectCommand();
+ return false;
+ } else if (currentConfig.ephemeral && (!mAllowUntrustedConnections ||
+ !haveRecentlySeenScoredBssid(currentConfig))) {
+ // The current connection is untrusted (the framework added it), but we're either
+ // no longer allowed to connect to such networks, the score has been nullified
+ // since we connected, or the scored BSSID has gone out of range.
+ // Drop the current connection and perform the rest of autojoin.
+ logDbg("attemptAutoJoin() disconnecting from unwanted ephemeral network");
+ mWifiStateMachine.disconnectCommand(Process.WIFI_UID,
+ mAllowUntrustedConnections ? 1 : 0);
+ return false;
+ } else {
+ mCurrentConfigurationKey = currentConfig.configKey();
+ return true;
+ }
+ } else {
+ // If not invalid, then maybe in the process of associating, skip this attempt
+ return supplicantNetId == WifiConfiguration.INVALID_NETWORK_ID;
+ }
+ }
+
+ private void updateBlackListStatus(WifiConfiguration config, long now) {
+ // Wait for 5 minutes before reenabling config that have known,
+ // repeated connection or DHCP failures
+ if (config.disableReason == WifiConfiguration.DISABLED_DHCP_FAILURE
+ || config.disableReason
+ == WifiConfiguration.DISABLED_ASSOCIATION_REJECT
+ || config.disableReason
+ == WifiConfiguration.DISABLED_AUTH_FAILURE) {
+ if (config.blackListTimestamp == 0
+ || (config.blackListTimestamp > now)) {
+ // Sanitize the timestamp
+ config.blackListTimestamp = now;
+ }
+ if ((now - config.blackListTimestamp) >
+ mWifiConfigStore.wifiConfigBlacklistMinTimeMilli) {
+ // Re-enable the WifiConfiguration
+ config.status = WifiConfiguration.Status.ENABLED;
+
+ // Reset the blacklist condition
+ config.numConnectionFailures = 0;
+ config.numIpConfigFailures = 0;
+ config.numAuthFailures = 0;
+ config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
+
+ config.dirty = true;
+ } else {
+ if (VDBG) {
+ long delay = mWifiConfigStore.wifiConfigBlacklistMinTimeMilli
+ - (now - config.blackListTimestamp);
+ logDbg("attemptautoJoin " + config.configKey()
+ + " dont unblacklist yet, waiting for "
+ + delay + " ms");
+ }
+ }
+ }
+ // Avoid networks disabled because of AUTH failure altogether
+ if (DBG) {
+ logDbg("attemptAutoJoin skip candidate due to auto join status "
+ + Integer.toString(config.autoJoinStatus) + " key "
+ + config.configKey(true)
+ + " reason " + config.disableReason);
+ }
+ }
+
+ boolean underSoftThreshold(WifiConfiguration config) {
+ return config.visibility.rssi24 < mWifiConfigStore.thresholdUnblacklistThreshold24Soft.get()
+ && config.visibility.rssi5 < mWifiConfigStore.thresholdUnblacklistThreshold5Soft.get();
+ }
+
+ boolean underHardThreshold(WifiConfiguration config) {
+ return config.visibility.rssi24 < mWifiConfigStore.thresholdUnblacklistThreshold24Hard.get()
+ && config.visibility.rssi5 < mWifiConfigStore.thresholdUnblacklistThreshold5Hard.get();
+ }
+
+ boolean underThreshold(WifiConfiguration config, int rssi24, int rssi5) {
+ return config.visibility.rssi24 < rssi24 && config.visibility.rssi5 < rssi5;
+ }
+
/**
* attemptAutoJoin() function implements the core of the a network switching algorithm
* Return false if no acceptable networks were found.
@@ -1249,79 +1438,32 @@
didOverride = false;
didBailDueToWeakRssi = false;
int networkSwitchType = AUTO_JOIN_IDLE;
+ int age = mScanResultAutoJoinAge;
long now = System.currentTimeMillis();
String lastSelectedConfiguration = mWifiConfigStore.getLastSelectedConfiguration();
-
+ if (lastSelectedConfiguration != null) {
+ age = 14000;
+ }
// Reset the currentConfiguration Key, and set it only if WifiStateMachine and
// supplicant agree
mCurrentConfigurationKey = null;
WifiConfiguration currentConfiguration = mWifiStateMachine.getCurrentWifiConfiguration();
WifiConfiguration candidate = null;
-
// Obtain the subset of recently seen networks
List<WifiConfiguration> list =
- mWifiConfigStore.getRecentConfiguredNetworks(mScanResultAutoJoinAge, false);
+ mWifiConfigStore.getRecentConfiguredNetworks(age, false);
if (list == null) {
- if (VDBG) logDbg("attemptAutoJoin nothing known=" +
- mWifiConfigStore.getconfiguredNetworkSize());
+ if (VDBG) logDbg("attemptAutoJoin nothing known=" +
+ mWifiConfigStore.getConfiguredNetworksSize());
return false;
}
// Find the currently connected network: ask the supplicant directly
- String val = mWifiNative.status(true);
- String status[] = val.split("\\r?\\n");
- if (VDBG) {
- logDbg("attemptAutoJoin() status=" + val + " split="
- + Integer.toString(status.length));
- }
+ int supplicantNetId = getNetID(mWifiNative.status(true));
- int supplicantNetId = -1;
- for (String key : status) {
- if (key.regionMatches(0, "id=", 0, 3)) {
- int idx = 3;
- supplicantNetId = 0;
- while (idx < key.length()) {
- char c = key.charAt(idx);
-
- if ((c >= 0x30) && (c <= 0x39)) {
- supplicantNetId *= 10;
- supplicantNetId += c - 0x30;
- idx++;
- } else {
- break;
- }
- }
- } else if (key.contains("wpa_state=ASSOCIATING")
- || key.contains("wpa_state=ASSOCIATED")
- || key.contains("wpa_state=FOUR_WAY_HANDSHAKE")
- || key.contains("wpa_state=GROUP_KEY_HANDSHAKE")) {
- if (DBG) {
- logDbg("attemptAutoJoin: bail out due to sup state " + key);
- }
- // After WifiStateMachine ask the supplicant to associate or reconnect
- // we might still obtain scan results from supplicant
- // however the supplicant state in the mWifiInfo and supplicant state tracker
- // are updated when we get the supplicant state change message which can be
- // processed after the SCAN_RESULT message, so at this point the framework doesn't
- // know that supplicant is ASSOCIATING.
- // A good fix for this race condition would be for the WifiStateMachine to add
- // a new transient state where it expects to get the supplicant message indicating
- // that it started the association process and within which critical operations
- // like autojoin should be deleted.
-
- // This transient state would remove the need for the roam Wathchdog which
- // basically does that.
-
- // At the moment, we just query the supplicant state synchronously with the
- // mWifiNative.status() command, which allow us to know that
- // supplicant has started association process, even though we didnt yet get the
- // SUPPLICANT_STATE_CHANGE message.
- return false;
- }
- }
if (DBG) {
String conf = "";
String last = "";
@@ -1336,35 +1478,8 @@
+ " ---> suppNetId=" + Integer.toString(supplicantNetId));
}
- if (currentConfiguration != null) {
- if (supplicantNetId != currentConfiguration.networkId
- // https://b.corp.google.com/issue?id=16484607
- // mark this condition as an error only if the mismatched networkId are valid
- && supplicantNetId != WifiConfiguration.INVALID_NETWORK_ID
- && currentConfiguration.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
- logDbg("attemptAutoJoin() ERROR wpa_supplicant out of sync nid="
- + Integer.toString(supplicantNetId) + " WifiStateMachine="
- + Integer.toString(currentConfiguration.networkId));
- mWifiStateMachine.disconnectCommand();
- return false;
- } else if (currentConfiguration.ephemeral && (!mAllowUntrustedConnections ||
- !haveRecentlySeenScoredBssid(currentConfiguration))) {
- // The current connection is untrusted (the framework added it), but we're either
- // no longer allowed to connect to such networks, the score has been nullified
- // since we connected, or the scored BSSID has gone out of range.
- // Drop the current connection and perform the rest of autojoin.
- logDbg("attemptAutoJoin() disconnecting from unwanted ephemeral network");
- mWifiStateMachine.disconnectCommand(Process.WIFI_UID,
- mAllowUntrustedConnections ? 1 : 0);
- return false;
- } else {
- mCurrentConfigurationKey = currentConfiguration.configKey();
- }
- } else {
- if (supplicantNetId != WifiConfiguration.INVALID_NETWORK_ID) {
- // Maybe in the process of associating, skip this attempt
- return false;
- }
+ if (!setCurrentConfigurationKey(currentConfiguration, supplicantNetId)) {
+ return false;
}
int currentNetId = -1;
@@ -1384,48 +1499,17 @@
continue;
}
- if (config.autoJoinStatus >=
- WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
- // Wait for 5 minutes before reenabling config that have known,
- // repeated connection or DHCP failures
- if (config.disableReason == WifiConfiguration.DISABLED_DHCP_FAILURE
- || config.disableReason
- == WifiConfiguration.DISABLED_ASSOCIATION_REJECT
- || config.disableReason
- == WifiConfiguration.DISABLED_AUTH_FAILURE) {
- if (config.blackListTimestamp == 0
- || (config.blackListTimestamp > now)) {
- // Sanitize the timestamp
- config.blackListTimestamp = now;
- }
- if ((now - config.blackListTimestamp) >
- mWifiConfigStore.wifiConfigBlacklistMinTimeMilli) {
- // Re-enable the WifiConfiguration
- config.status = WifiConfiguration.Status.ENABLED;
+ if (config.autoJoinStatus >= WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
+ updateBlackListStatus(config, now);
+ continue;
+ }
- // Reset the blacklist condition
- config.numConnectionFailures = 0;
- config.numIpConfigFailures = 0;
- config.numAuthFailures = 0;
- config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
-
- config.dirty = true;
- } else {
- if (VDBG) {
- long delay = mWifiConfigStore.wifiConfigBlacklistMinTimeMilli
- - (now - config.blackListTimestamp);
- logDbg("attemptautoJoin " + config.configKey()
- + " dont unblacklist yet, waiting for "
- + delay + " ms");
- }
- }
- }
- // Avoid networks disabled because of AUTH failure altogether
+ if (config.userApproved == WifiConfiguration.USER_PENDING ||
+ config.userApproved == WifiConfiguration.USER_BANNED) {
if (DBG) {
- logDbg("attemptAutoJoin skip candidate due to auto join status "
- + Integer.toString(config.autoJoinStatus) + " key "
- + config.configKey(true)
- + " reason " + config.disableReason);
+ logDbg("attemptAutoJoin skip candidate due to user approval status "
+ + WifiConfiguration.userApprovedAsString(config.userApproved) + " key "
+ + config.configKey(true));
}
continue;
}
@@ -1450,42 +1534,29 @@
}
}
+ if (config.visibility == null) {
+ continue;
+ }
+
// Try to unblacklist based on good visibility
- if (config.visibility.rssi5 < mWifiConfigStore.thresholdUnblacklistThreshold5Soft
- && config.visibility.rssi24
- < mWifiConfigStore.thresholdUnblacklistThreshold24Soft) {
+ if (underSoftThreshold(config)) {
if (DBG) {
- logDbg("attemptAutoJoin do not unblacklist due to low visibility "
- + config.autoJoinStatus
- + " key " + config.configKey(true)
- + " rssi=(" + config.visibility.rssi24
- + "," + config.visibility.rssi5
- + ") num=(" + config.visibility.num24
- + "," + config.visibility.num5 + ")");
+ logDbg("attemptAutoJoin do not unblacklist due to low visibility " +
+ config.configKey() + " status=" + config.autoJoinStatus);
}
- } else if (config.visibility.rssi5 < mWifiConfigStore.thresholdUnblacklistThreshold5Hard
- && config.visibility.rssi24
- < mWifiConfigStore.thresholdUnblacklistThreshold24Hard) {
+ } else if (underHardThreshold(config)) {
// If the network is simply temporary disabled, don't allow reconnect until
// RSSI becomes good enough
config.setAutoJoinStatus(config.autoJoinStatus - 1);
if (DBG) {
- logDbg("attemptAutoJoin good candidate seen, bumped soft -> status="
- + config.autoJoinStatus
- + " " + config.configKey(true) + " rssi=("
- + config.visibility.rssi24 + "," + config.visibility.rssi5
- + ") num=(" + config.visibility.num24
- + "," + config.visibility.num5 + ")");
+ logDbg("attemptAutoJoin good candidate seen, bumped soft -> status=" +
+ config.configKey() + " status=" + config.autoJoinStatus);
}
} else {
config.setAutoJoinStatus(config.autoJoinStatus - 3);
if (DBG) {
- logDbg("attemptAutoJoin good candidate seen, bumped hard -> status="
- + config.autoJoinStatus
- + " " + config.configKey(true) + " rssi=("
- + config.visibility.rssi24 + "," + config.visibility.rssi5
- + ") num=(" + config.visibility.num24
- + "," + config.visibility.num5 + ")");
+ logDbg("attemptAutoJoin good candidate seen, bumped hard -> status=" +
+ config.configKey() + " status=" + config.autoJoinStatus);
}
}
@@ -1493,12 +1564,8 @@
WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED) {
// Network is blacklisted, skip
if (DBG) {
- logDbg("attemptAutoJoin skip blacklisted -> status="
- + config.autoJoinStatus
- + " " + config.configKey(true) + " rssi=("
- + config.visibility.rssi24 + "," + config.visibility.rssi5
- + ") num=(" + config.visibility.num24
- + "," + config.visibility.num5 + ")");
+ logDbg("attemptAutoJoin skip blacklisted -> status=" +
+ config.configKey() + " status=" + config.autoJoinStatus);
}
continue;
}
@@ -1517,10 +1584,6 @@
isLastSelected = true;
}
- if (config.visibility == null) {
- continue;
- }
-
if (config.lastRoamingFailure != 0
&& currentConfiguration != null
&& (lastSelectedConfiguration == null
@@ -1544,17 +1607,12 @@
}
int boost = config.autoJoinUseAggressiveJoinAttemptThreshold + weakRssiBailCount;
- if ((config.visibility.rssi5 + boost)
- < mWifiConfigStore.thresholdInitialAutoJoinAttemptMin5RSSI
- && (config.visibility.rssi24 + boost)
- < mWifiConfigStore.thresholdInitialAutoJoinAttemptMin24RSSI) {
+ if (underThreshold(config,
+ mWifiConfigStore.thresholdInitialAutoJoinAttemptMin24RSSI.get() - boost,
+ mWifiConfigStore.thresholdInitialAutoJoinAttemptMin5RSSI.get() - boost)) {
+
if (DBG) {
- logDbg("attemptAutoJoin skip due to low visibility -> status="
- + config.autoJoinStatus
- + " key " + config.configKey(true) + " rssi="
- + config.visibility.rssi24 + ", " + config.visibility.rssi5
- + " num=" + config.visibility.num24
- + ", " + config.visibility.num5);
+ logDbg("attemptAutoJoin skip due to low visibility " + config.configKey());
}
// Don't try to autojoin a network that is too far but
@@ -1572,6 +1630,7 @@
}
}
}
+ // NOTE: If this condition is updated, update NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN.
if (config.numNoInternetAccessReports > 0
&& !isLastSelected
&& !config.validatedInternetAccess) {
@@ -1601,11 +1660,15 @@
if (candidate == null) {
candidate = config;
} else {
- if (VDBG) {
+ if (VDBG) {
logDbg("attemptAutoJoin will compare candidate " + candidate.configKey()
+ " with " + config.configKey());
}
+
int order = compareWifiConfigurations(candidate, config);
+ if (VDBG) {
+ logDbg("attemptAutoJoin compareWifiConfigurations returned " + order);
+ }
// The lastSelectedConfiguration is the configuration the user has manually selected
// thru WifiPicker, or that a 3rd party app asked us to connect to via the
@@ -1619,7 +1682,7 @@
// above RSSI/scorer based selection of linked configuration (+/- 50)
// by reducing order by -100
order = order - 100;
- if (VDBG) {
+ if (VDBG) {
logDbg(" ...and prefers -100 " + candidate.configKey()
+ " over " + config.configKey()
+ " because it is the last selected -> "
@@ -1632,7 +1695,7 @@
// above RSSI/scorer based selection of linked configuration (+/- 50)
// by increasing order by +100
order = order + 100;
- if (VDBG) {
+ if (VDBG) {
logDbg(" ...and prefers +100 " + config.configKey()
+ " over " + candidate.configKey()
+ " because it is the last selected -> "
@@ -1660,10 +1723,11 @@
long nowMs = System.currentTimeMillis();
int currentScore = -10000;
// The untrusted network with highest score
- ScanResult untrustedCandidate = null;
+ ScanDetail untrustedCandidate = null;
// Look for untrusted scored network only if the current candidate is bad
if (isBadCandidate(rssi24, rssi5)) {
- for (ScanResult result : scanResultCache.values()) {
+ for (ScanDetail scanDetail : scanResultCache.values()) {
+ ScanResult result = scanDetail.getScanResult();
// We look only at untrusted networks with a valid SSID
// A trusted result would have been looked at thru it's Wificonfiguration
if (TextUtils.isEmpty(result.SSID) || !result.untrusted ||
@@ -1686,13 +1750,13 @@
&& score > currentScore) {
// Highest score: Select this candidate
currentScore = score;
- untrustedCandidate = result;
+ untrustedCandidate = scanDetail;
if (VDBG) {
logDbg("AutoJoinController: found untrusted candidate "
+ result.SSID
- + " RSSI=" + result.level
- + " freq=" + result.frequency
- + " score=" + score);
+ + " RSSI=" + result.level
+ + " freq=" + result.frequency
+ + " score=" + score);
}
}
}
@@ -1705,6 +1769,7 @@
mWifiConfigStore.wifiConfigurationFromScanResult(untrustedCandidate);
candidate.allowedKeyManagement.set(KeyMgmt.NONE);
candidate.ephemeral = true;
+ candidate.dirty = true;
}
}
@@ -1716,7 +1781,7 @@
&& currentConfiguration == null
&& didBailDueToWeakRssi
&& (mWifiConfigStore.lastUnwantedNetworkDisconnectTimestamp == 0
- || lastUnwanted > (1000 * 60 * 60 * 24 * 7))
+ || lastUnwanted > (1000 * 60 * 60 * 24 * 7))
) {
// We are bailing out of autojoin although we are seeing a weak configuration, and
// - we didn't find another valid candidate
@@ -1751,7 +1816,7 @@
+ candidate.configKey()
+ current
+ " linked=" + (currentConfiguration != null
- && currentConfiguration.isLinked(candidate))
+ && currentConfiguration.isLinked(candidate))
+ " : delta="
+ Integer.toString(networkDelta) + " "
+ doSwitch);
@@ -1762,7 +1827,7 @@
* if user is currently streaming voice traffic,
* then we should not be allowed to switch regardless of the delta
*/
- if (mWifiStateMachine.shouldSwitchNetwork(networkDelta)) {
+ if (mWifiStateMachine.shouldSwitchNetwork(networkDelta)) { // !!! JNo: Here!
if (mStaStaSupported) {
logDbg("mStaStaSupported --> error do nothing now ");
} else {
@@ -1818,39 +1883,16 @@
}
}
mWifiStateMachine.sendMessage(WifiStateMachine.CMD_AUTO_CONNECT,
- candidate.networkId, networkSwitchType, candidate);
+ candidate.networkId, networkSwitchType, candidate);
found = true;
}
}
- if (networkSwitchType == AUTO_JOIN_IDLE) {
+ if (networkSwitchType == AUTO_JOIN_IDLE && !mWifiConfigStore.enableHalBasedPno.get()) {
String currentBSSID = mWifiStateMachine.getCurrentBSSID();
// Attempt same WifiConfiguration roaming
ScanResult roamCandidate =
attemptRoam(null, currentConfiguration, mScanResultAutoJoinAge, currentBSSID);
- /**
- * TODO: (post L initial release)
- * consider handling linked configurations roaming (i.e. extended Roaming)
- * thru the attemptRoam function which makes use of the RSSI roaming threshold.
- * At the moment, extended roaming is only handled thru the attemptAutoJoin()
- * function which compare configurations.
- *
- * The advantage of making use of attemptRoam function is that this function
- * will looks at all the BSSID of each configurations, instead of only looking
- * at WifiConfiguration.visibility which keeps trackonly of the RSSI/band of the
- * two highest BSSIDs.
- */
- // Attempt linked WifiConfiguration roaming
- /* if (currentConfiguration != null
- && currentConfiguration.linkedConfigurations != null) {
- for (String key : currentConfiguration.linkedConfigurations.keySet()) {
- WifiConfiguration link = mWifiConfigStore.getWifiConfiguration(key);
- if (link != null) {
- roamCandidate = attemptRoam(roamCandidate, link, mScanResultAutoJoinAge,
- currentBSSID);
- }
- }
- }*/
if (roamCandidate != null && currentBSSID != null
&& currentBSSID.equals(roamCandidate.BSSID)) {
roamCandidate = null;
@@ -1867,12 +1909,87 @@
mWifiConnectionStatistics.numAutoRoamAttempt++;
mWifiStateMachine.sendMessage(WifiStateMachine.CMD_AUTO_ROAM,
- currentConfiguration.networkId, 1, roamCandidate);
+ currentConfiguration.networkId, 1, roamCandidate);
found = true;
}
}
if (VDBG) logDbg("Done attemptAutoJoin status=" + Integer.toString(networkSwitchType));
return found;
}
+
+ private void logDenial(String reason, WifiConfiguration config) {
+ if (!DBG) {
+ return;
+ }
+ logDbg(reason + config.toString());
+ }
+
+ WifiConfiguration getWifiConfiguration(WifiNative.WifiPnoNetwork network) {
+ if (network.configKey != null) {
+ return mWifiConfigStore.getWifiConfiguration(network.configKey);
+ }
+ return null;
+ }
+
+ ArrayList<WifiNative.WifiPnoNetwork> getPnoList(WifiConfiguration current) {
+ int size = -1;
+ ArrayList<WifiNative.WifiPnoNetwork> list = new ArrayList<WifiNative.WifiPnoNetwork>();
+
+ if (mWifiConfigStore.mCachedPnoList != null) {
+ size = mWifiConfigStore.mCachedPnoList.size();
+ }
+
+ if (DBG) {
+ String s = "";
+ if (current != null) {
+ s = " for: " + current.configKey();
+ }
+ Log.e(TAG, " get Pno List total size:" + size + s);
+ }
+ if (current != null) {
+ String configKey = current.configKey();
+ /**
+ * If we are currently associated to a WifiConfiguration then include
+ * only those networks that have a higher priority
+ */
+ for (WifiNative.WifiPnoNetwork network : mWifiConfigStore.mCachedPnoList) {
+ WifiConfiguration config = getWifiConfiguration(network);
+ if (config == null) {
+ continue;
+ }
+ if (config.autoJoinStatus
+ >= WifiConfiguration.AUTO_JOIN_DISABLED_NO_CREDENTIALS) {
+ continue;
+ }
+
+ if (!configKey.equals(network.configKey)) {
+ int choice = getConnectChoice(config, current, true);
+ if (choice > 0) {
+ // config is of higher priority
+ if (DBG) {
+ Log.e(TAG, " Pno List adding:" + network.configKey
+ + " choice " + choice);
+ }
+ list.add(network);
+ network.rssi_threshold = mWifiConfigStore.thresholdGoodRssi24.get();
+ }
+ }
+ }
+ } else {
+ for (WifiNative.WifiPnoNetwork network : mWifiConfigStore.mCachedPnoList) {
+ WifiConfiguration config = getWifiConfiguration(network);
+ if (config == null) {
+ continue;
+ }
+ if (config.autoJoinStatus
+ >= WifiConfiguration.AUTO_JOIN_DISABLED_NO_CREDENTIALS) {
+ continue;
+ }
+ list.add(network);
+ network.rssi_threshold = mWifiConfigStore.thresholdGoodRssi24.get();
+ }
+ }
+ return list;
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiConfigStore.java
index 94752d3..dd47b08 100644
--- a/service/java/com/android/server/wifi/WifiConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiConfigStore.java
@@ -16,32 +16,34 @@
package com.android.server.wifi;
+import android.app.AppGlobals;
+import android.app.admin.DeviceAdminInfo;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.net.IpConfiguration;
import android.net.IpConfiguration.IpAssignment;
import android.net.IpConfiguration.ProxySettings;
-import android.net.LinkAddress;
import android.net.NetworkInfo.DetailedState;
import android.net.ProxyInfo;
-import android.net.RouteInfo;
import android.net.StaticIpConfiguration;
+import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiConfiguration.Status;
-import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
-
import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiSsid;
import android.net.wifi.WpsInfo;
import android.net.wifi.WpsResult;
-import android.net.wifi.ScanResult;
-import android.net.wifi.WifiInfo;
-
import android.os.Environment;
import android.os.FileObserver;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
@@ -53,12 +55,24 @@
import android.util.Log;
import android.util.SparseArray;
+import com.android.server.LocalServices;
+import com.android.internal.R;
import com.android.server.net.DelayedDiskWrite;
import com.android.server.net.IpConfigStore;
-import com.android.internal.R;
+import com.android.server.wifi.anqp.ANQPElement;
+import com.android.server.wifi.anqp.Constants;
+import com.android.server.wifi.hotspot2.ANQPData;
+import com.android.server.wifi.hotspot2.AnqpCache;
+import com.android.server.wifi.hotspot2.NetworkDetail;
+import com.android.server.wifi.hotspot2.PasspointMatch;
+import com.android.server.wifi.hotspot2.SupplicantBridge;
+import com.android.server.wifi.hotspot2.Utils;
+import com.android.server.wifi.hotspot2.omadm.MOManager;
+import com.android.server.wifi.hotspot2.pps.Credential;
+import com.android.server.wifi.hotspot2.pps.HomeSP;
-import java.io.BufferedReader;
import java.io.BufferedInputStream;
+import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
@@ -69,19 +83,31 @@
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
-import java.math.BigInteger;
-import java.net.InetAddress;
-import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
-import java.text.SimpleDateFormat;
-import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import java.util.*;
-import java.util.zip.Checksum;
import java.util.zip.CRC32;
+import java.util.zip.Checksum;
+
+import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
+
/**
* This class provides the API to manage configured
@@ -131,16 +157,17 @@
public class WifiConfigStore extends IpConfigStore {
private Context mContext;
- private static final String TAG = "WifiConfigStore";
+ public static final String TAG = "WifiConfigStore";
private static final boolean DBG = true;
private static boolean VDBG = false;
private static boolean VVDBG = false;
private static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf";
+ private static final String SUPPLICANT_CONFIG_FILE_BACKUP = SUPPLICANT_CONFIG_FILE + ".tmp";
+ private static final String PPS_FILE = "/data/misc/wifi/PerProviderSubscription.conf";
/* configured networks with network id as the key */
- private HashMap<Integer, WifiConfiguration> mConfiguredNetworks =
- new HashMap<Integer, WifiConfiguration>();
+ private final ConfigurationMap mConfiguredNetworks = new ConfigurationMap();
/* A network id is a unique identifier for a network configured in the
* supplicant. Network ids are generated when the supplicant reads
@@ -149,8 +176,9 @@
* that is generated from SSID and security type of the network. A mapping
* from the generated unique id to network id of the network is needed to
* map supplicant config to IP configuration. */
- private HashMap<Integer, Integer> mNetworkIds =
- new HashMap<Integer, Integer>();
+
+ /* Stores a map of NetworkId to ScanCache */
+ private HashMap<Integer, ScanDetailCache> mScanDetailCaches;
/**
* Framework keeps a list of (the CRC32 hashes of) all SSIDs that where deleted by user,
@@ -180,176 +208,159 @@
"/misc/wifi/autojoinconfig.txt";
/* Network History Keys */
- private static final String SSID_KEY = "SSID: ";
- private static final String CONFIG_KEY = "CONFIG: ";
- private static final String CHOICE_KEY = "CHOICE: ";
- private static final String LINK_KEY = "LINK: ";
- private static final String BSSID_KEY = "BSSID: ";
- private static final String BSSID_KEY_END = "/BSSID: ";
- private static final String RSSI_KEY = "RSSI: ";
- private static final String FREQ_KEY = "FREQ: ";
- private static final String DATE_KEY = "DATE: ";
- private static final String MILLI_KEY = "MILLI: ";
- private static final String BLACKLIST_MILLI_KEY = "BLACKLIST_MILLI: ";
- private static final String NETWORK_ID_KEY = "ID: ";
- private static final String PRIORITY_KEY = "PRIORITY: ";
- private static final String DEFAULT_GW_KEY = "DEFAULT_GW: ";
- private static final String AUTH_KEY = "AUTH: ";
- private static final String SEPARATOR_KEY = "\n";
- private static final String STATUS_KEY = "AUTO_JOIN_STATUS: ";
- private static final String BSSID_STATUS_KEY = "BSSID_STATUS: ";
- private static final String SELF_ADDED_KEY = "SELF_ADDED: ";
- private static final String FAILURE_KEY = "FAILURE: ";
- private static final String DID_SELF_ADD_KEY = "DID_SELF_ADD: ";
- private static final String PEER_CONFIGURATION_KEY = "PEER_CONFIGURATION: ";
- private static final String CREATOR_UID_KEY = "CREATOR_UID_KEY: ";
- private static final String CONNECT_UID_KEY = "CONNECT_UID_KEY: ";
- private static final String UPDATE_UID_KEY = "UPDATE_UID: ";
- private static final String SUPPLICANT_STATUS_KEY = "SUP_STATUS: ";
- private static final String SUPPLICANT_DISABLE_REASON_KEY = "SUP_DIS_REASON: ";
- private static final String FQDN_KEY = "FQDN: ";
- private static final String NUM_CONNECTION_FAILURES_KEY = "CONNECT_FAILURES: ";
- private static final String NUM_IP_CONFIG_FAILURES_KEY = "IP_CONFIG_FAILURES: ";
- private static final String NUM_AUTH_FAILURES_KEY = "AUTH_FAILURES: ";
- private static final String SCORER_OVERRIDE_KEY = "SCORER_OVERRIDE: ";
- private static final String SCORER_OVERRIDE_AND_SWITCH_KEY = "SCORER_OVERRIDE_AND_SWITCH: ";
- private static final String VALIDATED_INTERNET_ACCESS_KEY = "VALIDATED_INTERNET_ACCESS: ";
- private static final String NO_INTERNET_ACCESS_REPORTS_KEY = "NO_INTERNET_ACCESS_REPORTS : ";
- private static final String EPHEMERAL_KEY = "EPHEMERAL: ";
- private static final String NUM_ASSOCIATION_KEY = "NUM_ASSOCIATION: ";
- private static final String DELETED_CRC32_KEY = "DELETED_CRC32: ";
- private static final String DELETED_EPHEMERAL_KEY = "DELETED_EPHEMERAL: ";
+ private static final String SSID_KEY = "SSID";
+ private static final String CONFIG_KEY = "CONFIG";
+ private static final String CHOICE_KEY = "CHOICE";
+ private static final String LINK_KEY = "LINK";
+ private static final String BSSID_KEY = "BSSID";
+ private static final String BSSID_KEY_END = "/BSSID";
+ private static final String RSSI_KEY = "RSSI";
+ private static final String FREQ_KEY = "FREQ";
+ private static final String DATE_KEY = "DATE";
+ private static final String MILLI_KEY = "MILLI";
+ private static final String BLACKLIST_MILLI_KEY = "BLACKLIST_MILLI";
+ private static final String NETWORK_ID_KEY = "ID";
+ private static final String PRIORITY_KEY = "PRIORITY";
+ private static final String DEFAULT_GW_KEY = "DEFAULT_GW";
+ private static final String AUTH_KEY = "AUTH";
+ private static final String STATUS_KEY = "AUTO_JOIN_STATUS";
+ private static final String BSSID_STATUS_KEY = "BSSID_STATUS";
+ private static final String SELF_ADDED_KEY = "SELF_ADDED";
+ private static final String FAILURE_KEY = "FAILURE";
+ private static final String DID_SELF_ADD_KEY = "DID_SELF_ADD";
+ private static final String PEER_CONFIGURATION_KEY = "PEER_CONFIGURATION";
+ private static final String CREATOR_UID_KEY = "CREATOR_UID_KEY";
+ private static final String CONNECT_UID_KEY = "CONNECT_UID_KEY";
+ private static final String UPDATE_UID_KEY = "UPDATE_UID";
+ private static final String SUPPLICANT_STATUS_KEY = "SUP_STATUS";
+ private static final String SUPPLICANT_DISABLE_REASON_KEY = "SUP_DIS_REASON";
+ private static final String FQDN_KEY = "FQDN";
+ private static final String NUM_CONNECTION_FAILURES_KEY = "CONNECT_FAILURES";
+ private static final String NUM_IP_CONFIG_FAILURES_KEY = "IP_CONFIG_FAILURES";
+ private static final String NUM_AUTH_FAILURES_KEY = "AUTH_FAILURES";
+ private static final String SCORER_OVERRIDE_KEY = "SCORER_OVERRIDE";
+ private static final String SCORER_OVERRIDE_AND_SWITCH_KEY = "SCORER_OVERRIDE_AND_SWITCH";
+ private static final String VALIDATED_INTERNET_ACCESS_KEY = "VALIDATED_INTERNET_ACCESS";
+ private static final String NO_INTERNET_ACCESS_REPORTS_KEY = "NO_INTERNET_ACCESS_REPORTS";
+ private static final String EPHEMERAL_KEY = "EPHEMERAL";
+ private static final String NUM_ASSOCIATION_KEY = "NUM_ASSOCIATION";
+ private static final String DELETED_CRC32_KEY = "DELETED_CRC32";
+ private static final String DELETED_EPHEMERAL_KEY = "DELETED_EPHEMERAL";
+ private static final String JOIN_ATTEMPT_BOOST_KEY = "JOIN_ATTEMPT_BOOST";
+ private static final String CREATOR_NAME_KEY = "CREATOR_NAME";
+ private static final String UPDATE_NAME_KEY = "UPDATE_NAME";
+ private static final String USER_APPROVED_KEY = "USER_APPROVED";
+ private static final String CREATION_TIME_KEY = "CREATION_TIME";
+ private static final String UPDATE_TIME_KEY = "UPDATE_TIME";
- private static final String JOIN_ATTEMPT_BOOST_KEY = "JOIN_ATTEMPT_BOOST: ";
+ private static final String SEPARATOR = ": ";
+ private static final String NL = "\n";
+
private static final String THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY
- = "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G: ";
+ = "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G";
private static final String THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY
- = "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G: ";
+ = "THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G";
private static final String THRESHOLD_UNBLACKLIST_HARD_5G_KEY
- = "THRESHOLD_UNBLACKLIST_HARD_5G: ";
+ = "THRESHOLD_UNBLACKLIST_HARD_5G";
private static final String THRESHOLD_UNBLACKLIST_SOFT_5G_KEY
- = "THRESHOLD_UNBLACKLIST_SOFT_5G: ";
+ = "THRESHOLD_UNBLACKLIST_SOFT_5G";
private static final String THRESHOLD_UNBLACKLIST_HARD_24G_KEY
- = "THRESHOLD_UNBLACKLIST_HARD_24G: ";
+ = "THRESHOLD_UNBLACKLIST_HARD_24G";
private static final String THRESHOLD_UNBLACKLIST_SOFT_24G_KEY
- = "THRESHOLD_UNBLACKLIST_SOFT_24G: ";
+ = "THRESHOLD_UNBLACKLIST_SOFT_24G";
private static final String THRESHOLD_GOOD_RSSI_5_KEY
- = "THRESHOLD_GOOD_RSSI_5: ";
+ = "THRESHOLD_GOOD_RSSI_5";
private static final String THRESHOLD_LOW_RSSI_5_KEY
- = "THRESHOLD_LOW_RSSI_5: ";
+ = "THRESHOLD_LOW_RSSI_5";
private static final String THRESHOLD_BAD_RSSI_5_KEY
- = "THRESHOLD_BAD_RSSI_5: ";
+ = "THRESHOLD_BAD_RSSI_5";
private static final String THRESHOLD_GOOD_RSSI_24_KEY
- = "THRESHOLD_GOOD_RSSI_24: ";
+ = "THRESHOLD_GOOD_RSSI_24";
private static final String THRESHOLD_LOW_RSSI_24_KEY
- = "THRESHOLD_LOW_RSSI_24: ";
+ = "THRESHOLD_LOW_RSSI_24";
private static final String THRESHOLD_BAD_RSSI_24_KEY
- = "THRESHOLD_BAD_RSSI_24: ";
+ = "THRESHOLD_BAD_RSSI_24";
private static final String THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY
- = "THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING: ";
+ = "THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING";
private static final String THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY
- = "THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING: ";
+ = "THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING";
private static final String THRESHOLD_MAX_TX_PACKETS_FOR_FULL_SCANS_KEY
- = "THRESHOLD_MAX_TX_PACKETS_FOR_FULL_SCANS: ";
+ = "THRESHOLD_MAX_TX_PACKETS_FOR_FULL_SCANS";
private static final String THRESHOLD_MAX_RX_PACKETS_FOR_FULL_SCANS_KEY
- = "THRESHOLD_MAX_RX_PACKETS_FOR_FULL_SCANS: ";
+ = "THRESHOLD_MAX_RX_PACKETS_FOR_FULL_SCANS";
private static final String THRESHOLD_MAX_TX_PACKETS_FOR_PARTIAL_SCANS_KEY
- = "THRESHOLD_MAX_TX_PACKETS_FOR_PARTIAL_SCANS: ";
+ = "THRESHOLD_MAX_TX_PACKETS_FOR_PARTIAL_SCANS";
private static final String THRESHOLD_MAX_RX_PACKETS_FOR_PARTIAL_SCANS_KEY
- = "THRESHOLD_MAX_RX_PACKETS_FOR_PARTIAL_SCANS: ";
+ = "THRESHOLD_MAX_RX_PACKETS_FOR_PARTIAL_SCANS";
private static final String MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY
- = "MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCANS: ";
+ = "MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCANS";
private static final String MAX_NUM_PASSIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY
- = "MAX_NUM_PASSIVE_CHANNELS_FOR_PARTIAL_SCANS: ";
+ = "MAX_NUM_PASSIVE_CHANNELS_FOR_PARTIAL_SCANS";
private static final String A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW_KEY =
- "A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW: ";
+ "A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW";
private static final String A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY =
- "A_BAND_PREFERENCE_RSSI_THRESHOLD: ";
+ "A_BAND_PREFERENCE_RSSI_THRESHOLD";
private static final String G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY =
- "G_BAND_PREFERENCE_RSSI_THRESHOLD: ";
+ "G_BAND_PREFERENCE_RSSI_THRESHOLD";
private static final String ENABLE_AUTOJOIN_WHILE_ASSOCIATED_KEY
= "ENABLE_AUTOJOIN_WHILE_ASSOCIATED: ";
private static final String ASSOCIATED_PARTIAL_SCAN_PERIOD_KEY
- = "ASSOCIATED_PARTIAL_SCAN_PERIOD: ";
+ = "ASSOCIATED_PARTIAL_SCAN_PERIOD";
private static final String ASSOCIATED_FULL_SCAN_BACKOFF_KEY
- = "ASSOCIATED_FULL_SCAN_BACKOFF_PERIOD: ";
+ = "ASSOCIATED_FULL_SCAN_BACKOFF_PERIOD";
private static final String ALWAYS_ENABLE_SCAN_WHILE_ASSOCIATED_KEY
- = "ALWAYS_ENABLE_SCAN_WHILE_ASSOCIATED: ";
+ = "ALWAYS_ENABLE_SCAN_WHILE_ASSOCIATED";
private static final String ONLY_LINK_SAME_CREDENTIAL_CONFIGURATIONS_KEY
- = "ONLY_LINK_SAME_CREDENTIAL_CONFIGURATIONS: ";
+ = "ONLY_LINK_SAME_CREDENTIAL_CONFIGURATIONS";
private static final String ENABLE_FULL_BAND_SCAN_WHEN_ASSOCIATED_KEY
- = "ENABLE_FULL_BAND_SCAN_WHEN_ASSOCIATED: ";
+ = "ENABLE_FULL_BAND_SCAN_WHEN_ASSOCIATED";
+
+ private static final String ENABLE_HAL_BASED_PNO
+ = "ENABLE_HAL_BASED_PNO";
// The three below configurations are mainly for power stats and CPU usage tracking
// allowing to incrementally disable framework features
- private static final String ENABLE_AUTO_JOIN_SCAN_WHILE_ASSOCIATED_KEY
- = "ENABLE_AUTO_JOIN_SCAN_WHILE_ASSOCIATED: ";
private static final String ENABLE_AUTO_JOIN_WHILE_ASSOCIATED_KEY
- = "ENABLE_AUTO_JOIN_WHILE_ASSOCIATED: ";
+ = "ENABLE_AUTO_JOIN_WHILE_ASSOCIATED";
private static final String ENABLE_CHIP_WAKE_UP_WHILE_ASSOCIATED_KEY
- = "ENABLE_CHIP_WAKE_UP_WHILE_ASSOCIATED: ";
+ = "ENABLE_CHIP_WAKE_UP_WHILE_ASSOCIATED";
private static final String ENABLE_RSSI_POLL_WHILE_ASSOCIATED_KEY
- = "ENABLE_RSSI_POLL_WHILE_ASSOCIATED_KEY: ";
+ = "ENABLE_RSSI_POLL_WHILE_ASSOCIATED_KEY";
+
+ public static final String idStringVarName = "id_str";
// The Wifi verbose log is provided as a way to persist the verbose logging settings
// for testing purpose.
// It is not intended for normal use.
private static final String WIFI_VERBOSE_LOGS_KEY
- = "WIFI_VERBOSE_LOGS: ";
+ = "WIFI_VERBOSE_LOGS";
// As we keep deleted PSK WifiConfiguration for a while, the PSK of
// those deleted WifiConfiguration is set to this random unused PSK
private static final String DELETED_CONFIG_PSK = "Mjkd86jEMGn79KhKll298Uu7-deleted";
- public boolean enableAutoJoinScanWhenAssociated = true;
- public boolean enableAutoJoinWhenAssociated = true;
- public boolean enableChipWakeUpWhenAssociated = true;
- public boolean enableRssiPollWhenAssociated = true;
-
- public int maxTxPacketForNetworkSwitching = 40;
- public int maxRxPacketForNetworkSwitching = 80;
-
public int maxTxPacketForFullScans = 8;
public int maxRxPacketForFullScans = 16;
public int maxTxPacketForPartialScans = 40;
public int maxRxPacketForPartialScans = 80;
- public boolean enableFullBandScanWhenAssociated = true;
-
- public int thresholdInitialAutoJoinAttemptMin5RSSI
- = WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_5;
- public int thresholdInitialAutoJoinAttemptMin24RSSI
- = WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_24;
-
- public int thresholdBadRssi5 = WifiConfiguration.BAD_RSSI_5;
- public int thresholdLowRssi5 = WifiConfiguration.LOW_RSSI_5;
- public int thresholdGoodRssi5 = WifiConfiguration.GOOD_RSSI_5;
- public int thresholdBadRssi24 = WifiConfiguration.BAD_RSSI_24;
- public int thresholdLowRssi24 = WifiConfiguration.LOW_RSSI_24;
- public int thresholdGoodRssi24 = WifiConfiguration.GOOD_RSSI_24;
-
- public int associatedFullScanBackoff = 12; // Will be divided by 8 by WifiStateMachine
public int associatedFullScanMaxIntervalMilli = 300000;
- public int associatedPartialScanPeriodMilli;
-
// Sane value for roam blacklisting (not switching to a network if already associated)
// 2 days
public int networkSwitchingBlackListPeriodMilli = 2 * 24 * 60 * 60 * 1000;
public int bandPreferenceBoostFactor5 = 5; // Boost by 5 dB per dB above threshold
public int bandPreferencePenaltyFactor5 = 2; // Penalize by 2 dB per dB below threshold
- public int bandPreferencePenaltyThreshold5 = WifiConfiguration.G_BAND_PREFERENCE_RSSI_THRESHOLD;
- public int bandPreferenceBoostThreshold5 = WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD;
public int badLinkSpeed24 = 6;
public int badLinkSpeed5 = 12;
@@ -367,22 +378,8 @@
public int associatedHysteresisHigh = +14;
public int associatedHysteresisLow = +8;
- public int thresholdUnblacklistThreshold5Hard
- = WifiConfiguration.UNBLACKLIST_THRESHOLD_5_HARD;
- public int thresholdUnblacklistThreshold5Soft
- = WifiConfiguration.UNBLACKLIST_THRESHOLD_5_SOFT;
- public int thresholdUnblacklistThreshold24Hard
- = WifiConfiguration.UNBLACKLIST_THRESHOLD_24_HARD;
- public int thresholdUnblacklistThreshold24Soft
- = WifiConfiguration.UNBLACKLIST_THRESHOLD_24_SOFT;
- public int enableVerboseLogging = 0;
boolean showNetworks = true; // TODO set this back to false, used for debugging 17516271
- public int alwaysEnableScansWhileAssociated = 0;
-
- public int maxNumActiveChannelsForPartialScans = 6;
- public int maxNumPassiveChannelsForPartialScans = 2;
-
public boolean roamOnAny = false;
public boolean onlyLinkSameCredentialConfigurations = true;
@@ -395,6 +392,51 @@
public static final int maxNumScanCacheEntries = 128;
+ public final AtomicBoolean enableHalBasedPno = new AtomicBoolean(false);
+ public final AtomicBoolean enableSsidWhitelist = new AtomicBoolean(false);
+ public final AtomicBoolean enableAutoJoinWhenAssociated = new AtomicBoolean(true);
+ public final AtomicBoolean enableFullBandScanWhenAssociated = new AtomicBoolean(true);
+ public final AtomicBoolean enableChipWakeUpWhenAssociated = new AtomicBoolean(true);
+ public final AtomicBoolean enableRssiPollWhenAssociated = new AtomicBoolean(true);
+ public final AtomicInteger thresholdInitialAutoJoinAttemptMin5RSSI =
+ new AtomicInteger(WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_5);
+ public final AtomicInteger thresholdInitialAutoJoinAttemptMin24RSSI =
+ new AtomicInteger(WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_24);
+ public final AtomicInteger thresholdUnblacklistThreshold5Hard
+ = new AtomicInteger(WifiConfiguration.UNBLACKLIST_THRESHOLD_5_HARD);
+ public final AtomicInteger thresholdUnblacklistThreshold5Soft
+ = new AtomicInteger(WifiConfiguration.UNBLACKLIST_THRESHOLD_5_SOFT);
+ public final AtomicInteger thresholdUnblacklistThreshold24Hard
+ = new AtomicInteger(WifiConfiguration.UNBLACKLIST_THRESHOLD_24_HARD);
+ public final AtomicInteger thresholdUnblacklistThreshold24Soft
+ = new AtomicInteger(WifiConfiguration.UNBLACKLIST_THRESHOLD_24_SOFT);
+ public final AtomicInteger thresholdGoodRssi5 =
+ new AtomicInteger(WifiConfiguration.GOOD_RSSI_5);
+ public final AtomicInteger thresholdLowRssi5 = new AtomicInteger(WifiConfiguration.LOW_RSSI_5);
+ public final AtomicInteger thresholdBadRssi5 = new AtomicInteger(WifiConfiguration.BAD_RSSI_5);
+ public final AtomicInteger thresholdGoodRssi24 =
+ new AtomicInteger(WifiConfiguration.GOOD_RSSI_24);
+ public final AtomicInteger thresholdLowRssi24 = new AtomicInteger(WifiConfiguration.LOW_RSSI_24);
+ public final AtomicInteger thresholdBadRssi24 = new AtomicInteger(WifiConfiguration.BAD_RSSI_24);
+ public final AtomicInteger maxTxPacketForNetworkSwitching = new AtomicInteger(40);
+ public final AtomicInteger maxRxPacketForNetworkSwitching = new AtomicInteger(80);
+ public final AtomicInteger enableVerboseLogging = new AtomicInteger(0);
+ public final AtomicInteger bandPreferenceBoostThreshold5 =
+ new AtomicInteger(WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD);
+ public final AtomicInteger associatedFullScanBackoff =
+ new AtomicInteger(12); // Will be divided by 8 by WifiStateMachine
+ public final AtomicInteger bandPreferencePenaltyThreshold5 =
+ new AtomicInteger(WifiConfiguration.G_BAND_PREFERENCE_RSSI_THRESHOLD);
+ public final AtomicInteger alwaysEnableScansWhileAssociated = new AtomicInteger(0);
+ public final AtomicInteger maxNumPassiveChannelsForPartialScans = new AtomicInteger(2);
+ public final AtomicInteger maxNumActiveChannelsForPartialScans = new AtomicInteger(6);
+ public final AtomicInteger wifiDisconnectedShortScanIntervalMilli = new AtomicInteger(15000);
+ public final AtomicInteger wifiDisconnectedLongScanIntervalMilli = new AtomicInteger(120000);
+ public final AtomicInteger wifiAssociatedShortScanIntervalMilli = new AtomicInteger(20000);
+ public final AtomicInteger wifiAssociatedLongScanIntervalMilli = new AtomicInteger(180000);
+
+ private static final Map<String, Object> sKeyMap = new HashMap<>();
+
/**
* Regex pattern for extracting a connect choice.
* Matches a strings like the following:
@@ -426,7 +468,9 @@
WifiEnterpriseConfig.PASSWORD_KEY, WifiEnterpriseConfig.CLIENT_CERT_KEY,
WifiEnterpriseConfig.CA_CERT_KEY, WifiEnterpriseConfig.SUBJECT_MATCH_KEY,
WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ID_KEY,
- WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY };
+ WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY,
+ WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY
+ };
/**
@@ -460,9 +504,70 @@
*/
private String lastSelectedConfiguration = null;
- WifiConfigStore(Context c, WifiNative wn) {
+ /**
+ * Cached PNO list, it is updated when WifiConfiguration changes due to user input.
+ */
+ ArrayList<WifiNative.WifiPnoNetwork> mCachedPnoList
+ = new ArrayList<WifiNative.WifiPnoNetwork>();
+
+ /*
+ * BSSID blacklist, i.e. list of BSSID we want to avoid
+ */
+ HashSet<String> mBssidBlacklist = new HashSet<String>();
+
+ /*
+ * Lost config list, whenever we read a config from networkHistory.txt that was not in
+ * wpa_supplicant.conf
+ */
+ HashSet<String> mLostConfigsDbg = new HashSet<String>();
+
+ private final AnqpCache mAnqpCache;
+ private final SupplicantBridge mSupplicantBridge;
+ private final MOManager mMOManager;
+ private final SIMAccessor mSIMAccessor;
+
+ private WifiStateMachine mWifiStateMachine;
+
+ WifiConfigStore(Context c, WifiStateMachine w, WifiNative wn) {
mContext = c;
mWifiNative = wn;
+ mWifiStateMachine = w;
+
+ // A map for value setting in readAutoJoinConfig() - replacing the replicated code.
+ sKeyMap.put(ENABLE_AUTO_JOIN_WHILE_ASSOCIATED_KEY, enableAutoJoinWhenAssociated);
+ sKeyMap.put(ENABLE_FULL_BAND_SCAN_WHEN_ASSOCIATED_KEY, enableFullBandScanWhenAssociated);
+ sKeyMap.put(ENABLE_CHIP_WAKE_UP_WHILE_ASSOCIATED_KEY, enableChipWakeUpWhenAssociated);
+ sKeyMap.put(ENABLE_RSSI_POLL_WHILE_ASSOCIATED_KEY, enableRssiPollWhenAssociated);
+ sKeyMap.put(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY, thresholdInitialAutoJoinAttemptMin5RSSI);
+ sKeyMap.put(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY, thresholdInitialAutoJoinAttemptMin24RSSI);
+ sKeyMap.put(THRESHOLD_UNBLACKLIST_HARD_5G_KEY, thresholdUnblacklistThreshold5Hard);
+ sKeyMap.put(THRESHOLD_UNBLACKLIST_SOFT_5G_KEY, thresholdUnblacklistThreshold5Soft);
+ sKeyMap.put(THRESHOLD_UNBLACKLIST_HARD_24G_KEY, thresholdUnblacklistThreshold24Hard);
+ sKeyMap.put(THRESHOLD_UNBLACKLIST_SOFT_24G_KEY, thresholdUnblacklistThreshold24Soft);
+ sKeyMap.put(THRESHOLD_GOOD_RSSI_5_KEY, thresholdGoodRssi5);
+ sKeyMap.put(THRESHOLD_LOW_RSSI_5_KEY, thresholdLowRssi5);
+ sKeyMap.put(THRESHOLD_BAD_RSSI_5_KEY, thresholdBadRssi5);
+ sKeyMap.put(THRESHOLD_GOOD_RSSI_24_KEY, thresholdGoodRssi24);
+ sKeyMap.put(THRESHOLD_LOW_RSSI_24_KEY, thresholdLowRssi24);
+ sKeyMap.put(THRESHOLD_BAD_RSSI_24_KEY, thresholdBadRssi24);
+ sKeyMap.put(THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY, maxTxPacketForNetworkSwitching);
+ sKeyMap.put(THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY, maxRxPacketForNetworkSwitching);
+ sKeyMap.put(THRESHOLD_MAX_TX_PACKETS_FOR_FULL_SCANS_KEY, maxTxPacketForNetworkSwitching);
+ sKeyMap.put(THRESHOLD_MAX_RX_PACKETS_FOR_FULL_SCANS_KEY, maxRxPacketForNetworkSwitching);
+ sKeyMap.put(THRESHOLD_MAX_TX_PACKETS_FOR_PARTIAL_SCANS_KEY, maxTxPacketForNetworkSwitching);
+ sKeyMap.put(THRESHOLD_MAX_RX_PACKETS_FOR_PARTIAL_SCANS_KEY, maxRxPacketForNetworkSwitching);
+ sKeyMap.put(WIFI_VERBOSE_LOGS_KEY, enableVerboseLogging);
+ sKeyMap.put(A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, bandPreferenceBoostThreshold5);
+ sKeyMap.put(ASSOCIATED_PARTIAL_SCAN_PERIOD_KEY, wifiAssociatedShortScanIntervalMilli);
+ sKeyMap.put(ASSOCIATED_PARTIAL_SCAN_PERIOD_KEY, wifiAssociatedShortScanIntervalMilli);
+
+ sKeyMap.put(ASSOCIATED_FULL_SCAN_BACKOFF_KEY, associatedFullScanBackoff);
+ sKeyMap.put(G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, bandPreferencePenaltyThreshold5);
+ sKeyMap.put(ALWAYS_ENABLE_SCAN_WHILE_ASSOCIATED_KEY, alwaysEnableScansWhileAssociated);
+ sKeyMap.put(MAX_NUM_PASSIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY, maxNumPassiveChannelsForPartialScans);
+ sKeyMap.put(MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY, maxNumActiveChannelsForPartialScans);
+ sKeyMap.put(ENABLE_HAL_BASED_PNO, enableHalBasedPno);
+ sKeyMap.put(ENABLE_HAL_BASED_PNO, enableSsidWhitelist);
if (showNetworks) {
mLocalLog = mWifiNative.getLocalLog();
@@ -473,20 +578,25 @@
mFileObserver = null;
}
- associatedPartialScanPeriodMilli = mContext.getResources().getInteger(
- R.integer.config_wifi_framework_associated_scan_interval);
- loge("associatedPartialScanPeriodMilli set to " + associatedPartialScanPeriodMilli);
+ wifiAssociatedShortScanIntervalMilli.set(mContext.getResources().getInteger(
+ R.integer.config_wifi_associated_short_scan_interval));
+ wifiAssociatedLongScanIntervalMilli.set(mContext.getResources().getInteger(
+ R.integer.config_wifi_associated_short_scan_interval));
+ wifiDisconnectedShortScanIntervalMilli.set(mContext.getResources().getInteger(
+ R.integer.config_wifi_disconnected_short_scan_interval));
+ wifiDisconnectedLongScanIntervalMilli.set(mContext.getResources().getInteger(
+ R.integer.config_wifi_disconnected_long_scan_interval));
onlyLinkSameCredentialConfigurations = mContext.getResources().getBoolean(
R.bool.config_wifi_only_link_same_credential_configurations);
- maxNumActiveChannelsForPartialScans = mContext.getResources().getInteger(
- R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels);
- maxNumPassiveChannelsForPartialScans = mContext.getResources().getInteger(
- R.integer.config_wifi_framework_associated_partial_scan_max_num_passive_channels);
+ maxNumActiveChannelsForPartialScans.set(mContext.getResources().getInteger(
+ R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels));
+ maxNumPassiveChannelsForPartialScans.set(mContext.getResources().getInteger(
+ R.integer.config_wifi_framework_associated_partial_scan_max_num_passive_channels));
associatedFullScanMaxIntervalMilli = mContext.getResources().getInteger(
R.integer.config_wifi_framework_associated_full_scan_max_interval);
- associatedFullScanBackoff = mContext.getResources().getInteger(
- R.integer.config_wifi_framework_associated_full_scan_backoff);
+ associatedFullScanBackoff.set(mContext.getResources().getInteger(
+ R.integer.config_wifi_framework_associated_full_scan_backoff));
enableLinkDebouncing = mContext.getResources().getBoolean(
R.bool.config_wifi_enable_disconnection_debounce);
@@ -498,28 +608,28 @@
bandPreferencePenaltyFactor5 = mContext.getResources().getInteger(
R.integer.config_wifi_framework_5GHz_preference_penalty_factor);
- bandPreferencePenaltyThreshold5 = mContext.getResources().getInteger(
- R.integer.config_wifi_framework_5GHz_preference_penalty_threshold);
- bandPreferenceBoostThreshold5 = mContext.getResources().getInteger(
- R.integer.config_wifi_framework_5GHz_preference_boost_threshold);
+ bandPreferencePenaltyThreshold5.set(mContext.getResources().getInteger(
+ R.integer.config_wifi_framework_5GHz_preference_penalty_threshold));
+ bandPreferenceBoostThreshold5.set(mContext.getResources().getInteger(
+ R.integer.config_wifi_framework_5GHz_preference_boost_threshold));
associatedHysteresisHigh = mContext.getResources().getInteger(
R.integer.config_wifi_framework_current_association_hysteresis_high);
associatedHysteresisLow = mContext.getResources().getInteger(
R.integer.config_wifi_framework_current_association_hysteresis_low);
- thresholdBadRssi5 = mContext.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz);
- thresholdLowRssi5 = mContext.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz);
- thresholdGoodRssi5 = mContext.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz);
- thresholdBadRssi24 = mContext.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz);
- thresholdLowRssi24 = mContext.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz);
- thresholdGoodRssi24 = mContext.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz);
+ thresholdBadRssi5.set(mContext.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz));
+ thresholdLowRssi5.set(mContext.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz));
+ thresholdGoodRssi5.set(mContext.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz));
+ thresholdBadRssi24.set(mContext.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz));
+ thresholdLowRssi24.set(mContext.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz));
+ thresholdGoodRssi24.set(mContext.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz));
enableWifiCellularHandoverUserTriggeredAdjustment = mContext.getResources().getBoolean(
R.bool.config_wifi_framework_cellular_handover_enable_user_triggered_adjustment);
@@ -540,12 +650,8 @@
wifiConfigBlacklistMinTimeMilli = mContext.getResources().getInteger(
R.integer.config_wifi_framework_network_black_list_min_time_milli);
-
- enableAutoJoinScanWhenAssociated = mContext.getResources().getBoolean(
- R.bool.config_wifi_framework_enable_associated_autojoin_scan);
-
- enableAutoJoinWhenAssociated = mContext.getResources().getBoolean(
- R.bool.config_wifi_framework_enable_associated_network_selection);
+ enableAutoJoinWhenAssociated.set(mContext.getResources().getBoolean(
+ R.bool.config_wifi_framework_enable_associated_network_selection));
currentNetworkBoost = mContext.getResources().getInteger(
R.integer.config_wifi_framework_current_network_boost);
@@ -555,10 +661,33 @@
networkSwitchingBlackListPeriodMilli = mContext.getResources().getInteger(
R.integer.config_wifi_network_switching_blacklist_time);
+
+ enableHalBasedPno.set(mContext.getResources().getBoolean(
+ R.bool.config_wifi_hal_pno_enable));
+
+ enableSsidWhitelist.set(mContext.getResources().getBoolean(
+ R.bool.config_wifi_ssid_white_list_enable));
+ if (!enableHalBasedPno.get() && enableSsidWhitelist.get()) {
+ enableSsidWhitelist.set(false);
+ }
+
+ boolean hs2on = mContext.getResources().getBoolean(R.bool.config_wifi_hotspot2_enabled);
+ Log.d(Utils.hs2LogTag(getClass()), "Passpoint is " + (hs2on ? "enabled" : "disabled"));
+
+ mMOManager = new MOManager(new File(PPS_FILE), hs2on);
+ mAnqpCache = new AnqpCache();
+ mSupplicantBridge = new SupplicantBridge(mWifiNative, this);
+ mScanDetailCaches = new HashMap<>();
+
+ mSIMAccessor = new SIMAccessor(mContext);
+ }
+
+ public void trimANQPCache(boolean all) {
+ mAnqpCache.clear(all, DBG);
}
void enableVerboseLogging(int verbose) {
- enableVerboseLogging = verbose;
+ enableVerboseLogging.set(verbose);
if (verbose > 0) {
VDBG = true;
showNetworks = true;
@@ -602,7 +731,8 @@
return mConfiguredNetworks.size();
}
- private List<WifiConfiguration> getConfiguredNetworks(Map<String, String> pskMap) {
+ private List<WifiConfiguration>
+ getConfiguredNetworks(Map<String, String> pskMap) {
List<WifiConfiguration> networks = new ArrayList<>();
for(WifiConfiguration config : mConfiguredNetworks.values()) {
WifiConfiguration newConfig = new WifiConfiguration(config);
@@ -627,6 +757,19 @@
}
/**
+ * This function returns all configuration, and is used for cebug and creating bug reports.
+ */
+ private List<WifiConfiguration>
+ getAllConfiguredNetworks() {
+ List<WifiConfiguration> networks = new ArrayList<>();
+ for(WifiConfiguration config : mConfiguredNetworks.values()) {
+ WifiConfiguration newConfig = new WifiConfiguration(config);
+ networks.add(newConfig);
+ }
+ return networks;
+ }
+
+ /**
* Fetch the list of currently configured networks
* @return List of networks
*/
@@ -644,6 +787,27 @@
}
/**
+ * Find matching network for this scanResult
+ */
+ WifiConfiguration getMatchingConfig(ScanResult scanResult) {
+ if (scanResult == null) {
+ return null;
+ }
+ for (Map.Entry entry : mScanDetailCaches.entrySet()) {
+ Integer netId = (Integer) entry.getKey();
+ ScanDetailCache cache = (ScanDetailCache) entry.getValue();
+ WifiConfiguration config = getWifiConfiguration(netId);
+ if (config == null)
+ continue;
+ if (cache.get(scanResult.BSSID) != null) {
+ return config;
+ }
+ }
+
+ return null;
+ }
+
+ /**
* Fetch the preSharedKeys for all networks.
* @return a map from Ssid to preSharedKey.
*/
@@ -651,12 +815,6 @@
return readNetworkVariablesFromSupplicantFile("psk");
}
- int getconfiguredNetworkSize() {
- if (mConfiguredNetworks == null)
- return 0;
- return mConfiguredNetworks.size();
- }
-
/**
* Fetch the list of currently configured networks that were recently seen
*
@@ -674,7 +832,11 @@
}
// Calculate the RSSI for scan results that are more recent than milli
- config.setVisibility(milli);
+ ScanDetailCache cache = getScanDetailCache(config);
+ if (cache == null) {
+ continue;
+ }
+ config.setVisibility(cache.getVisibility(milli));
if (config.visibility == null) {
continue;
}
@@ -696,14 +858,15 @@
*/
void updateConfiguration(WifiInfo info) {
WifiConfiguration config = getWifiConfiguration(info.getNetworkId());
- if (config != null && config.scanResultCache != null) {
- ScanResult result = config.scanResultCache.get(info.getBSSID());
- if (result != null) {
+ if (config != null && getScanDetailCache(config) != null) {
+ ScanDetail scanDetail = getScanDetailCache(config).getScanDetail(info.getBSSID());
+ if (scanDetail != null) {
+ ScanResult result = scanDetail.getScanResult();
long previousSeen = result.seen;
int previousRssi = result.level;
// Update the scan result
- result.seen = System.currentTimeMillis();
+ scanDetail.setSeen();
result.level = info.getRssi();
// Average the RSSI value
@@ -725,8 +888,6 @@
* @return Wificonfiguration
*/
WifiConfiguration getWifiConfiguration(int netId) {
- if (mConfiguredNetworks == null)
- return null;
return mConfiguredNetworks.get(netId);
}
@@ -735,16 +896,7 @@
* @return Wificonfiguration
*/
WifiConfiguration getWifiConfiguration(String key) {
- if (key == null)
- return null;
- int hash = key.hashCode();
- if (mNetworkIds == null)
- return null;
- Integer n = mNetworkIds.get(hash);
- if (n == null)
- return null;
- int netId = n.intValue();
- return getWifiConfiguration(netId);
+ return mConfiguredNetworks.getByConfigKey(key);
}
/**
@@ -786,6 +938,7 @@
config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
} else {
loge("Enable network failed on " + config.networkId);
+
}
}
}
@@ -796,6 +949,26 @@
}
}
+ private boolean setNetworkPriorityNative(int netId, int priority) {
+ return mWifiNative.setNetworkVariable(netId,
+ WifiConfiguration.priorityVarName, Integer.toString(priority));
+ }
+
+ private boolean setSSIDNative(int netId, String ssid) {
+ return mWifiNative.setNetworkVariable(netId, WifiConfiguration.ssidVarName,
+ encodeSSID(ssid));
+ }
+
+ public boolean updateLastConnectUid(WifiConfiguration config, int uid) {
+ if (config != null) {
+ if (config.lastConnectUid != uid) {
+ config.lastConnectUid = uid;
+ config.dirty = true;
+ return true;
+ }
+ }
+ return false;
+ }
/**
* Selects the specified network for connection. This involves
@@ -806,34 +979,61 @@
* a call to enableAllNetworks() needs to be issued upon a connection
* or a failure event from supplicant
*
- * @param netId network to select for connection
+ * @param config network to select for connection
+ * @param updatePriorities makes config highest priority network
* @return false if the network id is invalid
*/
- boolean selectNetwork(int netId) {
- if (VDBG) localLog("selectNetwork", netId);
- if (netId == INVALID_NETWORK_ID) return false;
+ boolean selectNetwork(WifiConfiguration config, boolean updatePriorities, int uid) {
+ if (VDBG) localLog("selectNetwork", config.networkId);
+ if (config.networkId == INVALID_NETWORK_ID) return false;
// Reset the priority of each network at start or if it goes too high.
if (mLastPriority == -1 || mLastPriority > 1000000) {
- for(WifiConfiguration config : mConfiguredNetworks.values()) {
- if (config.networkId != INVALID_NETWORK_ID) {
- config.priority = 0;
- addOrUpdateNetworkNative(config, -1);
+ for(WifiConfiguration config2 : mConfiguredNetworks.values()) {
+ if (updatePriorities) {
+ if (config2.networkId != INVALID_NETWORK_ID) {
+ config2.priority = 0;
+ setNetworkPriorityNative(config2.networkId, config.priority);
+ }
}
}
mLastPriority = 0;
}
// Set to the highest priority and save the configuration.
- WifiConfiguration config = new WifiConfiguration();
- config.networkId = netId;
- config.priority = ++mLastPriority;
+ if (updatePriorities) {
+ config.priority = ++mLastPriority;
+ setNetworkPriorityNative(config.networkId, config.priority);
+ buildPnoList();
+ }
- addOrUpdateNetworkNative(config, -1);
- mWifiNative.saveConfig();
+ if (config.isPasspoint()) {
+ /* need to slap on the SSID of selected bssid to work */
+ if (getScanDetailCache(config).size() != 0) {
+ ScanDetail result = getScanDetailCache(config).getFirst();
+ if (result == null) {
+ loge("Could not find scan result for " + config.BSSID);
+ } else {
+ log("Setting SSID for " + config.networkId + " to" + result.getSSID());
+ setSSIDNative(config.networkId, result.getSSID());
+ config.SSID = result.getSSID();
+ }
+
+ } else {
+ loge("Could not find bssid for " + config);
+ }
+ }
+
+ if (updatePriorities)
+ mWifiNative.saveConfig();
+ else
+ mWifiNative.selectNetwork(config.networkId);
+
+ updateLastConnectUid(config, uid);
+ writeKnownNetworkHistory(false);
/* Enable the given network while disabling all other networks */
- enableNetworkWithoutBroadcast(netId, true);
+ enableNetworkWithoutBroadcast(config.networkId, true);
/* Avoid saving the config & sending a broadcast to prevent settings
* from displaying a disabled list of networks */
@@ -922,11 +1122,11 @@
if (info != null
&& info.getBSSID() != null
&& ScanResult.is5GHz(info.getFrequency())
- && info.getRssi() > (bandPreferenceBoostThreshold5 + 3)) {
+ && info.getRssi() > (bandPreferenceBoostThreshold5.get() + 3)) {
WifiConfiguration config = getWifiConfiguration(info.getNetworkId());
if (config != null) {
- if (config.scanResultCache != null) {
- ScanResult result = config.scanResultCache.get(info.getBSSID());
+ if (getScanDetailCache(config) != null) {
+ ScanResult result = getScanDetailCache(config).get(info.getBSSID());
if (result != null) {
result.setAutoJoinStatus(ScanResult.AUTO_ROAM_DISABLED + 1);
}
@@ -1017,16 +1217,13 @@
return null;
}
- WifiConfiguration foundConfig = null;
+ WifiConfiguration foundConfig = mConfiguredNetworks.getEphemeral(SSID);
mDeletedEphemeralSSIDs.add(SSID);
loge("Forget ephemeral SSID " + SSID + " num=" + mDeletedEphemeralSSIDs.size());
- for (WifiConfiguration config : mConfiguredNetworks.values()) {
- if (SSID.equals(config.SSID) && config.ephemeral) {
- loge("Found ephemeral config in disableEphemeralNetwork: " + config.networkId);
- foundConfig = config;
- }
+ if (foundConfig != null) {
+ loge("Found ephemeral config in disableEphemeralNetwork: " + foundConfig.networkId);
}
// Force a write, because the mDeletedEphemeralSSIDs list has changed even though the
@@ -1045,13 +1242,18 @@
boolean forgetNetwork(int netId) {
if (showNetworks) localLog("forgetNetwork", netId);
+ WifiConfiguration config = mConfiguredNetworks.get(netId);
boolean remove = removeConfigAndSendBroadcastIfNeeded(netId);
if (!remove) {
//success but we dont want to remove the network from supplicant conf file
return true;
}
if (mWifiNative.removeNetwork(netId)) {
+ if (config != null && config.isPasspoint()) {
+ writePasspointConfigs(config.FQDN, null);
+ }
mWifiNative.saveConfig();
+ writeKnownNetworkHistory(true);
return true;
} else {
loge("Failed to remove network " + netId);
@@ -1074,6 +1276,14 @@
Log.e(TAG, " key=" + config.configKey() + " netId=" + Integer.toString(config.networkId)
+ " uid=" + Integer.toString(config.creatorUid)
+ "/" + Integer.toString(config.lastUpdateUid));
+
+ if (config.isPasspoint()) {
+ /* create a temporary SSID with providerFriendlyName */
+ Long csum = getChecksum(config.FQDN);
+ config.SSID = csum.toString();
+ config.enterpriseConfig.setDomainSuffixMatch(config.FQDN);
+ }
+
NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid);
if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
WifiConfiguration conf = mConfiguredNetworks.get(result.getNetworkId());
@@ -1083,9 +1293,103 @@
WifiManager.CHANGE_REASON_CONFIG_CHANGE);
}
}
+
return result.getNetworkId();
}
+
+ /**
+ * Get the Wifi PNO list
+ *
+ * @return list of WifiNative.WifiPnoNetwork
+ */
+ private void buildPnoList() {
+ mCachedPnoList = new ArrayList<WifiNative.WifiPnoNetwork>();
+
+ ArrayList<WifiConfiguration> sortedWifiConfigurations
+ = new ArrayList<WifiConfiguration>(getConfiguredNetworks());
+ Log.e(TAG, "buildPnoList sortedWifiConfigurations size " + sortedWifiConfigurations.size());
+ if (sortedWifiConfigurations.size() != 0) {
+ // Sort by descending priority
+ Collections.sort(sortedWifiConfigurations, new Comparator<WifiConfiguration>() {
+ public int compare(WifiConfiguration a, WifiConfiguration b) {
+ return a.priority >= b.priority ? 1 : -1;
+ }
+ });
+ }
+
+ for (WifiConfiguration config : sortedWifiConfigurations) {
+ // Initialize the RSSI threshold with sane value:
+ // Use the 2.4GHz threshold since most WifiConfigurations are dual bands
+ // There is very little penalty with triggering too soon, i.e. if PNO finds a network
+ // that has an RSSI too low for us to attempt joining it.
+ int threshold = thresholdInitialAutoJoinAttemptMin24RSSI.get();
+ Log.e(TAG, "found sortedWifiConfigurations : " + config.configKey());
+ WifiNative.WifiPnoNetwork network = mWifiNative.new WifiPnoNetwork(config, threshold);
+ mCachedPnoList.add(network);
+ }
+ }
+
+ String[] getWhiteListedSsids(WifiConfiguration config) {
+ int num_ssids = 0;
+ String nonQuoteSSID;
+ int length;
+ if (enableSsidWhitelist.get() == false)
+ return null;
+ List<String> list = new ArrayList<String>();
+ if (config == null)
+ return null;
+ if (config.linkedConfigurations == null) {
+ return null;
+ }
+ if (config.SSID == null || TextUtils.isEmpty(config.SSID)) {
+ return null;
+ }
+ for (String configKey : config.linkedConfigurations.keySet()) {
+
+ // Sanity check that the linked configuration is still valid
+ WifiConfiguration link = getWifiConfiguration(configKey);
+ if (link == null) {
+ continue;
+ }
+
+ if (link.autoJoinStatus != WifiConfiguration.AUTO_JOIN_ENABLED) {
+ continue;
+ }
+
+ if (link.hiddenSSID == true) {
+ continue;
+ }
+
+ if (link.SSID == null || TextUtils.isEmpty(link.SSID)) {
+ continue;
+ }
+
+ length = link.SSID.length();
+ if (length > 2 && (link.SSID.charAt(0) == '"') && link.SSID.charAt(length - 1) == '"') {
+ nonQuoteSSID = link.SSID.substring(1, length - 1);
+ } else {
+ nonQuoteSSID = link.SSID;
+ }
+
+ list.add(nonQuoteSSID);
+ }
+
+ if (list.size() != 0) {
+ length = config.SSID.length();
+ if (length > 2 && (config.SSID.charAt(0) == '"')
+ && config.SSID.charAt(length - 1) == '"') {
+ nonQuoteSSID = config.SSID.substring(1, length - 1);
+ } else {
+ nonQuoteSSID = config.SSID;
+ }
+
+ list.add(nonQuoteSSID);
+ }
+
+ return (String[])list.toArray(new String[0]);
+ }
+
/**
* Remove a network. Note that there is no saveConfig operation.
* This function is retained for compatibility with the public
@@ -1097,13 +1401,24 @@
*/
boolean removeNetwork(int netId) {
if (showNetworks) localLog("removeNetwork", netId);
+ WifiConfiguration config = mConfiguredNetworks.get(netId);
boolean ret = mWifiNative.removeNetwork(netId);
if (ret) {
removeConfigAndSendBroadcastIfNeeded(netId);
+ if (config != null && config.isPasspoint()) {
+ writePasspointConfigs(config.FQDN, null);
+ }
}
return ret;
}
+
+ static private Long getChecksum(String source) {
+ Checksum csum = new CRC32();
+ csum.update(source.getBytes(), 0, source.getBytes().length);
+ return csum.getValue();
+ }
+
private boolean removeConfigAndSendBroadcastIfNeeded(int netId) {
WifiConfiguration config = mConfiguredNetworks.get(netId);
if (config != null) {
@@ -1126,20 +1441,23 @@
|| config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
if (!TextUtils.isEmpty(config.SSID)) {
/* Remember that we deleted this PSK SSID */
- Checksum csum = new CRC32();
if (config.SSID != null) {
- csum.update(config.SSID.getBytes(), 0, config.SSID.getBytes().length);
- mDeletedSSIDs.add(csum.getValue());
+ Long csum = getChecksum(config.SSID);
+ mDeletedSSIDs.add(csum);
+ loge("removeNetwork " + Integer.toString(netId)
+ + " key=" + config.configKey()
+ + " config.id=" + Integer.toString(config.networkId)
+ + " crc=" + csum);
+ } else {
+ loge("removeNetwork " + Integer.toString(netId)
+ + " key=" + config.configKey()
+ + " config.id=" + Integer.toString(config.networkId));
}
- loge("removeNetwork " + Integer.toString(netId)
- + " key=" + config.configKey()
- + " config.id=" + Integer.toString(config.networkId)
- + " crc=" + csum.getValue());
}
}
mConfiguredNetworks.remove(netId);
- mNetworkIds.remove(configKey(config));
+ mScanDetailCaches.remove(netId);
writeIpAndProxyConfigurations();
sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
@@ -1148,6 +1466,57 @@
return true;
}
+ /*
+ * Remove all networks associated with an application
+ *
+ * @param packageName name of the package of networks to remove
+ * @return {@code true} if all networks removed successfully, {@code false} otherwise
+ */
+ boolean removeNetworksForApp(ApplicationInfo app) {
+ if (app == null || app.packageName == null) {
+ return false;
+ }
+
+ boolean success = true;
+
+ WifiConfiguration [] copiedConfigs =
+ mConfiguredNetworks.values().toArray(new WifiConfiguration[0]);
+ for (WifiConfiguration config : copiedConfigs) {
+ if (app.uid != config.creatorUid || !app.packageName.equals(config.creatorName)) {
+ continue;
+ }
+ if (showNetworks) {
+ localLog("Removing network " + config.SSID
+ + ", application \"" + app.packageName + "\" uninstalled"
+ + " from user " + UserHandle.getUserId(app.uid));
+ }
+ success &= removeNetwork(config.networkId);
+ }
+
+ mWifiNative.saveConfig();
+
+ return success;
+ }
+
+ boolean removeNetworksForUser(int userId) {
+ boolean success = true;
+
+ WifiConfiguration[] copiedConfigs =
+ mConfiguredNetworks.values().toArray(new WifiConfiguration[0]);
+ for (WifiConfiguration config : copiedConfigs) {
+ if (userId != UserHandle.getUserId(config.creatorUid)) {
+ continue;
+ }
+ success &= removeNetwork(config.networkId);
+ if (showNetworks) {
+ localLog("Removing network " + config.SSID
+ + ", user " + userId + " removed");
+ }
+ }
+
+ return success;
+ }
+
/**
* Enable a network. Note that there is no saveConfig operation.
* This function is retained for compatibility with the public
@@ -1157,15 +1526,17 @@
* @param netId network to be enabled
* @return {@code true} if it succeeds, {@code false} otherwise
*/
- boolean enableNetwork(int netId, boolean disableOthers) {
+ boolean enableNetwork(int netId, boolean disableOthers, int uid) {
boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers);
if (disableOthers) {
- if (VDBG) localLog("enableNetwork(disableOthers=true) ", netId);
+ if (VDBG) localLog("enableNetwork(disableOthers=true, uid=" + uid + ") ", netId);
+ updateLastConnectUid(getWifiConfiguration(netId), uid);
+ writeKnownNetworkHistory(false);
sendConfiguredNetworksChangedBroadcast();
} else {
if (VDBG) localLog("enableNetwork(disableOthers=false) ", netId);
- WifiConfiguration enabledNetwork = null;
- synchronized(mConfiguredNetworks) {
+ WifiConfiguration enabledNetwork;
+ synchronized(mConfiguredNetworks) { // !!! Useless synchronization!
enabledNetwork = mConfiguredNetworks.get(netId);
}
// check just in case the network was removed by someone else.
@@ -1192,14 +1563,12 @@
void disableAllNetworks() {
if (VDBG) localLog("disableAllNetworks");
boolean networkDisabled = false;
- for(WifiConfiguration config : mConfiguredNetworks.values()) {
- if(config != null && config.status != Status.DISABLED) {
- if(mWifiNative.disableNetwork(config.networkId)) {
- networkDisabled = true;
- config.status = Status.DISABLED;
- } else {
- loge("Disable network failed on " + config.networkId);
- }
+ for (WifiConfiguration enabled : mConfiguredNetworks.getEnabledNetworks()) {
+ if(mWifiNative.disableNetwork(enabled.networkId)) {
+ networkDisabled = true;
+ enabled.status = Status.DISABLED;
+ } else {
+ loge("Disable network failed on " + enabled.networkId);
}
}
@@ -1213,7 +1582,11 @@
* @return {@code true} if it succeeds, {@code false} otherwise
*/
boolean disableNetwork(int netId) {
- return disableNetwork(netId, WifiConfiguration.DISABLED_UNKNOWN_REASON);
+ boolean ret = disableNetwork(netId, WifiConfiguration.DISABLED_UNKNOWN_REASON);
+ if (ret) {
+ mWifiStateMachine.registerNetworkDisabled(netId);
+ }
+ return ret;
}
/**
@@ -1359,7 +1732,7 @@
/**
* Fetch the proxy properties for a given network id
- * @param network id
+ * @param netId id
* @return ProxyInfo for the network id
*/
ProxyInfo getProxyProperties(int netId) {
@@ -1372,7 +1745,7 @@
/**
* Return if the specified network is using static IP
- * @param network id
+ * @param netId id
* @return {@code true} if using static ip for netId
*/
boolean isUsingStaticIp(int netId) {
@@ -1383,6 +1756,11 @@
return false;
}
+ boolean isEphemeral(int netId) {
+ WifiConfiguration config = mConfiguredNetworks.get(netId);
+ return config != null && config.ephemeral;
+ }
+
/**
* Should be called when a single network configuration is made.
* @param network The network configuration that changed.
@@ -1414,7 +1792,6 @@
mLastPriority = 0;
mConfiguredNetworks.clear();
- mNetworkIds.clear();
int last_id = -1;
boolean done = false;
@@ -1474,12 +1851,11 @@
config.setIpAssignment(IpAssignment.DHCP);
config.setProxySettings(ProxySettings.NONE);
- if (mNetworkIds.containsKey(configKey(config))) {
+ if (mConfiguredNetworks.getByConfigKey(config.configKey()) != null) {
// That SSID is already known, just ignore this duplicate entry
if (showNetworks) localLog("discarded duplicate network ", config.networkId);
- } else if(config.isValid()){
+ } else if(WifiServiceImpl.isValid(config)){
mConfiguredNetworks.put(config.networkId, config);
- mNetworkIds.put(configKey(config), config.networkId);
if (showNetworks) localLog("loaded configured network", config.networkId);
} else {
if (showNetworks) log("Ignoring loaded configured for network " + config.networkId
@@ -1490,40 +1866,48 @@
done = (lines.length == 1);
}
+ readPasspointConfig();
readIpAndProxyConfigurations();
readNetworkHistory();
readAutoJoinConfig();
+ buildPnoList();
+
sendConfiguredNetworksChangedBroadcast();
- if (showNetworks) localLog("loadConfiguredNetworks loaded " + mNetworkIds.size() + " networks");
+ if (showNetworks) localLog("loadConfiguredNetworks loaded " + mConfiguredNetworks.size() + " networks");
- if (mNetworkIds.size() == 0) {
- // no networks? Lets log if the wpa_supplicant.conf file contents
- BufferedReader reader = null;
+ if (mConfiguredNetworks.isEmpty()) {
+ // no networks? Lets log if the file contents
+ logKernelTime();
+ logContents(SUPPLICANT_CONFIG_FILE);
+ logContents(SUPPLICANT_CONFIG_FILE_BACKUP);
+ logContents(networkHistoryConfigFile);
+ }
+ }
+
+ private void logContents(String file) {
+ localLog("--- Begin " + file + " ---", true);
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new FileReader(file));
+ for (String line = reader.readLine(); line != null; line = reader.readLine()) {
+ localLog(line, true);
+ }
+ } catch (FileNotFoundException e) {
+ localLog("Could not open " + file + ", " + e, true);
+ } catch (IOException e) {
+ localLog("Could not read " + file + ", " + e, true);
+ } finally {
try {
- reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE));
- if (DBG) {
- localLog("--- Begin wpa_supplicant.conf Contents ---", true);
- for (String line = reader.readLine(); line != null; line = reader.readLine()) {
- localLog(line, true);
- }
- localLog("--- End wpa_supplicant.conf Contents ---", true);
+ if (reader != null) {
+ reader.close();
}
- } catch (FileNotFoundException e) {
- localLog("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e, true);
} catch (IOException e) {
- localLog("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e, true);
- } finally {
- try {
- if (reader != null) {
- reader.close();
- }
- } catch (IOException e) {
- // Just ignore the fact that we couldn't close
- }
+ // Just ignore the fact that we couldn't close
}
}
+ localLog("--- End " + file + " Contents ---", true);
}
private Map<String, String> readNetworkVariablesFromSupplicantFile(String key) {
@@ -1627,6 +2011,37 @@
return false;
}
+ void readPasspointConfig() {
+
+ List<HomeSP> homeSPs;
+ try {
+ homeSPs = mMOManager.loadAllSPs();
+ } catch (IOException e) {
+ loge("Could not read " + PPS_FILE + " : " + e);
+ return;
+ }
+
+ mConfiguredNetworks.populatePasspointData(homeSPs, mWifiNative);
+ }
+
+ public void writePasspointConfigs(final String fqdn, final HomeSP homeSP) {
+ mWriter.write(PPS_FILE, new DelayedDiskWrite.Writer() {
+ @Override
+ public void onWriteCalled(DataOutputStream out) throws IOException {
+ try {
+ if (homeSP != null) {
+ mMOManager.addSP(homeSP);
+ }
+ else {
+ mMOManager.removeSP(fqdn);
+ }
+ } catch (IOException e) {
+ loge("Could not write " + PPS_FILE + " : " + e);
+ }
+ }
+ }, false);
+ }
+
public void writeKnownNetworkHistory(boolean force) {
boolean needUpdate = force;
@@ -1675,7 +2090,7 @@
+ " nid:" + Integer.toString(config.networkId));
}
- if (config.isValid() == false)
+ if (!WifiServiceImpl.isValid(config))
continue;
if (config.SSID == null) {
@@ -1687,133 +2102,142 @@
if (VDBG) {
loge("writeKnownNetworkHistory write config " + config.configKey());
}
- out.writeUTF(CONFIG_KEY + config.configKey() + SEPARATOR_KEY);
+ out.writeUTF(CONFIG_KEY + SEPARATOR + config.configKey() + NL);
- out.writeUTF(SSID_KEY + config.SSID + SEPARATOR_KEY);
- out.writeUTF(FQDN_KEY + config.FQDN + SEPARATOR_KEY);
-
- out.writeUTF(PRIORITY_KEY + Integer.toString(config.priority) + SEPARATOR_KEY);
- out.writeUTF(STATUS_KEY + Integer.toString(config.autoJoinStatus)
- + SEPARATOR_KEY);
- out.writeUTF(SUPPLICANT_STATUS_KEY + Integer.toString(config.status)
- + SEPARATOR_KEY);
- out.writeUTF(SUPPLICANT_DISABLE_REASON_KEY
- + Integer.toString(config.disableReason)
- + SEPARATOR_KEY);
- out.writeUTF(NETWORK_ID_KEY + Integer.toString(config.networkId)
- + SEPARATOR_KEY);
- out.writeUTF(SELF_ADDED_KEY + Boolean.toString(config.selfAdded)
- + SEPARATOR_KEY);
- out.writeUTF(DID_SELF_ADD_KEY + Boolean.toString(config.didSelfAdd)
- + SEPARATOR_KEY);
- out.writeUTF(NO_INTERNET_ACCESS_REPORTS_KEY
- + Integer.toString(config.numNoInternetAccessReports)
- + SEPARATOR_KEY);
- out.writeUTF(VALIDATED_INTERNET_ACCESS_KEY
- + Boolean.toString(config.validatedInternetAccess)
- + SEPARATOR_KEY);
- out.writeUTF(EPHEMERAL_KEY
- + Boolean.toString(config.ephemeral)
- + SEPARATOR_KEY);
- if (config.peerWifiConfiguration != null) {
- out.writeUTF(PEER_CONFIGURATION_KEY + config.peerWifiConfiguration
- + SEPARATOR_KEY);
+ if (config.SSID != null) {
+ out.writeUTF(SSID_KEY + SEPARATOR + config.SSID + NL);
}
- out.writeUTF(NUM_CONNECTION_FAILURES_KEY
- + Integer.toString(config.numConnectionFailures)
- + SEPARATOR_KEY);
- out.writeUTF(NUM_AUTH_FAILURES_KEY
- + Integer.toString(config.numAuthFailures)
- + SEPARATOR_KEY);
- out.writeUTF(NUM_IP_CONFIG_FAILURES_KEY
- + Integer.toString(config.numIpConfigFailures)
- + SEPARATOR_KEY);
- out.writeUTF(SCORER_OVERRIDE_KEY + Integer.toString(config.numScorerOverride)
- + SEPARATOR_KEY);
- out.writeUTF(SCORER_OVERRIDE_AND_SWITCH_KEY
- + Integer.toString(config.numScorerOverrideAndSwitchedNetwork)
- + SEPARATOR_KEY);
- out.writeUTF(NUM_ASSOCIATION_KEY
- + Integer.toString(config.numAssociation)
- + SEPARATOR_KEY);
- out.writeUTF(JOIN_ATTEMPT_BOOST_KEY
- + Integer.toString(config.autoJoinUseAggressiveJoinAttemptThreshold)
- + SEPARATOR_KEY);
- //out.writeUTF(BLACKLIST_MILLI_KEY + Long.toString(config.blackListTimestamp)
- // + SEPARATOR_KEY);
- out.writeUTF(CREATOR_UID_KEY + Integer.toString(config.creatorUid)
- + SEPARATOR_KEY);
- out.writeUTF(CONNECT_UID_KEY + Integer.toString(config.lastConnectUid)
- + SEPARATOR_KEY);
- out.writeUTF(UPDATE_UID_KEY + Integer.toString(config.lastUpdateUid)
- + SEPARATOR_KEY);
+ if (config.FQDN != null) {
+ out.writeUTF(FQDN_KEY + SEPARATOR + config.FQDN + NL);
+ }
+
+ out.writeUTF(PRIORITY_KEY + SEPARATOR +
+ Integer.toString(config.priority) + NL);
+ out.writeUTF(STATUS_KEY + SEPARATOR +
+ Integer.toString(config.autoJoinStatus) + NL);
+ out.writeUTF(SUPPLICANT_STATUS_KEY + SEPARATOR +
+ Integer.toString(config.status) + NL);
+ out.writeUTF(SUPPLICANT_DISABLE_REASON_KEY + SEPARATOR +
+ Integer.toString(config.disableReason) + NL);
+ out.writeUTF(NETWORK_ID_KEY + SEPARATOR +
+ Integer.toString(config.networkId) + NL);
+ out.writeUTF(SELF_ADDED_KEY + SEPARATOR +
+ Boolean.toString(config.selfAdded) + NL);
+ out.writeUTF(DID_SELF_ADD_KEY + SEPARATOR +
+ Boolean.toString(config.didSelfAdd) + NL);
+ out.writeUTF(NO_INTERNET_ACCESS_REPORTS_KEY + SEPARATOR +
+ Integer.toString(config.numNoInternetAccessReports) + NL);
+ out.writeUTF(VALIDATED_INTERNET_ACCESS_KEY + SEPARATOR +
+ Boolean.toString(config.validatedInternetAccess) + NL);
+ out.writeUTF(EPHEMERAL_KEY + SEPARATOR +
+ Boolean.toString(config.ephemeral) + NL);
+ if (config.creationTime != null) {
+ out.writeUTF(CREATION_TIME_KEY + SEPARATOR + config.creationTime + NL);
+ }
+ if (config.updateTime != null) {
+ out.writeUTF(UPDATE_TIME_KEY + SEPARATOR + config.updateTime + NL);
+ }
+ if (config.peerWifiConfiguration != null) {
+ out.writeUTF(PEER_CONFIGURATION_KEY + SEPARATOR +
+ config.peerWifiConfiguration + NL);
+ }
+ out.writeUTF(NUM_CONNECTION_FAILURES_KEY + SEPARATOR +
+ Integer.toString(config.numConnectionFailures) + NL);
+ out.writeUTF(NUM_AUTH_FAILURES_KEY + SEPARATOR +
+ Integer.toString(config.numAuthFailures) + NL);
+ out.writeUTF(NUM_IP_CONFIG_FAILURES_KEY + SEPARATOR +
+ Integer.toString(config.numIpConfigFailures) + NL);
+ out.writeUTF(SCORER_OVERRIDE_KEY + SEPARATOR +
+ Integer.toString(config.numScorerOverride) + NL);
+ out.writeUTF(SCORER_OVERRIDE_AND_SWITCH_KEY + SEPARATOR +
+ Integer.toString(config.numScorerOverrideAndSwitchedNetwork) + NL);
+ out.writeUTF(NUM_ASSOCIATION_KEY + SEPARATOR +
+ Integer.toString(config.numAssociation) + NL);
+ out.writeUTF(JOIN_ATTEMPT_BOOST_KEY + SEPARATOR +
+ Integer.toString(config.autoJoinUseAggressiveJoinAttemptThreshold)+ NL);
+ //out.writeUTF(BLACKLIST_MILLI_KEY + SEPARATOR +
+ // Long.toString(config.blackListTimestamp) + NL);
+ out.writeUTF(CREATOR_UID_KEY + SEPARATOR +
+ Integer.toString(config.creatorUid) + NL);
+ out.writeUTF(CONNECT_UID_KEY + SEPARATOR +
+ Integer.toString(config.lastConnectUid) + NL);
+ out.writeUTF(UPDATE_UID_KEY + SEPARATOR +
+ Integer.toString(config.lastUpdateUid) + NL);
+ out.writeUTF(CREATOR_NAME_KEY + SEPARATOR +
+ config.creatorName + NL);
+ out.writeUTF(UPDATE_NAME_KEY + SEPARATOR +
+ config.lastUpdateName + NL);
+ out.writeUTF(USER_APPROVED_KEY + SEPARATOR +
+ Integer.toString(config.userApproved) + NL);
String allowedKeyManagementString =
makeString(config.allowedKeyManagement,
WifiConfiguration.KeyMgmt.strings);
- out.writeUTF(AUTH_KEY + allowedKeyManagementString + SEPARATOR_KEY);
+ out.writeUTF(AUTH_KEY + SEPARATOR +
+ allowedKeyManagementString + NL);
if (config.connectChoices != null) {
for (String key : config.connectChoices.keySet()) {
Integer choice = config.connectChoices.get(key);
- out.writeUTF(CHOICE_KEY + key + "="
- + choice.toString() + SEPARATOR_KEY);
+ out.writeUTF(CHOICE_KEY + SEPARATOR +
+ key + "=" + choice.toString() + NL);
}
}
if (config.linkedConfigurations != null) {
- loge("writeKnownNetworkHistory write linked "
+ log("writeKnownNetworkHistory write linked "
+ config.linkedConfigurations.size());
for (String key : config.linkedConfigurations.keySet()) {
- out.writeUTF(LINK_KEY + key + SEPARATOR_KEY);
+ out.writeUTF(LINK_KEY + SEPARATOR + key + NL);
}
}
String macAddress = config.defaultGwMacAddress;
if (macAddress != null) {
- out.writeUTF(DEFAULT_GW_KEY + macAddress + SEPARATOR_KEY);
+ out.writeUTF(DEFAULT_GW_KEY + SEPARATOR + macAddress + NL);
}
- if (config.scanResultCache != null) {
- for (ScanResult result : config.scanResultCache.values()) {
- out.writeUTF(BSSID_KEY + result.BSSID + SEPARATOR_KEY);
+ if (getScanDetailCache(config) != null) {
+ for (ScanDetail scanDetail : getScanDetailCache(config).values()) {
+ ScanResult result = scanDetail.getScanResult();
+ out.writeUTF(BSSID_KEY + SEPARATOR +
+ result.BSSID + NL);
- out.writeUTF(FREQ_KEY + Integer.toString(result.frequency)
- + SEPARATOR_KEY);
+ out.writeUTF(FREQ_KEY + SEPARATOR +
+ Integer.toString(result.frequency) + NL);
- out.writeUTF(RSSI_KEY + Integer.toString(result.level)
- + SEPARATOR_KEY);
+ out.writeUTF(RSSI_KEY + SEPARATOR +
+ Integer.toString(result.level) + NL);
- out.writeUTF(BSSID_STATUS_KEY
- + Integer.toString(result.autoJoinStatus)
- + SEPARATOR_KEY);
+ out.writeUTF(BSSID_STATUS_KEY + SEPARATOR +
+ Integer.toString(result.autoJoinStatus) + NL);
//if (result.seen != 0) {
- // out.writeUTF(MILLI_KEY + Long.toString(result.seen)
- // + SEPARATOR_KEY);
+ // out.writeUTF(MILLI_KEY + SEPARATOR + Long.toString(result.seen)
+ // + NL);
//}
- out.writeUTF(BSSID_KEY_END + SEPARATOR_KEY);
+ out.writeUTF(BSSID_KEY_END + NL);
}
}
if (config.lastFailure != null) {
- out.writeUTF(FAILURE_KEY + config.lastFailure + SEPARATOR_KEY);
+ out.writeUTF(FAILURE_KEY + SEPARATOR + config.lastFailure + NL);
}
- out.writeUTF(SEPARATOR_KEY);
+ out.writeUTF(NL);
// Add extra blank lines for clarity
- out.writeUTF(SEPARATOR_KEY);
- out.writeUTF(SEPARATOR_KEY);
+ out.writeUTF(NL);
+ out.writeUTF(NL);
}
if (mDeletedSSIDs != null && mDeletedSSIDs.size() > 0) {
for (Long i : mDeletedSSIDs) {
out.writeUTF(DELETED_CRC32_KEY);
out.writeUTF(String.valueOf(i));
- out.writeUTF(SEPARATOR_KEY);
+ out.writeUTF(NL);
}
}
if (mDeletedEphemeralSSIDs != null && mDeletedEphemeralSSIDs.size() > 0) {
for (String ssid : mDeletedEphemeralSSIDs) {
out.writeUTF(DELETED_EPHEMERAL_KEY);
out.writeUTF(ssid);
- out.writeUTF(SEPARATOR_KEY);
+ out.writeUTF(NL);
}
}
}
@@ -1857,720 +2281,279 @@
if (showNetworks) {
localLog("readNetworkHistory() path:" + networkHistoryConfigFile);
}
- DataInputStream in = null;
- try {
- in = new DataInputStream(new BufferedInputStream(new FileInputStream(
- networkHistoryConfigFile)));
+
+ try (DataInputStream in =
+ new DataInputStream(new BufferedInputStream(
+ new FileInputStream(networkHistoryConfigFile)))) {
+
+ String bssid = null;
+ String ssid = null;
+
+ int freq = 0;
+ int status = 0;
+ long seen = 0;
+ int rssi = WifiConfiguration.INVALID_RSSI;
+ String caps = null;
+
WifiConfiguration config = null;
while (true) {
- int id = -1;
- String key = in.readUTF();
- String bssid = null;
- String ssid = null;
+ String line = in.readUTF();
+ if (line == null) {
+ break;
+ }
+ int colon = line.indexOf(':');
+ if (colon < 0) {
+ continue;
+ }
- int freq = 0;
- int status = 0;
- long seen = 0;
- int rssi = WifiConfiguration.INVALID_RSSI;
- String caps = null;
- if (key.startsWith(CONFIG_KEY)) {
+ String key = line.substring(0, colon).trim();
+ String value = line.substring(colon + 1).trim();
- if (config != null) {
- config = null;
- }
- String configKey = key.replace(CONFIG_KEY, "");
- configKey = configKey.replace(SEPARATOR_KEY, "");
- // get the networkId for that config Key
- Integer n = mNetworkIds.get(configKey.hashCode());
+ if (key.equals(CONFIG_KEY)) {
+
+ config = mConfiguredNetworks.getByConfigKey(value);
+
// skip reading that configuration data
// since we don't have a corresponding network ID
- if (n == null) {
- localLog("readNetworkHistory didnt find netid for hash="
- + Integer.toString(configKey.hashCode())
- + " key: " + configKey);
- continue;
- }
- config = mConfiguredNetworks.get(n);
if (config == null) {
- localLog("readNetworkHistory didnt find config for netid="
- + n.toString()
- + " key: " + configKey);
- }
- status = 0;
- ssid = null;
- bssid = null;
- freq = 0;
- seen = 0;
- rssi = WifiConfiguration.INVALID_RSSI;
- caps = null;
+ localLog("readNetworkHistory didnt find netid for hash="
+ + Integer.toString(value.hashCode())
+ + " key: " + value);
+ mLostConfigsDbg.add(value);
+ continue;
+ } else {
+ // After an upgrade count old connections as owned by system
+ if (config.creatorName == null || config.lastUpdateName == null) {
+ config.creatorName =
+ mContext.getPackageManager().getNameForUid(Process.SYSTEM_UID);
+ config.lastUpdateName = config.creatorName;
+ if (DBG) Log.w(TAG, "Upgrading network " + config.networkId
+ + " to " + config.creatorName);
+ }
+ }
} else if (config != null) {
- if (key.startsWith(SSID_KEY)) {
- ssid = key.replace(SSID_KEY, "");
- ssid = ssid.replace(SEPARATOR_KEY, "");
- if (config.SSID != null && !config.SSID.equals(ssid)) {
- loge("Error parsing network history file, mismatched SSIDs");
- config = null; //error
- ssid = null;
- } else {
- config.SSID = ssid;
- }
- }
-
- if (key.startsWith(FQDN_KEY)) {
- String fqdn = key.replace(FQDN_KEY, "");
- fqdn = fqdn.replace(SEPARATOR_KEY, "");
- config.FQDN = fqdn;
- }
-
- if (key.startsWith(DEFAULT_GW_KEY)) {
- String gateway = key.replace(DEFAULT_GW_KEY, "");
- gateway = gateway.replace(SEPARATOR_KEY, "");
- config.defaultGwMacAddress = gateway;
- }
-
- if (key.startsWith(STATUS_KEY)) {
- String st = key.replace(STATUS_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- config.autoJoinStatus = Integer.parseInt(st);
- }
-
- if (key.startsWith(SUPPLICANT_DISABLE_REASON_KEY)) {
- String reason = key.replace(SUPPLICANT_DISABLE_REASON_KEY, "");
- reason = reason.replace(SEPARATOR_KEY, "");
- config.disableReason = Integer.parseInt(reason);
- }
-
- if (key.startsWith(SELF_ADDED_KEY)) {
- String selfAdded = key.replace(SELF_ADDED_KEY, "");
- selfAdded = selfAdded.replace(SEPARATOR_KEY, "");
- config.selfAdded = Boolean.parseBoolean(selfAdded);
- }
-
- if (key.startsWith(DID_SELF_ADD_KEY)) {
- String didSelfAdd = key.replace(DID_SELF_ADD_KEY, "");
- didSelfAdd = didSelfAdd.replace(SEPARATOR_KEY, "");
- config.didSelfAdd = Boolean.parseBoolean(didSelfAdd);
- }
-
- if (key.startsWith(NO_INTERNET_ACCESS_REPORTS_KEY)) {
- String access = key.replace(NO_INTERNET_ACCESS_REPORTS_KEY, "");
- access = access.replace(SEPARATOR_KEY, "");
- config.numNoInternetAccessReports = Integer.parseInt(access);
- }
-
- if (key.startsWith(VALIDATED_INTERNET_ACCESS_KEY)) {
- String access = key.replace(VALIDATED_INTERNET_ACCESS_KEY, "");
- access = access.replace(SEPARATOR_KEY, "");
- config.validatedInternetAccess = Boolean.parseBoolean(access);
- }
-
- if (key.startsWith(EPHEMERAL_KEY)) {
- String access = key.replace(EPHEMERAL_KEY, "");
- access = access.replace(SEPARATOR_KEY, "");
- config.ephemeral = Boolean.parseBoolean(access);
- }
-
- if (key.startsWith(CREATOR_UID_KEY)) {
- String uid = key.replace(CREATOR_UID_KEY, "");
- uid = uid.replace(SEPARATOR_KEY, "");
- config.creatorUid = Integer.parseInt(uid);
- }
-
- if (key.startsWith(BLACKLIST_MILLI_KEY)) {
- String milli = key.replace(BLACKLIST_MILLI_KEY, "");
- milli = milli.replace(SEPARATOR_KEY, "");
- config.blackListTimestamp = Long.parseLong(milli);
- }
-
- if (key.startsWith(NUM_CONNECTION_FAILURES_KEY)) {
- String num = key.replace(NUM_CONNECTION_FAILURES_KEY, "");
- num = num.replace(SEPARATOR_KEY, "");
- config.numConnectionFailures = Integer.parseInt(num);
- }
-
- if (key.startsWith(NUM_IP_CONFIG_FAILURES_KEY)) {
- String num = key.replace(NUM_IP_CONFIG_FAILURES_KEY, "");
- num = num.replace(SEPARATOR_KEY, "");
- config.numIpConfigFailures = Integer.parseInt(num);
- }
-
- if (key.startsWith(NUM_AUTH_FAILURES_KEY)) {
- String num = key.replace(NUM_AUTH_FAILURES_KEY, "");
- num = num.replace(SEPARATOR_KEY, "");
- config.numIpConfigFailures = Integer.parseInt(num);
- }
-
- if (key.startsWith(SCORER_OVERRIDE_KEY)) {
- String num = key.replace(SCORER_OVERRIDE_KEY, "");
- num = num.replace(SEPARATOR_KEY, "");
- config.numScorerOverride = Integer.parseInt(num);
- }
-
- if (key.startsWith(SCORER_OVERRIDE_AND_SWITCH_KEY)) {
- String num = key.replace(SCORER_OVERRIDE_AND_SWITCH_KEY, "");
- num = num.replace(SEPARATOR_KEY, "");
- config.numScorerOverrideAndSwitchedNetwork = Integer.parseInt(num);
- }
-
- if (key.startsWith(NUM_ASSOCIATION_KEY)) {
- String num = key.replace(NUM_ASSOCIATION_KEY, "");
- num = num.replace(SEPARATOR_KEY, "");
- config.numAssociation = Integer.parseInt(num);
- }
-
- if (key.startsWith(JOIN_ATTEMPT_BOOST_KEY)) {
- String num = key.replace(JOIN_ATTEMPT_BOOST_KEY, "");
- num = num.replace(SEPARATOR_KEY, "");
- config.autoJoinUseAggressiveJoinAttemptThreshold = Integer.parseInt(num);
- }
-
- if (key.startsWith(CONNECT_UID_KEY)) {
- String uid = key.replace(CONNECT_UID_KEY, "");
- uid = uid.replace(SEPARATOR_KEY, "");
- config.lastConnectUid = Integer.parseInt(uid);
- }
-
- if (key.startsWith(UPDATE_UID_KEY)) {
- String uid = key.replace(UPDATE_UID_KEY, "");
- uid = uid.replace(SEPARATOR_KEY, "");
- config.lastUpdateUid = Integer.parseInt(uid);
- }
-
- if (key.startsWith(FAILURE_KEY)) {
- config.lastFailure = key.replace(FAILURE_KEY, "");
- config.lastFailure = config.lastFailure.replace(SEPARATOR_KEY, "");
- }
-
- if (key.startsWith(PEER_CONFIGURATION_KEY)) {
- config.peerWifiConfiguration = key.replace(PEER_CONFIGURATION_KEY, "");
- config.peerWifiConfiguration =
- config.peerWifiConfiguration.replace(SEPARATOR_KEY, "");
- }
-
- if (key.startsWith(CHOICE_KEY)) {
- String choiceStr = key.replace(CHOICE_KEY, "");
- choiceStr = choiceStr.replace(SEPARATOR_KEY, "");
- String configKey = "";
- int choice = 0;
- Matcher match = mConnectChoice.matcher(choiceStr);
- if (!match.find()) {
- if (DBG) Log.d(TAG, "WifiConfigStore: connectChoice: " +
- " Couldnt match pattern : " + choiceStr);
- } else {
- configKey = match.group(1);
- try {
- choice = Integer.parseInt(match.group(2));
- } catch (NumberFormatException e) {
- choice = 0;
+ switch (key) {
+ case SSID_KEY:
+ if (config.isPasspoint()) {
+ break;
}
- if (choice > 0) {
- if (config.connectChoices == null) {
- config.connectChoices = new HashMap<String, Integer>();
+ ssid = value;
+ if (config.SSID != null && !config.SSID.equals(ssid)) {
+ loge("Error parsing network history file, mismatched SSIDs");
+ config = null; //error
+ ssid = null;
+ } else {
+ config.SSID = ssid;
+ }
+ break;
+ case FQDN_KEY:
+ // Check for literal 'null' to be backwards compatible.
+ config.FQDN = value.equals("null") ? null : value;
+ break;
+ case DEFAULT_GW_KEY:
+ config.defaultGwMacAddress = value;
+ break;
+ case STATUS_KEY:
+ config.autoJoinStatus = Integer.parseInt(value);
+ break;
+ case SUPPLICANT_DISABLE_REASON_KEY:
+ config.disableReason = Integer.parseInt(value);
+ break;
+ case SELF_ADDED_KEY:
+ config.selfAdded = Boolean.parseBoolean(value);
+ break;
+ case DID_SELF_ADD_KEY:
+ config.didSelfAdd = Boolean.parseBoolean(value);
+ break;
+ case NO_INTERNET_ACCESS_REPORTS_KEY:
+ config.numNoInternetAccessReports = Integer.parseInt(value);
+ break;
+ case VALIDATED_INTERNET_ACCESS_KEY:
+ config.validatedInternetAccess = Boolean.parseBoolean(value);
+ break;
+ case CREATION_TIME_KEY:
+ config.creationTime = value;
+ break;
+ case UPDATE_TIME_KEY:
+ config.updateTime = value;
+ break;
+ case EPHEMERAL_KEY:
+ config.ephemeral = Boolean.parseBoolean(value);
+ break;
+ case CREATOR_UID_KEY:
+ config.creatorUid = Integer.parseInt(value);
+ break;
+ case BLACKLIST_MILLI_KEY:
+ config.blackListTimestamp = Long.parseLong(value);
+ break;
+ case NUM_CONNECTION_FAILURES_KEY:
+ config.numConnectionFailures = Integer.parseInt(value);
+ break;
+ case NUM_IP_CONFIG_FAILURES_KEY:
+ config.numIpConfigFailures = Integer.parseInt(value);
+ break;
+ case NUM_AUTH_FAILURES_KEY:
+ config.numIpConfigFailures = Integer.parseInt(value);
+ break;
+ case SCORER_OVERRIDE_KEY:
+ config.numScorerOverride = Integer.parseInt(value);
+ break;
+ case SCORER_OVERRIDE_AND_SWITCH_KEY:
+ config.numScorerOverrideAndSwitchedNetwork = Integer.parseInt(value);
+ break;
+ case NUM_ASSOCIATION_KEY:
+ config.numAssociation = Integer.parseInt(value);
+ break;
+ case JOIN_ATTEMPT_BOOST_KEY:
+ config.autoJoinUseAggressiveJoinAttemptThreshold =
+ Integer.parseInt(value);
+ break;
+ case CONNECT_UID_KEY:
+ config.lastConnectUid = Integer.parseInt(value);
+ break;
+ case UPDATE_UID_KEY:
+ config.lastUpdateUid = Integer.parseInt(value);
+ break;
+ case FAILURE_KEY:
+ config.lastFailure = value;
+ break;
+ case PEER_CONFIGURATION_KEY:
+ config.peerWifiConfiguration = value;
+ break;
+ case CHOICE_KEY:
+ String configKey = "";
+ int choice = 0;
+ Matcher match = mConnectChoice.matcher(value);
+ if (!match.find()) {
+ if (DBG) Log.d(TAG, "WifiConfigStore: connectChoice: " +
+ " Couldnt match pattern : " + value);
+ } else {
+ configKey = match.group(1);
+ try {
+ choice = Integer.parseInt(match.group(2));
+ } catch (NumberFormatException e) {
+ choice = 0;
}
- config.connectChoices.put(configKey, choice);
+ if (choice > 0) {
+ if (config.connectChoices == null) {
+ config.connectChoices = new HashMap<>();
+ }
+ config.connectChoices.put(configKey, choice);
+ }
}
- }
- }
-
- if (key.startsWith(LINK_KEY)) {
- String configKey = key.replace(LINK_KEY, "");
- configKey = configKey.replace(SEPARATOR_KEY, "");
- if (config.linkedConfigurations == null) {
- config.linkedConfigurations = new HashMap<String, Integer>();
- }
- if (config.linkedConfigurations != null) {
- config.linkedConfigurations.put(configKey, -1);
- }
- }
-
- if (key.startsWith(BSSID_KEY)) {
- if (key.startsWith(BSSID_KEY)) {
- bssid = key.replace(BSSID_KEY, "");
- bssid = bssid.replace(SEPARATOR_KEY, "");
+ break;
+ case LINK_KEY:
+ if (config.linkedConfigurations == null) {
+ config.linkedConfigurations = new HashMap<>();
+ }
+ else {
+ config.linkedConfigurations.put(value, -1);
+ }
+ break;
+ case BSSID_KEY:
+ status = 0;
+ ssid = null;
+ bssid = null;
freq = 0;
seen = 0;
rssi = WifiConfiguration.INVALID_RSSI;
caps = "";
- status = 0;
- }
-
- if (key.startsWith(RSSI_KEY)) {
- String lvl = key.replace(RSSI_KEY, "");
- lvl = lvl.replace(SEPARATOR_KEY, "");
- rssi = Integer.parseInt(lvl);
- }
-
- if (key.startsWith(BSSID_STATUS_KEY)) {
- String st = key.replace(BSSID_STATUS_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- status = Integer.parseInt(st);
- }
-
- if (key.startsWith(FREQ_KEY)) {
- String channel = key.replace(FREQ_KEY, "");
- channel = channel.replace(SEPARATOR_KEY, "");
- freq = Integer.parseInt(channel);
- }
-
- if (key.startsWith(DATE_KEY)) {
- /*
- * when reading the configuration from file we don't update the date
- * so as to avoid reading back stale or non-sensical data that would
- * depend on network time.
- * The date of a WifiConfiguration should only come from actual scan result.
- *
- String s = key.replace(FREQ_KEY, "");
- seen = Integer.getInteger(s);
- */
- }
-
- if (key.startsWith(BSSID_KEY_END)) {
+ break;
+ case RSSI_KEY:
+ rssi = Integer.parseInt(value);
+ break;
+ case BSSID_STATUS_KEY:
+ status = Integer.parseInt(value);
+ break;
+ case FREQ_KEY:
+ freq = Integer.parseInt(value);
+ break;
+ case DATE_KEY:
+ /*
+ * when reading the configuration from file we don't update the date
+ * so as to avoid reading back stale or non-sensical data that would
+ * depend on network time.
+ * The date of a WifiConfiguration should only come from actual scan result.
+ *
+ String s = key.replace(FREQ_KEY, "");
+ seen = Integer.getInteger(s);
+ */
+ break;
+ case BSSID_KEY_END:
if ((bssid != null) && (ssid != null)) {
- if (config.scanResultCache == null) {
- config.scanResultCache = new HashMap<String, ScanResult>();
+ if (getScanDetailCache(config) != null) {
+ WifiSsid wssid = WifiSsid.createFromAsciiEncoded(ssid);
+ ScanDetail scanDetail = new ScanDetail(wssid, bssid,
+ caps, rssi, freq, (long) 0, seen);
+ getScanDetailCache(config).put(scanDetail);
+ scanDetail.getScanResult().autoJoinStatus = status;
}
- WifiSsid wssid = WifiSsid.createFromAsciiEncoded(ssid);
- ScanResult result = new ScanResult(wssid, bssid,
- caps, rssi, freq, (long) 0);
- result.seen = seen;
- config.scanResultCache.put(bssid, result);
- result.autoJoinStatus = status;
}
- }
-
- if (key.startsWith(DELETED_CRC32_KEY)) {
- String crc = key.replace(DELETED_CRC32_KEY, "");
- Long c = Long.parseLong(crc);
- mDeletedSSIDs.add(c);
- }
- if (key.startsWith(DELETED_EPHEMERAL_KEY)) {
- String s = key.replace(DELETED_EPHEMERAL_KEY, "");
- if (!TextUtils.isEmpty(s)) {
- s = s.replace(SEPARATOR_KEY, "");
- mDeletedEphemeralSSIDs.add(s);
+ break;
+ case DELETED_CRC32_KEY:
+ mDeletedSSIDs.add(Long.parseLong(value));
+ break;
+ case DELETED_EPHEMERAL_KEY:
+ if (!TextUtils.isEmpty(value)) {
+ mDeletedEphemeralSSIDs.add(value);
}
- }
+ break;
+ case CREATOR_NAME_KEY:
+ config.creatorName = value;
+ break;
+ case UPDATE_NAME_KEY:
+ config.lastUpdateName = value;
+ break;
+ case USER_APPROVED_KEY:
+ config.userApproved = Integer.parseInt(value);
+ break;
}
}
}
- } catch (EOFException ignore) {
- if (in != null) {
- try {
- in.close();
- } catch (Exception e) {
- loge("readNetworkHistory: Error reading file" + e);
- }
- }
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "readNetworkHistory: failed to read, revert to default, " + e, e);
+ } catch (EOFException e) {
+ // do nothing
} catch (IOException e) {
- loge("readNetworkHistory: No config file, revert to default" + e);
- }
-
- if(in!=null) {
- try {
- in.close();
- } catch (Exception e) {
- loge("readNetworkHistory: Error closing file" + e);
- }
+ Log.e(TAG, "readNetworkHistory: No config file, revert to default, " + e, e);
}
}
private void readAutoJoinConfig() {
- BufferedReader reader = null;
- try {
-
- reader = new BufferedReader(new FileReader(autoJoinConfigFile));
-
+ try (BufferedReader reader = new BufferedReader(new FileReader(autoJoinConfigFile))) {
for (String key = reader.readLine(); key != null; key = reader.readLine()) {
- if (key != null) {
- Log.d(TAG, "readAutoJoinConfig line: " + key);
- }
- if (key.startsWith(ENABLE_AUTO_JOIN_WHILE_ASSOCIATED_KEY)) {
- String st = key.replace(ENABLE_AUTO_JOIN_WHILE_ASSOCIATED_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- enableAutoJoinWhenAssociated = Integer.parseInt(st) != 0;
- Log.d(TAG,"readAutoJoinConfig: enabled = " + enableAutoJoinWhenAssociated);
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
+ Log.d(TAG, "readAutoJoinConfig line: " + key);
+
+ int split = key.indexOf(':');
+ if (split < 0) {
+ continue;
}
- if (key.startsWith(ENABLE_FULL_BAND_SCAN_WHEN_ASSOCIATED_KEY)) {
- String st = key.replace(ENABLE_FULL_BAND_SCAN_WHEN_ASSOCIATED_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- enableFullBandScanWhenAssociated = Integer.parseInt(st) != 0;
- Log.d(TAG,"readAutoJoinConfig: enableFullBandScanWhenAssociated = "
- + enableFullBandScanWhenAssociated);
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
+ String name = key.substring(0, split);
+ Object reference = sKeyMap.get(name);
+ if (reference == null) {
+ continue;
}
- if (key.startsWith(ENABLE_AUTO_JOIN_SCAN_WHILE_ASSOCIATED_KEY)) {
- String st = key.replace(ENABLE_AUTO_JOIN_SCAN_WHILE_ASSOCIATED_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- enableAutoJoinScanWhenAssociated = Integer.parseInt(st) != 0;
- Log.d(TAG,"readAutoJoinConfig: enabled = "
- + enableAutoJoinScanWhenAssociated);
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
-
- if (key.startsWith(ENABLE_CHIP_WAKE_UP_WHILE_ASSOCIATED_KEY)) {
- String st = key.replace(ENABLE_CHIP_WAKE_UP_WHILE_ASSOCIATED_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- enableChipWakeUpWhenAssociated = Integer.parseInt(st) != 0;
- Log.d(TAG,"readAutoJoinConfig: enabled = "
- + enableChipWakeUpWhenAssociated);
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
-
- if (key.startsWith(ENABLE_RSSI_POLL_WHILE_ASSOCIATED_KEY)) {
- String st = key.replace(ENABLE_RSSI_POLL_WHILE_ASSOCIATED_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- enableRssiPollWhenAssociated = Integer.parseInt(st) != 0;
- Log.d(TAG,"readAutoJoinConfig: enabled = "
- + enableRssiPollWhenAssociated);
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
-
- if (key.startsWith(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY)) {
- String st =
- key.replace(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_5G_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- thresholdInitialAutoJoinAttemptMin5RSSI = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: thresholdInitialAutoJoinAttemptMin5RSSI = "
- + Integer.toString(thresholdInitialAutoJoinAttemptMin5RSSI));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
-
- if (key.startsWith(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY)) {
- String st =
- key.replace(THRESHOLD_INITIAL_AUTO_JOIN_ATTEMPT_RSSI_MIN_24G_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- thresholdInitialAutoJoinAttemptMin24RSSI = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: thresholdInitialAutoJoinAttemptMin24RSSI = "
- + Integer.toString(thresholdInitialAutoJoinAttemptMin24RSSI));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
-
- if (key.startsWith(THRESHOLD_UNBLACKLIST_HARD_5G_KEY)) {
- String st = key.replace(THRESHOLD_UNBLACKLIST_HARD_5G_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- thresholdUnblacklistThreshold5Hard = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold5Hard = "
- + Integer.toString(thresholdUnblacklistThreshold5Hard));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
- if (key.startsWith(THRESHOLD_UNBLACKLIST_SOFT_5G_KEY)) {
- String st = key.replace(THRESHOLD_UNBLACKLIST_SOFT_5G_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- thresholdUnblacklistThreshold5Soft = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold5Soft = "
- + Integer.toString(thresholdUnblacklistThreshold5Soft));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
- if (key.startsWith(THRESHOLD_UNBLACKLIST_HARD_24G_KEY)) {
- String st = key.replace(THRESHOLD_UNBLACKLIST_HARD_24G_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- thresholdUnblacklistThreshold24Hard = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold24Hard = "
- + Integer.toString(thresholdUnblacklistThreshold24Hard));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
- if (key.startsWith(THRESHOLD_UNBLACKLIST_SOFT_24G_KEY)) {
- String st = key.replace(THRESHOLD_UNBLACKLIST_SOFT_24G_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- thresholdUnblacklistThreshold24Soft = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: thresholdUnblacklistThreshold24Soft = "
- + Integer.toString(thresholdUnblacklistThreshold24Soft));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
-
- if (key.startsWith(THRESHOLD_GOOD_RSSI_5_KEY)) {
- String st = key.replace(THRESHOLD_GOOD_RSSI_5_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- thresholdGoodRssi5 = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: thresholdGoodRssi5 = "
- + Integer.toString(thresholdGoodRssi5));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
- if (key.startsWith(THRESHOLD_LOW_RSSI_5_KEY)) {
- String st = key.replace(THRESHOLD_LOW_RSSI_5_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- thresholdLowRssi5 = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: thresholdLowRssi5 = "
- + Integer.toString(thresholdLowRssi5));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
- if (key.startsWith(THRESHOLD_BAD_RSSI_5_KEY)) {
- String st = key.replace(THRESHOLD_BAD_RSSI_5_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- thresholdBadRssi5 = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: thresholdBadRssi5 = "
- + Integer.toString(thresholdBadRssi5));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
-
- if (key.startsWith(THRESHOLD_GOOD_RSSI_24_KEY)) {
- String st = key.replace(THRESHOLD_GOOD_RSSI_24_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- thresholdGoodRssi24 = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: thresholdGoodRssi24 = "
- + Integer.toString(thresholdGoodRssi24));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
- if (key.startsWith(THRESHOLD_LOW_RSSI_24_KEY)) {
- String st = key.replace(THRESHOLD_LOW_RSSI_24_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- thresholdLowRssi24 = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: thresholdLowRssi24 = "
- + Integer.toString(thresholdLowRssi24));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
- if (key.startsWith(THRESHOLD_BAD_RSSI_24_KEY)) {
- String st = key.replace(THRESHOLD_BAD_RSSI_24_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- thresholdBadRssi24 = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: thresholdBadRssi24 = "
- + Integer.toString(thresholdBadRssi24));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
-
- if (key.startsWith(THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY)) {
- String st = key.replace(THRESHOLD_MAX_TX_PACKETS_FOR_NETWORK_SWITCHING_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- maxTxPacketForNetworkSwitching = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: maxTxPacketForNetworkSwitching = "
- + Integer.toString(maxTxPacketForNetworkSwitching));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
- if (key.startsWith(THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY)) {
- String st = key.replace(THRESHOLD_MAX_RX_PACKETS_FOR_NETWORK_SWITCHING_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- maxRxPacketForNetworkSwitching = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: maxRxPacketForNetworkSwitching = "
- + Integer.toString(maxRxPacketForNetworkSwitching));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
-
- if (key.startsWith(THRESHOLD_MAX_TX_PACKETS_FOR_FULL_SCANS_KEY)) {
- String st = key.replace(THRESHOLD_MAX_TX_PACKETS_FOR_FULL_SCANS_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- maxTxPacketForNetworkSwitching = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: maxTxPacketForFullScans = "
- + Integer.toString(maxTxPacketForFullScans));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
- if (key.startsWith(THRESHOLD_MAX_RX_PACKETS_FOR_FULL_SCANS_KEY)) {
- String st = key.replace(THRESHOLD_MAX_RX_PACKETS_FOR_FULL_SCANS_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- maxRxPacketForNetworkSwitching = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: maxRxPacketForFullScans = "
- + Integer.toString(maxRxPacketForFullScans));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
-
- if (key.startsWith(THRESHOLD_MAX_TX_PACKETS_FOR_PARTIAL_SCANS_KEY)) {
- String st = key.replace(THRESHOLD_MAX_TX_PACKETS_FOR_PARTIAL_SCANS_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- maxTxPacketForNetworkSwitching = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: maxTxPacketForPartialScans = "
- + Integer.toString(maxTxPacketForPartialScans));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
- if (key.startsWith(THRESHOLD_MAX_RX_PACKETS_FOR_PARTIAL_SCANS_KEY)) {
- String st = key.replace(THRESHOLD_MAX_RX_PACKETS_FOR_PARTIAL_SCANS_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- maxRxPacketForNetworkSwitching = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: maxRxPacketForPartialScans = "
- + Integer.toString(maxRxPacketForPartialScans));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
-
- if (key.startsWith(WIFI_VERBOSE_LOGS_KEY)) {
- String st = key.replace(WIFI_VERBOSE_LOGS_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- enableVerboseLogging = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: enable verbose logs = "
- + Integer.toString(enableVerboseLogging));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
- if (key.startsWith(A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY)) {
- String st = key.replace(A_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- bandPreferenceBoostThreshold5 = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: bandPreferenceBoostThreshold5 = "
- + Integer.toString(bandPreferenceBoostThreshold5));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
- if (key.startsWith(ASSOCIATED_PARTIAL_SCAN_PERIOD_KEY)) {
- String st = key.replace(ASSOCIATED_PARTIAL_SCAN_PERIOD_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- associatedPartialScanPeriodMilli = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: associatedScanPeriod = "
- + Integer.toString(associatedPartialScanPeriodMilli));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
- if (key.startsWith(ASSOCIATED_FULL_SCAN_BACKOFF_KEY)) {
- String st = key.replace(ASSOCIATED_FULL_SCAN_BACKOFF_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- associatedFullScanBackoff = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: associatedFullScanBackoff = "
- + Integer.toString(associatedFullScanBackoff));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
- if (key.startsWith(G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY)) {
- String st = key.replace(G_BAND_PREFERENCE_RSSI_THRESHOLD_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- bandPreferencePenaltyThreshold5 = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: bandPreferencePenaltyThreshold5 = "
- + Integer.toString(bandPreferencePenaltyThreshold5));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
- if (key.startsWith(ALWAYS_ENABLE_SCAN_WHILE_ASSOCIATED_KEY)) {
- String st = key.replace(ALWAYS_ENABLE_SCAN_WHILE_ASSOCIATED_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- alwaysEnableScansWhileAssociated = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: alwaysEnableScansWhileAssociated = "
- + Integer.toString(alwaysEnableScansWhileAssociated));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
- if (key.startsWith(MAX_NUM_PASSIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY)) {
- String st = key.replace(MAX_NUM_PASSIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- maxNumPassiveChannelsForPartialScans = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: maxNumPassiveChannelsForPartialScans = "
- + Integer.toString(maxNumPassiveChannelsForPartialScans));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
- if (key.startsWith(MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY)) {
- String st = key.replace(MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCANS_KEY, "");
- st = st.replace(SEPARATOR_KEY, "");
- try {
- maxNumActiveChannelsForPartialScans = Integer.parseInt(st);
- Log.d(TAG,"readAutoJoinConfig: maxNumActiveChannelsForPartialScans = "
- + Integer.toString(maxNumActiveChannelsForPartialScans));
- } catch (NumberFormatException e) {
- Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
- }
- }
- }
- } catch (EOFException ignore) {
- if (reader != null) {
try {
- reader.close();
- reader = null;
- } catch (Exception e) {
- loge("readAutoJoinStatus: Error closing file" + e);
+ int value = Integer.parseInt(key.substring(split+1).trim());
+ if (reference.getClass() == AtomicBoolean.class) {
+ ((AtomicBoolean)reference).set(value != 0);
+ }
+ else {
+ ((AtomicInteger)reference).set(value);
+ }
+ Log.d(TAG,"readAutoJoinConfig: " + name + " = " + value);
}
- }
- } catch (FileNotFoundException ignore) {
- if (reader != null) {
- try {
- reader.close();
- reader = null;
- } catch (Exception e) {
- loge("readAutoJoinStatus: Error closing file" + e);
+ catch (NumberFormatException nfe) {
+ Log.d(TAG,"readAutoJoinConfig: incorrect format :" + key);
}
}
} catch (IOException e) {
loge("readAutoJoinStatus: Error parsing configuration" + e);
}
-
- if (reader!=null) {
- try {
- reader.close();
- } catch (Exception e) {
- loge("readAutoJoinStatus: Error closing file" + e);
- }
- }
}
@@ -2595,8 +2578,8 @@
for (int i = 0; i < networks.size(); i++) {
int id = networks.keyAt(i);
- WifiConfiguration config = mConfiguredNetworks.get(mNetworkIds.get(id));
-
+ WifiConfiguration config = mConfiguredNetworks.getByConfigKeyID(id);
+ // This is the only place the map is looked up through a (dangerous) hash-value!
if (config == null || config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED ||
config.ephemeral) {
@@ -2615,9 +2598,8 @@
* and that can confuses the supplicant because it uses space charaters as delimiters
*/
- private String encodeSSID(String str){
- String tmp = removeDoubleQuotes(str);
- return String.format("%x", new BigInteger(1, tmp.getBytes(Charset.forName("UTF-8"))));
+ public static String encodeSSID(String str){
+ return Utils.toHex(removeDoubleQuotes(str).getBytes(StandardCharsets.UTF_8));
}
private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config, int uid) {
@@ -2628,27 +2610,23 @@
*/
if (VDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid());
+ if (config.isPasspoint() && !mMOManager.isEnabled()) {
+ Log.e(TAG, "Passpoint is not enabled");
+ return new NetworkUpdateResult(INVALID_NETWORK_ID);
+ }
int netId = config.networkId;
boolean newNetwork = false;
// networkId of INVALID_NETWORK_ID means we want to create a new network
if (netId == INVALID_NETWORK_ID) {
- Integer savedNetId = mNetworkIds.get(configKey(config));
- // Check if either we have a network Id or a WifiConfiguration
- // matching the one we are trying to add.
- if (savedNetId == null) {
- for (WifiConfiguration test : mConfiguredNetworks.values()) {
- if (test.configKey().equals(config.configKey())) {
- savedNetId = test.networkId;
- loge("addOrUpdateNetworkNative " + config.configKey()
- + " was found, but no network Id");
- break;
- }
- }
- }
- if (savedNetId != null) {
- netId = savedNetId;
+ WifiConfiguration savedConfig = mConfiguredNetworks.getByConfigKey(config.configKey());
+ if (savedConfig != null) {
+ netId = savedConfig.networkId;
} else {
+ if (mMOManager.getHomeSP(config.FQDN) != null) {
+ loge("addOrUpdateNetworkNative passpoint " + config.FQDN
+ + " was found, but no network Id");
+ }
newNetwork = true;
netId = mWifiNative.addNetwork();
if (netId < 0) {
@@ -2673,8 +2651,18 @@
break setVariables;
}
+ if (config.isPasspoint()) {
+ if (!mWifiNative.setNetworkVariable(
+ netId,
+ idStringVarName,
+ '"' + config.FQDN + '"')) {
+ loge("failed to set id_str: " + config.FQDN);
+ break setVariables;
+ }
+ }
+
if (config.BSSID != null) {
- loge("Setting BSSID for " + config.configKey() + " to " + config.BSSID);
+ log("Setting BSSID for " + config.configKey() + " to " + config.BSSID);
if (!mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.bssidVarName,
@@ -2862,6 +2850,11 @@
// No need to try to set an obfuscated password, which will fail
continue;
}
+ if (key.equals(WifiEnterpriseConfig.REALM_KEY)
+ || key.equals(WifiEnterpriseConfig.PLMN_KEY)) {
+ // No need to save realm or PLMN in supplicant
+ continue;
+ }
if (!mWifiNative.setNetworkVariable(
netId,
key,
@@ -2907,15 +2900,54 @@
currentConfig.lastConnectUid = config.lastConnectUid;
currentConfig.lastUpdateUid = config.lastUpdateUid;
currentConfig.creatorUid = config.creatorUid;
+ currentConfig.creatorName = config.creatorName;
+ currentConfig.lastUpdateName = config.lastUpdateName;
currentConfig.peerWifiConfiguration = config.peerWifiConfiguration;
+ currentConfig.FQDN = config.FQDN;
+ currentConfig.providerFriendlyName = config.providerFriendlyName;
+ currentConfig.roamingConsortiumIds = config.roamingConsortiumIds;
+ currentConfig.validatedInternetAccess = config.validatedInternetAccess;
+ currentConfig.numNoInternetAccessReports = config.numNoInternetAccessReports;
+ currentConfig.updateTime = config.updateTime;
+ currentConfig.creationTime = config.creationTime;
}
if (DBG) {
- loge("created new config netId=" + Integer.toString(netId)
- + " uid=" + Integer.toString(currentConfig.creatorUid));
+ log("created new config netId=" + Integer.toString(netId)
+ + " uid=" + Integer.toString(currentConfig.creatorUid)
+ + " name=" + currentConfig.creatorName);
}
}
- if (uid >= 0) {
+ /* save HomeSP object for passpoint networks */
+ HomeSP homeSP = null;
+
+ if (config.isPasspoint()) {
+ try {
+ Credential credential =
+ new Credential(config.enterpriseConfig, mKeyStore, !newNetwork);
+ HashSet<Long> roamingConsortiumIds = new HashSet<Long>();
+ for (Long roamingConsortiumId : config.roamingConsortiumIds) {
+ roamingConsortiumIds.add(roamingConsortiumId);
+ }
+
+ homeSP = new HomeSP(Collections.<String, Long>emptyMap(), config.FQDN,
+ roamingConsortiumIds, Collections.<String>emptySet(),
+ Collections.<Long>emptySet(), Collections.<Long>emptyList(),
+ config.providerFriendlyName, null, credential);
+
+ log("created a homeSP object for " + config.networkId + ":" + config.SSID);
+
+ /* fix enterprise config properties for passpoint */
+ currentConfig.enterpriseConfig.setRealm(config.enterpriseConfig.getRealm());
+ currentConfig.enterpriseConfig.setPlmn(config.enterpriseConfig.getPlmn());
+ }
+ catch (IOException ioe) {
+ Log.e(TAG, "Failed to create Passpoint config: " + ioe);
+ return new NetworkUpdateResult(INVALID_NETWORK_ID);
+ }
+ }
+
+ if (uid != WifiConfiguration.UNKNOWN_UID) {
if (newNetwork) {
currentConfig.creatorUid = uid;
} else {
@@ -2923,8 +2955,18 @@
}
}
+ // For debug, record the time the configuration was modified
+ StringBuilder sb = new StringBuilder();
+ sb.append("time=");
+ Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(System.currentTimeMillis());
+ sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
+
if (newNetwork) {
currentConfig.dirty = true;
+ currentConfig.creationTime = sb.toString();
+ } else {
+ currentConfig.updateTime = sb.toString();
}
if (currentConfig.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED) {
@@ -2934,7 +2976,7 @@
currentConfig.selfAdded = false;
currentConfig.didSelfAdd = false;
if (DBG) {
- loge("remove deleted status netId=" + Integer.toString(netId)
+ log("remove deleted status netId=" + Integer.toString(netId)
+ " " + currentConfig.configKey());
}
}
@@ -2948,26 +2990,59 @@
currentConfig.ephemeral) {
// Make the config non-ephemeral since the user just explicitly clicked it.
currentConfig.ephemeral = false;
- if (DBG) loge("remove ephemeral status netId=" + Integer.toString(netId)
+ if (DBG) log("remove ephemeral status netId=" + Integer.toString(netId)
+ " " + currentConfig.configKey());
}
- if (DBG) loge("will read network variables netId=" + Integer.toString(netId));
+ if (VDBG) log("will read network variables netId=" + Integer.toString(netId));
readNetworkVariables(currentConfig);
+ // Persist configuration paramaters that are not saved by supplicant.
+ if (config.lastUpdateName != null) {
+ currentConfig.lastUpdateName = config.lastUpdateName;
+ }
+ if (config.lastUpdateUid != WifiConfiguration.UNKNOWN_UID) {
+ currentConfig.lastUpdateUid = config.lastUpdateUid;
+ }
+
mConfiguredNetworks.put(netId, currentConfig);
- mNetworkIds.put(configKey(currentConfig), netId);
NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config);
result.setIsNewNetwork(newNetwork);
result.setNetworkId(netId);
+ if (homeSP != null) {
+ writePasspointConfigs(null, homeSP);
+ }
writeKnownNetworkHistory(false);
return result;
}
+ public WifiConfiguration getWifiConfigForHomeSP(HomeSP homeSP) {
+ WifiConfiguration config = mConfiguredNetworks.getByFQDN(homeSP.getFQDN());
+ if (config == null) {
+ Log.e(TAG, "Could not find network for homeSP " + homeSP.getFQDN());
+ }
+ return config;
+ }
+
+ private HomeSP getHomeSPForConfig(WifiConfiguration config) {
+ WifiConfiguration storedConfig = mConfiguredNetworks.get(config.networkId);
+ return storedConfig != null && storedConfig.isPasspoint() ?
+ mMOManager.getHomeSP(storedConfig.FQDN) : null;
+ }
+
+ public ScanDetailCache getScanDetailCache(WifiConfiguration config) {
+ if (config == null) return null;
+ ScanDetailCache cache = mScanDetailCaches.get(config.networkId);
+ if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
+ cache = new ScanDetailCache(config);
+ mScanDetailCaches.put(config.networkId, cache);
+ }
+ return cache;
+ }
/**
* This function run thru the Saved WifiConfigurations and check if some should be linked.
@@ -2975,7 +3050,7 @@
*/
public void linkConfiguration(WifiConfiguration config) {
- if (config.scanResultCache != null && config.scanResultCache.size() > 6) {
+ if (getScanDetailCache(config) != null && getScanDetailCache(config).size() > 6) {
// Ignore configurations with large number of BSSIDs
return;
}
@@ -3000,7 +3075,8 @@
continue;
}
- if (link.scanResultCache != null && link.scanResultCache.size() > 6) {
+ ScanDetailCache linkedScanDetailCache = getScanDetailCache(link);
+ if (linkedScanDetailCache != null && linkedScanDetailCache.size() > 6) {
// Ignore configurations with large number of BSSIDs
continue;
}
@@ -3019,10 +3095,11 @@
// hoping that WifiConfigurations are indeed behind the same gateway.
// once both WifiConfiguration have been tried and thus once both efault gateways
// are known we will revisit the choice of linking them
- if ((config.scanResultCache != null) && (config.scanResultCache.size() <= 6)
- && (link.scanResultCache != null) && (link.scanResultCache.size() <= 6)) {
- for (String abssid : config.scanResultCache.keySet()) {
- for (String bbssid : link.scanResultCache.keySet()) {
+ if ((getScanDetailCache(config) != null)
+ && (getScanDetailCache(config).size() <= 6)) {
+
+ for (String abssid : getScanDetailCache(config).keySet()) {
+ for (String bbssid : linkedScanDetailCache.keySet()) {
if (VVDBG) {
loge("linkConfiguration try to link due to DBDC BSSID match "
+ link.SSID +
@@ -3052,8 +3129,8 @@
if (doLink) {
if (VDBG) {
- loge("linkConfiguration: will link " + link.configKey()
- + " and " + config.configKey());
+ loge("linkConfiguration: will link " + link.configKey()
+ + " and " + config.configKey());
}
if (link.linkedConfigurations == null) {
link.linkedConfigurations = new HashMap<String, Integer>();
@@ -3092,139 +3169,6 @@
}
}
- /*
- * We try to link a scan result with a WifiConfiguration for which SSID and
- * key management dont match,
- * for instance, we try identify the 5GHz SSID of a DBDC AP,
- * even though we know only of the 2.4GHz
- *
- * Obviously, this function is not optimal since it is used to compare every scan
- * result with every Saved WifiConfiguration, with a string.equals operation.
- * As a speed up, might be better to implement the mConfiguredNetworks store as a
- * <String, WifiConfiguration> object instead of a <Integer, WifiConfiguration> object
- * so as to speed this up. Also to prevent the tiny probability of hash collision.
- *
- */
- public WifiConfiguration associateWithConfiguration(ScanResult result) {
- boolean doNotAdd = false;
- String configKey = WifiConfiguration.configKey(result);
- if (configKey == null) {
- if (DBG) loge("associateWithConfiguration(): no config key " );
- return null;
- }
-
- // Need to compare with quoted string
- String SSID = "\"" + result.SSID + "\"";
-
- if (VVDBG) {
- loge("associateWithConfiguration(): try " + configKey);
- }
-
- Checksum csum = new CRC32();
- csum.update(SSID.getBytes(), 0, SSID.getBytes().length);
- if (mDeletedSSIDs.contains(csum.getValue())) {
- doNotAdd = true;
- }
-
- WifiConfiguration config = null;
- for (WifiConfiguration link : mConfiguredNetworks.values()) {
- boolean doLink = false;
-
- if (link.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DELETED || link.selfAdded ||
- link.ephemeral) {
- if (VVDBG) loge("associateWithConfiguration(): skip selfadd " + link.configKey() );
- // Make sure we dont associate the scan result to a deleted config
- continue;
- }
-
- if (!link.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
- if (VVDBG) loge("associateWithConfiguration(): skip non-PSK " + link.configKey() );
- // Make sure we dont associate the scan result to a non-PSK config
- continue;
- }
-
- if (configKey.equals(link.configKey())) {
- if (VVDBG) loge("associateWithConfiguration(): found it!!! " + configKey );
- return link; // Found it exactly
- }
-
- if (!doNotAdd && (link.scanResultCache != null) && (link.scanResultCache.size() <= 6)) {
- for (String bssid : link.scanResultCache.keySet()) {
- if (result.BSSID.regionMatches(true, 0, bssid, 0, 16)
- && SSID.regionMatches(false, 0, link.SSID, 0, 4)) {
- // If first 16 ascii characters of BSSID matches, and first 3
- // characters of SSID match, we assume this is a home setup
- // and thus we will try to transfer the password from the known
- // BSSID/SSID to the recently found BSSID/SSID
-
- // If (VDBG)
- // loge("associateWithConfiguration OK " );
- doLink = true;
- break;
- }
- }
- }
-
- if (doLink) {
- // Try to make a non verified WifiConfiguration, but only if the original
- // configuration was not self already added
- if (VDBG) {
- loge("associateWithConfiguration: try to create " +
- result.SSID + " and associate it with: " + link.SSID
- + " key " + link.configKey());
- }
- config = wifiConfigurationFromScanResult(result);
- if (config != null) {
- config.selfAdded = true;
- config.didSelfAdd = true;
- config.dirty = true;
- config.peerWifiConfiguration = link.configKey();
- if (config.allowedKeyManagement.equals(link.allowedKeyManagement) &&
- config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
- if (VDBG && config != null) {
- loge("associateWithConfiguration: got a config from beacon"
- + config.SSID + " key " + config.configKey());
- }
- // Transfer the credentials from the configuration we are linking from
- String psk = readNetworkVariableFromSupplicantFile(link.SSID, "psk");
- if (psk != null) {
- config.preSharedKey = psk;
- if (VDBG) {
- if (config.preSharedKey != null)
- loge(" transfer PSK : " + config.preSharedKey);
- }
-
- // Link configurations
- if (link.linkedConfigurations == null) {
- link.linkedConfigurations = new HashMap<String, Integer>();
- }
- if (config.linkedConfigurations == null) {
- config.linkedConfigurations = new HashMap<String, Integer>();
- }
- link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1));
- config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1));
-
- // Carry over the Ip configuration
- if (link.getIpConfiguration() != null) {
- config.setIpConfiguration(link.getIpConfiguration());
- }
- } else {
- config = null;
- }
- } else {
- config = null;
- }
- if (config != null) break;
- }
- if (VDBG && config != null) {
- loge("associateWithConfiguration: success, created: " + config.SSID
- + " key " + config.configKey());
- }
- }
- }
- return config;
- }
-
public HashSet<Integer> makeChannelList(WifiConfiguration config, int age, boolean restrict) {
if (config == null)
return null;
@@ -3233,7 +3177,7 @@
HashSet<Integer> channels = new HashSet<Integer>();
//get channels for this configuration, if there are at least 2 BSSIDs
- if (config.scanResultCache == null && config.linkedConfigurations == null) {
+ if (getScanDetailCache(config) == null && config.linkedConfigurations == null) {
return null;
}
@@ -3242,8 +3186,8 @@
dbg.append("makeChannelList age=" + Integer.toString(age)
+ " for " + config.configKey()
+ " max=" + maxNumActiveChannelsForPartialScans);
- if (config.scanResultCache != null) {
- dbg.append(" bssids=" + config.scanResultCache.size());
+ if (getScanDetailCache(config) != null) {
+ dbg.append(" bssids=" + getScanDetailCache(config).size());
}
if (config.linkedConfigurations != null) {
dbg.append(" linked=" + config.linkedConfigurations.size());
@@ -3252,10 +3196,11 @@
}
int numChannels = 0;
- if (config.scanResultCache != null && config.scanResultCache.size() > 0) {
- for (ScanResult result : config.scanResultCache.values()) {
+ if (getScanDetailCache(config) != null && getScanDetailCache(config).size() > 0) {
+ for (ScanDetail scanDetail : getScanDetailCache(config).values()) {
+ ScanResult result = scanDetail.getScanResult();
//TODO : cout active and passive channels separately
- if (numChannels > maxNumActiveChannelsForPartialScans) {
+ if (numChannels > maxNumActiveChannelsForPartialScans.get()) {
break;
}
if (VDBG) {
@@ -3276,16 +3221,17 @@
WifiConfiguration linked = getWifiConfiguration(key);
if (linked == null)
continue;
- if (linked.scanResultCache == null) {
+ if (getScanDetailCache(linked) == null) {
continue;
}
- for (ScanResult result : linked.scanResultCache.values()) {
+ for (ScanDetail scanDetail : getScanDetailCache(linked).values()) {
+ ScanResult result = scanDetail.getScanResult();
if (VDBG) {
loge("has link: " + result.BSSID
+ " freq=" + Integer.toString(result.frequency)
+ " age=" + Long.toString(now_ms - result.seen));
}
- if (numChannels > maxNumActiveChannelsForPartialScans) {
+ if (numChannels > maxNumActiveChannelsForPartialScans.get()) {
break;
}
if (((now_ms - result.seen) < age)/*||(!restrict || result.is24GHz())*/) {
@@ -3298,15 +3244,214 @@
return channels;
}
+ private Map<HomeSP, PasspointMatch> matchPasspointNetworks(ScanDetail scanDetail) {
+ if (!mMOManager.isConfigured()) {
+ return null;
+ }
+ NetworkDetail networkDetail = scanDetail.getNetworkDetail();
+ if (!networkDetail.hasInterworking()) {
+ return null;
+ }
+ updateAnqpCache(scanDetail, networkDetail.getANQPElements());
+
+ Map<HomeSP, PasspointMatch> matches = matchNetwork(scanDetail, true);
+ Log.d(Utils.hs2LogTag(getClass()), scanDetail.getSSID() +
+ " pass 1 matches: " + toMatchString(matches));
+ return matches;
+ }
+
+ private Map<HomeSP, PasspointMatch> matchNetwork(ScanDetail scanDetail, boolean query) {
+ NetworkDetail networkDetail = scanDetail.getNetworkDetail();
+
+ ANQPData anqpData = mAnqpCache.getEntry(networkDetail);
+
+ Map<Constants.ANQPElementType, ANQPElement> anqpElements =
+ anqpData != null ? anqpData.getANQPElements() : null;
+
+ boolean queried = !query;
+ Collection<HomeSP> homeSPs = mMOManager.getLoadedSPs().values();
+ Map<HomeSP, PasspointMatch> matches = new HashMap<>(homeSPs.size());
+ Log.d(Utils.hs2LogTag(getClass()), "match nwk " + scanDetail.toKeyString() +
+ ", anqp " + ( anqpData != null ? "present" : "missing" ) +
+ ", query " + query + ", home sps: " + homeSPs.size());
+
+ for (HomeSP homeSP : homeSPs) {
+ PasspointMatch match = homeSP.match(networkDetail, anqpElements, mSIMAccessor);
+
+ Log.d(Utils.hs2LogTag(getClass()), " -- " +
+ homeSP.getFQDN() + ": match " + match + ", queried " + queried);
+
+ if (match == PasspointMatch.Incomplete && !queried) {
+ if (mAnqpCache.initiate(networkDetail)) {
+ mSupplicantBridge.startANQP(scanDetail);
+ }
+ queried = true;
+ }
+ matches.put(homeSP, match);
+ }
+ return matches;
+ }
+
+ public void notifyANQPDone(Long bssid, boolean success) {
+ mSupplicantBridge.notifyANQPDone(bssid, success);
+ }
+
+ public void notifyANQPResponse(ScanDetail scanDetail,
+ Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
+
+ updateAnqpCache(scanDetail, anqpElements);
+ if (anqpElements == null || anqpElements.isEmpty()) {
+ return;
+ }
+ scanDetail.propagateANQPInfo(anqpElements);
+
+ Map<HomeSP, PasspointMatch> matches = matchNetwork(scanDetail, false);
+ Log.d(Utils.hs2LogTag(getClass()), scanDetail.getSSID() +
+ " pass 2 matches: " + toMatchString(matches));
+
+ cacheScanResultForPasspointConfigs(scanDetail, matches);
+ }
+
+
+ private void updateAnqpCache(ScanDetail scanDetail,
+ Map<Constants.ANQPElementType,ANQPElement> anqpElements)
+ {
+ NetworkDetail networkDetail = scanDetail.getNetworkDetail();
+
+ if (anqpElements == null) {
+ // Try to pull cached data if query failed.
+ ANQPData data = mAnqpCache.getEntry(networkDetail);
+ if (data != null) {
+ scanDetail.propagateANQPInfo(data.getANQPElements());
+ }
+ return;
+ }
+
+ mAnqpCache.update(networkDetail, anqpElements);
+ }
+
+ private static String toMatchString(Map<HomeSP, PasspointMatch> matches) {
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<HomeSP, PasspointMatch> entry : matches.entrySet()) {
+ sb.append(' ').append(entry.getKey().getFQDN()).append("->").append(entry.getValue());
+ }
+ return sb.toString();
+ }
+
+ private void cacheScanResultForPasspointConfigs(ScanDetail scanDetail,
+ Map<HomeSP,PasspointMatch> matches) {
+
+ for (Map.Entry<HomeSP, PasspointMatch> entry : matches.entrySet()) {
+ PasspointMatch match = entry.getValue();
+ if (match == PasspointMatch.HomeProvider || match == PasspointMatch.RoamingProvider) {
+ WifiConfiguration config = getWifiConfigForHomeSP(entry.getKey());
+ if (config != null) {
+ cacheScanResultForConfig(config, scanDetail, entry.getValue());
+ } else {
+ Log.w(Utils.hs2LogTag(getClass()), "Failed to find config for '" +
+ entry.getKey().getFQDN() + "'");
+ /* perhaps the configuration was deleted?? */
+ }
+ }
+ }
+ }
+
+ private void cacheScanResultForConfig(
+ WifiConfiguration config, ScanDetail scanDetail, PasspointMatch passpointMatch) {
+
+ ScanResult scanResult = scanDetail.getScanResult();
+
+ if (config.autoJoinStatus >= WifiConfiguration.AUTO_JOIN_DELETED) {
+ if (VVDBG) {
+ loge("updateSavedNetworkHistory(): found a deleted, skip it... "
+ + config.configKey());
+ }
+ // The scan result belongs to a deleted config:
+ // - increment numConfigFound to remember that we found a config
+ // matching for this scan result
+ // - dont do anything since the config was deleted, just skip...
+ return;
+ }
+
+ ScanDetailCache scanDetailCache = getScanDetailCache(config);
+ if (scanDetailCache == null) {
+ Log.w(TAG, "Could not allocate scan cache for " + config.SSID);
+ return;
+ }
+
+ // Adding a new BSSID
+ ScanResult result = scanDetailCache.get(scanResult.BSSID);
+ if (result != null) {
+ // transfer the black list status
+ scanResult.autoJoinStatus = result.autoJoinStatus;
+ scanResult.blackListTimestamp = result.blackListTimestamp;
+ scanResult.numIpConfigFailures = result.numIpConfigFailures;
+ scanResult.numConnection = result.numConnection;
+ scanResult.isAutoJoinCandidate = result.isAutoJoinCandidate;
+ }
+
+ if (config.ephemeral) {
+ // For an ephemeral Wi-Fi config, the ScanResult should be considered
+ // untrusted.
+ scanResult.untrusted = true;
+ }
+
+ if (scanDetailCache.size() > (maxNumScanCacheEntries + 64)) {
+ long now_dbg = 0;
+ if (VVDBG) {
+ loge(" Will trim config " + config.configKey()
+ + " size " + scanDetailCache.size());
+
+ for (ScanDetail sd : scanDetailCache.values()) {
+ loge(" " + sd.getBSSIDString() + " " + sd.getSeen());
+ }
+ now_dbg = SystemClock.elapsedRealtimeNanos();
+ }
+ // Trim the scan result cache to maxNumScanCacheEntries entries max
+ // Since this operation is expensive, make sure it is not performed
+ // until the cache has grown significantly above the trim treshold
+ scanDetailCache.trim(maxNumScanCacheEntries);
+ if (VVDBG) {
+ long diff = SystemClock.elapsedRealtimeNanos() - now_dbg;
+ loge(" Finished trimming config, time(ns) " + diff);
+ for (ScanDetail sd : scanDetailCache.values()) {
+ loge(" " + sd.getBSSIDString() + " " + sd.getSeen());
+ }
+ }
+ }
+
+ // Add the scan result to this WifiConfiguration
+ if (passpointMatch != null)
+ scanDetailCache.put(scanDetail, passpointMatch, getHomeSPForConfig(config));
+ else
+ scanDetailCache.put(scanDetail);
+
+ // Since we added a scan result to this configuration, re-attempt linking
+ linkConfiguration(config);
+ }
+
+
// Update the WifiConfiguration database with the new scan result
// A scan result can be associated to multiple WifiConfigurations
- public boolean updateSavedNetworkHistory(ScanResult scanResult) {
+ public boolean updateSavedNetworkHistory(ScanDetail scanDetail) {
+
+ ScanResult scanResult = scanDetail.getScanResult();
+ NetworkDetail networkDetail = scanDetail.getNetworkDetail();
+
int numConfigFound = 0;
if (scanResult == null)
return false;
String SSID = "\"" + scanResult.SSID + "\"";
+ if (networkDetail.hasInterworking()) {
+ Map<HomeSP, PasspointMatch> matches = matchPasspointNetworks(scanDetail);
+ if (matches != null) {
+ cacheScanResultForPasspointConfigs(scanDetail, matches);
+ return matches.size() != 0;
+ }
+ }
+
for (WifiConfiguration config : mConfiguredNetworks.values()) {
boolean found = false;
@@ -3344,70 +3489,7 @@
if (found) {
numConfigFound ++;
-
- if (config.autoJoinStatus >= WifiConfiguration.AUTO_JOIN_DELETED) {
- if (VVDBG) {
- loge("updateSavedNetworkHistory(): found a deleted, skip it... "
- + config.configKey());
- }
- // The scan result belongs to a deleted config:
- // - increment numConfigFound to remember that we found a config
- // matching for this scan result
- // - dont do anything since the config was deleted, just skip...
- continue;
- }
-
- if (config.scanResultCache == null) {
- config.scanResultCache = new HashMap<String, ScanResult>();
- }
-
- // Adding a new BSSID
- ScanResult result = config.scanResultCache.get(scanResult.BSSID);
- if (result == null) {
- config.dirty = true;
- } else {
- // transfer the black list status
- scanResult.autoJoinStatus = result.autoJoinStatus;
- scanResult.blackListTimestamp = result.blackListTimestamp;
- scanResult.numIpConfigFailures = result.numIpConfigFailures;
- scanResult.numConnection = result.numConnection;
- scanResult.isAutoJoinCandidate = result.isAutoJoinCandidate;
- }
-
- if (config.ephemeral) {
- // For an ephemeral Wi-Fi config, the ScanResult should be considered
- // untrusted.
- scanResult.untrusted = true;
- }
-
- if (config.scanResultCache.size() > (maxNumScanCacheEntries + 64)) {
- long now_dbg = 0;
- if (VVDBG) {
- loge(" Will trim config " + config.configKey()
- + " size " + config.scanResultCache.size());
-
- for (ScanResult r : config.scanResultCache.values()) {
- loge(" " + result.BSSID + " " + result.seen);
- }
- now_dbg = SystemClock.elapsedRealtimeNanos();
- }
- // Trim the scan result cache to maxNumScanCacheEntries entries max
- // Since this operation is expensive, make sure it is not performed
- // until the cache has grown significantly above the trim treshold
- config.trimScanResultsCache(maxNumScanCacheEntries);
- if (VVDBG) {
- long diff = SystemClock.elapsedRealtimeNanos() - now_dbg;
- loge(" Finished trimming config, time(ns) " + diff);
- for (ScanResult r : config.scanResultCache.values()) {
- loge(" " + r.BSSID + " " + r.seen);
- }
- }
- }
-
- // Add the scan result to this WifiConfiguration
- config.scanResultCache.put(scanResult.BSSID, scanResult);
- // Since we added a scan result to this configuration, re-attempt linking
- linkConfiguration(config);
+ cacheScanResultForConfig(config, scanDetail, null);
}
if (VDBG && found) {
@@ -3418,7 +3500,7 @@
loge(" got known scan result " +
scanResult.BSSID + " key : "
+ config.configKey() + " num: " +
- Integer.toString(config.scanResultCache.size())
+ Integer.toString(getScanDetailCache(config).size())
+ " rssi=" + Integer.toString(scanResult.level)
+ " freq=" + Integer.toString(scanResult.frequency)
+ status);
@@ -3609,70 +3691,20 @@
config.preSharedKey = null;
}
- value = mWifiNative.getNetworkVariable(config.networkId,
- WifiConfiguration.Protocol.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.Protocol.strings);
- if (0 <= index) {
- config.allowedProtocols.set(index);
- }
- }
- }
+ readNetworkBitsetVariable(config.networkId, config.allowedProtocols,
+ WifiConfiguration.Protocol.varName, WifiConfiguration.Protocol.strings);
- value = mWifiNative.getNetworkVariable(config.networkId,
- WifiConfiguration.KeyMgmt.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.KeyMgmt.strings);
- if (0 <= index) {
- config.allowedKeyManagement.set(index);
- }
- }
- }
+ readNetworkBitsetVariable(config.networkId, config.allowedKeyManagement,
+ WifiConfiguration.KeyMgmt.varName, WifiConfiguration.KeyMgmt.strings);
- value = mWifiNative.getNetworkVariable(config.networkId,
- WifiConfiguration.AuthAlgorithm.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
- if (0 <= index) {
- config.allowedAuthAlgorithms.set(index);
- }
- }
- }
+ readNetworkBitsetVariable(config.networkId, config.allowedAuthAlgorithms,
+ WifiConfiguration.AuthAlgorithm.varName, WifiConfiguration.AuthAlgorithm.strings);
- value = mWifiNative.getNetworkVariable(config.networkId,
- WifiConfiguration.PairwiseCipher.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.PairwiseCipher.strings);
- if (0 <= index) {
- config.allowedPairwiseCiphers.set(index);
- }
- }
- }
+ readNetworkBitsetVariable(config.networkId, config.allowedPairwiseCiphers,
+ WifiConfiguration.PairwiseCipher.varName, WifiConfiguration.PairwiseCipher.strings);
- value = mWifiNative.getNetworkVariable(config.networkId,
- WifiConfiguration.GroupCipher.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.GroupCipher.strings);
- if (0 <= index) {
- config.allowedGroupCiphers.set(index);
- }
- }
- }
+ readNetworkBitsetVariable(config.networkId, config.allowedGroupCiphers,
+ WifiConfiguration.GroupCipher.varName, WifiConfiguration.GroupCipher.strings);
if (config.enterpriseConfig == null) {
config.enterpriseConfig = new WifiEnterpriseConfig();
@@ -3746,7 +3778,9 @@
/* return the allowed key management based on a scan result */
- public WifiConfiguration wifiConfigurationFromScanResult(ScanResult result) {
+ public WifiConfiguration wifiConfigurationFromScanResult(ScanDetail scanDetail) {
+
+ ScanResult result = scanDetail.getScanResult();
WifiConfiguration config = new WifiConfiguration();
config.SSID = "\"" + result.SSID + "\"";
@@ -3771,10 +3805,7 @@
config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
}
- config.scanResultCache = new HashMap<String, ScanResult>();
- if (config.scanResultCache == null)
- return null;
- config.scanResultCache.put(result.BSSID, result);
+ /* getScanDetailCache(config).put(scanDetail); */
return config;
}
@@ -3790,16 +3821,26 @@
pw.println("Dump of WifiConfigStore");
pw.println("mLastPriority " + mLastPriority);
pw.println("Configured networks");
- for (WifiConfiguration conf : getConfiguredNetworks()) {
+ for (WifiConfiguration conf : getAllConfiguredNetworks()) {
pw.println(conf);
}
pw.println();
-
+ if (mLostConfigsDbg != null && mLostConfigsDbg.size() > 0) {
+ pw.println("LostConfigs: ");
+ for (String s : mLostConfigsDbg) {
+ pw.println(s);
+ }
+ }
if (mLocalLog != null) {
pw.println("WifiConfigStore - Log Begin ----");
mLocalLog.dump(fd, pw, args);
pw.println("WifiConfigStore - Log End ----");
}
+ if (mMOManager.isConfigured()) {
+ pw.println("Begin dump of ANQP Cache");
+ mAnqpCache.dump(pw);
+ pw.println("End dump of ANQP Cache");
+ }
}
public String getConfigFile() {
@@ -3821,6 +3862,14 @@
}
}
+ private void logKernelTime() {
+ long kernelTimeMs = System.nanoTime()/(1000*1000);
+ StringBuilder builder = new StringBuilder();
+ builder.append("kernel time = ").append(kernelTimeMs/1000).append(".").append
+ (kernelTimeMs%1000).append("\n");
+ localLog(builder.toString());
+ }
+
protected void log(String s) {
Log.d(TAG, s);
}
@@ -3842,7 +3891,7 @@
}
WifiConfiguration config;
- synchronized(mConfiguredNetworks) {
+ synchronized(mConfiguredNetworks) { // !!! Useless synchronization
config = mConfiguredNetworks.get(netId);
}
@@ -3928,6 +3977,112 @@
return false;
}
+ boolean isNetworkConfigured(WifiConfiguration config) {
+ // Check if either we have a network Id or a WifiConfiguration
+ // matching the one we are trying to add.
+
+ if(config.networkId != INVALID_NETWORK_ID) {
+ return (mConfiguredNetworks.get(config.networkId) != null);
+ }
+
+ return (mConfiguredNetworks.getByConfigKey(config.configKey()) != null);
+ }
+
+ /**
+ * Checks if uid has access to modify the configuration corresponding to networkId.
+ *
+ * Factors involved in modifiability of a config are as follows.
+ * If uid is a Device Owner app then it has full control over the device, including WiFi
+ * configs.
+ * If the modification is only for administrative annotation (e.g. when connecting) or the
+ * config is not lockdown eligible (currently that means any config not last updated by the DO)
+ * then the creator of config or an app holding OVERRIDE_CONFIG_WIFI can modify the config.
+ * If the config is lockdown eligible and the modification is substantial (not annotation)
+ * then the requirement to be able to modify the config by the uid is as follows:
+ * a) the uid has to hold OVERRIDE_CONFIG_WIFI and
+ * b) the lockdown feature should be disabled.
+ */
+ boolean canModifyNetwork(int uid, int networkId, boolean onlyAnnotate) {
+ WifiConfiguration config = mConfiguredNetworks.get(networkId);
+
+ if (config == null) {
+ loge("canModifyNetwork: cannot find config networkId " + networkId);
+ return false;
+ }
+
+ final DevicePolicyManagerInternal dpmi = LocalServices.getService(
+ DevicePolicyManagerInternal.class);
+
+ final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
+ DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+ if (isUidDeviceOwner) {
+ // Device Owner has full control over the device, including WiFi Configs
+ return true;
+ }
+
+ final boolean isCreator = (config.creatorUid == uid);
+
+ if (onlyAnnotate) {
+ return isCreator || checkConfigOverridePermission(uid);
+ }
+
+ // Check if device has DPM capability. If it has and dpmi is still null, then we
+ // treat this case with suspicion and bail out.
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)
+ && dpmi == null) {
+ return false;
+ }
+
+ // WiFi config lockdown related logic. At this point we know uid NOT to be a Device Owner.
+
+ final boolean isConfigEligibleForLockdown = dpmi != null && dpmi.isActiveAdminWithPolicy(
+ config.creatorUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ if (!isConfigEligibleForLockdown) {
+ return isCreator || checkConfigOverridePermission(uid);
+ }
+
+ final ContentResolver resolver = mContext.getContentResolver();
+ final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
+ Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
+ return !isLockdownFeatureEnabled && checkConfigOverridePermission(uid);
+ }
+
+ /**
+ * Checks if uid has access to modify config.
+ */
+ boolean canModifyNetwork(int uid, WifiConfiguration config, boolean onlyAnnotate) {
+ if (config == null) {
+ loge("canModifyNetowrk recieved null configuration");
+ return false;
+ }
+
+ // Resolve the correct network id.
+ int netid;
+ if (config.networkId != INVALID_NETWORK_ID){
+ netid = config.networkId;
+ } else {
+ WifiConfiguration test = mConfiguredNetworks.getByConfigKey(config.configKey());
+ if (test == null) {
+ return false;
+ } else {
+ netid = test.networkId;
+ }
+ }
+
+ return canModifyNetwork(uid, netid, onlyAnnotate);
+ }
+
+ boolean checkConfigOverridePermission(int uid) {
+ try {
+ return (AppGlobals.getPackageManager().checkUidPermission(
+ android.Manifest.permission.OVERRIDE_WIFI_CONFIG, uid)
+ == PackageManager.PERMISSION_GRANTED);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
/** called when CS ask WiFistateMachine to disconnect the current network
* because the score is bad.
*/
@@ -3962,16 +4117,17 @@
// Look for the BSSID in our config store
for (WifiConfiguration config : mConfiguredNetworks.values()) {
- if (config.scanResultCache != null) {
- for (ScanResult result: config.scanResultCache.values()) {
- if (result.BSSID.equals(BSSID)) {
+ if (getScanDetailCache(config) != null) {
+ for (ScanDetail scanDetail : getScanDetailCache(config).values()) {
+ if (scanDetail.getBSSIDString().equals(BSSID)) {
if (enable) {
- result.setAutoJoinStatus(ScanResult.ENABLED);
+ scanDetail.getScanResult().setAutoJoinStatus(ScanResult.ENABLED);
} else {
// Black list the BSSID we were trying to join
// so as the Roam state machine
// doesn't pick it up over and over
- result.setAutoJoinStatus(ScanResult.AUTO_ROAM_DISABLED);
+ scanDetail.getScanResult().setAutoJoinStatus(
+ ScanResult.AUTO_ROAM_DISABLED);
found = true;
}
}
@@ -3987,20 +4143,48 @@
DEFAULT_MAX_DHCP_RETRIES);
}
+ void clearBssidBlacklist() {
+ if (!mWifiStateMachine.useHalBasedAutoJoinOffload()) {
+ if(DBG) {
+ Log.d(TAG, "No blacklist allowed without epno enabled");
+ }
+ return;
+ }
+ mBssidBlacklist = new HashSet<String>();
+ mWifiNative.clearBlacklist();
+ mWifiNative.setBssidBlacklist(null);
+ }
+
+ void blackListBssid(String BSSID) {
+ if (!mWifiStateMachine.useHalBasedAutoJoinOffload()) {
+ if(DBG) {
+ Log.d(TAG, "No blacklist allowed without epno enabled");
+ }
+ return;
+ }
+ if (BSSID == null)
+ return;
+ mBssidBlacklist.add(BSSID);
+ // Blacklist at wpa_supplicant
+ mWifiNative.addToBlacklist(BSSID);
+ // Blacklist at firmware
+ String list[] = new String[mBssidBlacklist.size()];
+ int count = 0;
+ for (String bssid : mBssidBlacklist) {
+ list[count++] = bssid;
+ }
+ mWifiNative.setBssidBlacklist(list);
+ }
+
void handleSSIDStateChange(int netId, boolean enabled, String message, String BSSID) {
WifiConfiguration config = mConfiguredNetworks.get(netId);
if (config != null) {
if (enabled) {
- loge("SSID re-enabled for " + config.configKey() +
+ loge("Ignoring SSID re-enabled from supplicant: " + config.configKey() +
" had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
+ " self added " + config.selfAdded + " ephemeral " + config.ephemeral);
- //TODO: http://b/16381983 Fix Wifi Network Blacklisting
- //TODO: really I don't know if re-enabling is right but we
- //TODO: should err on the side of trying to connect
- //TODO: even if the attempt will fail
- if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
- config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
- }
+ //We should not re-enable the BSSID based on Supplicant reanable.
+ // Framework will re-enable it after its own blacklist timer expires
} else {
loge("SSID temp disabled for " + config.configKey() +
" had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
@@ -4059,8 +4243,8 @@
// Also blacklist the BSSId if we find it
ScanResult result = null;
String bssidDbg = "";
- if (config.scanResultCache != null && BSSID != null) {
- result = config.scanResultCache.get(BSSID);
+ if (getScanDetailCache(config) != null && BSSID != null) {
+ result = getScanDetailCache(config).get(BSSID);
}
if (result != null) {
result.numIpConfigFailures ++;
@@ -4130,7 +4314,7 @@
ret = putCertInKeyStore(userCertName, config.getClientCertificate());
if (ret == false) {
// Remove private key installed
- mKeyStore.delKey(privKeyName, Process.WIFI_UID);
+ mKeyStore.delete(privKeyName, Process.WIFI_UID);
return ret;
}
}
@@ -4140,7 +4324,7 @@
if (ret == false) {
if (config.getClientCertificate() != null) {
// Remove client key+cert
- mKeyStore.delKey(privKeyName, Process.WIFI_UID);
+ mKeyStore.delete(privKeyName, Process.WIFI_UID);
mKeyStore.delete(userCertName, Process.WIFI_UID);
}
return ret;
@@ -4179,7 +4363,7 @@
// a valid client certificate is configured
if (!TextUtils.isEmpty(client)) {
if (DBG) Log.d(TAG, "removing client private key and user cert");
- mKeyStore.delKey(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
+ mKeyStore.delete(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
}
@@ -4269,4 +4453,18 @@
}
}
+ private void readNetworkBitsetVariable(int netId, BitSet variable, String varName,
+ String[] strings) {
+ String value = mWifiNative.getNetworkVariable(netId, varName);
+ if (!TextUtils.isEmpty(value)) {
+ variable.clear();
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index = lookupString(val, strings);
+ if (0 <= index) {
+ variable.set(index);
+ }
+ }
+ }
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiController.java b/service/java/com/android/server/wifi/WifiController.java
index 8f36cb7..3b41e3b 100644
--- a/service/java/com/android/server/wifi/WifiController.java
+++ b/service/java/com/android/server/wifi/WifiController.java
@@ -114,6 +114,7 @@
static final int CMD_SET_AP = BASE + 10;
static final int CMD_DEFERRED_TOGGLE = BASE + 11;
static final int CMD_USER_PRESENT = BASE + 12;
+ static final int CMD_AP_START_FAILURE = BASE + 13;
private DefaultState mDefaultState = new DefaultState();
private StaEnabledState mStaEnabledState = new StaEnabledState();
@@ -172,6 +173,7 @@
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_DEVICE_IDLE);
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
mContext.registerReceiver(
new BroadcastReceiver() {
@Override
@@ -182,6 +184,14 @@
} else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
WifiManager.EXTRA_NETWORK_INFO);
+ } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
+ int state = intent.getIntExtra(
+ WifiManager.EXTRA_WIFI_AP_STATE,
+ WifiManager.WIFI_AP_STATE_FAILED);
+ if(state == WifiManager.WIFI_AP_STATE_FAILED) {
+ loge(TAG + "SoftAP start failed");
+ sendMessage(CMD_AP_START_FAILURE);
+ }
}
}
},
@@ -371,6 +381,7 @@
case CMD_WIFI_TOGGLED:
case CMD_AIRPLANE_TOGGLED:
case CMD_EMERGENCY_MODE_CHANGED:
+ case CMD_AP_START_FAILURE:
break;
case CMD_USER_PRESENT:
mFirstUserSignOnSeen = true;
@@ -398,6 +409,7 @@
mDisabledTimestamp = SystemClock.elapsedRealtime();
mDeferredEnableSerialNumber++;
mHaveDeferredEnable = false;
+ mWifiStateMachine.clearANQPCache();
}
@Override
public boolean processMessage(Message msg) {
@@ -519,6 +531,7 @@
mDisabledTimestamp = SystemClock.elapsedRealtime();
mDeferredEnableSerialNumber++;
mHaveDeferredEnable = false;
+ mWifiStateMachine.clearANQPCache();
}
@Override
@@ -607,6 +620,12 @@
transitionTo(mApStaDisabledState);
}
break;
+ case CMD_AP_START_FAILURE:
+ if(!mSettingsStore.isScanAlwaysAvailable()) {
+ transitionTo(mApStaDisabledState);
+ } else {
+ transitionTo(mStaDisabledWithScanState);
+ }
default:
return NOT_HANDLED;
}
diff --git a/service/java/com/android/server/wifi/WifiLogger.java b/service/java/com/android/server/wifi/WifiLogger.java
new file mode 100644
index 0000000..8add779
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiLogger.java
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2010 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 com.android.internal.app.IBatteryStats;
+import com.android.internal.util.Protocol;
+
+import android.support.v4.util.CircularArray;
+import android.util.Base64;
+import android.util.LocalLog;
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.zip.Deflater;
+
+/**
+ * Tracks various logs for framework
+ */
+class WifiLogger {
+
+ private static final String TAG = "WifiLogger";
+ private static final boolean DBG = false;
+
+ /** log level flags; keep these consistent with wifi_logger.h */
+
+ /** No logs whatsoever */
+ public static final int VERBOSE_NO_LOG = 0;
+ /** No logs whatsoever */
+ public static final int VERBOSE_NORMAL_LOG = 1;
+ /** Be careful since this one can affect performance and power */
+ public static final int VERBOSE_LOG_WITH_WAKEUP = 2;
+ /** Be careful since this one can affect performance and power and memory */
+ public static final int VERBOSE_DETAILED_LOG_WITH_WAKEUP = 3;
+
+ /** ring buffer flags; keep these consistent with wifi_logger.h */
+ public static final int RING_BUFFER_FLAG_HAS_BINARY_ENTRIES = 0x00000001;
+ public static final int RING_BUFFER_FLAG_HAS_ASCII_ENTRIES = 0x00000002;
+ public static final int RING_BUFFER_FLAG_HAS_PER_PACKET_ENTRIES = 0x00000004;
+
+ /** various reason codes */
+ public static final int REPORT_REASON_NONE = 0;
+ public static final int REPORT_REASON_ASSOC_FAILURE = 1;
+ public static final int REPORT_REASON_AUTH_FAILURE = 2;
+ public static final int REPORT_REASON_AUTOROAM_FAILURE = 3;
+ public static final int REPORT_REASON_DHCP_FAILURE = 4;
+ public static final int REPORT_REASON_UNEXPECTED_DISCONNECT = 5;
+ public static final int REPORT_REASON_SCAN_FAILURE = 6;
+ public static final int REPORT_REASON_USER_ACTION = 7;
+
+ /** number of ring buffer entries to cache */
+ public static final int MAX_RING_BUFFERS = 10;
+
+ /** number of bug reports to hold */
+ public static final int MAX_BUG_REPORTS = 4;
+
+ /** number of alerts to hold */
+ public static final int MAX_ALERT_REPORTS = 1;
+
+ /** minimum wakeup interval for each of the log levels */
+ private static final int MinWakeupIntervals[] = new int[] { 0, 3600, 60, 10 };
+ /** minimum buffer size for each of the log levels */
+ private static final int MinBufferSizes[] = new int[] { 0, 16384, 16384, 65536 };
+
+ private int mLogLevel = VERBOSE_NO_LOG;
+ private String mFirmwareVersion;
+ private String mDriverVersion;
+ private int mSupportedFeatureSet;
+ private WifiNative.RingBufferStatus[] mRingBuffers;
+ private WifiNative.RingBufferStatus mPerPacketRingBuffer;
+ private WifiStateMachine mWifiStateMachine;
+
+ public WifiLogger(WifiStateMachine wifiStateMachine) {
+ mWifiStateMachine = wifiStateMachine;
+ }
+
+ public synchronized void startLogging(boolean verboseEnabled) {
+ mFirmwareVersion = WifiNative.getFirmwareVersion();
+ mDriverVersion = WifiNative.getDriverVersion();
+ mSupportedFeatureSet = WifiNative.getSupportedLoggerFeatureSet();
+
+ if (mLogLevel == VERBOSE_NO_LOG)
+ WifiNative.setLoggingEventHandler(mHandler);
+
+ if (verboseEnabled) {
+ mLogLevel = VERBOSE_LOG_WITH_WAKEUP;
+ } else {
+ mLogLevel = VERBOSE_NORMAL_LOG;
+ }
+ if (mRingBuffers == null) {
+ if (fetchRingBuffers()) {
+ startLoggingAllExceptPerPacketBuffers();
+ }
+ }
+ }
+
+ public synchronized void startPacketLog() {
+ if (mPerPacketRingBuffer != null) {
+ startLoggingRingBuffer(mPerPacketRingBuffer);
+ } else {
+ if (DBG) Log.d(TAG, "There is no per packet ring buffer");
+ }
+ }
+
+ public synchronized void stopPacketLog() {
+ if (mPerPacketRingBuffer != null) {
+ stopLoggingRingBuffer(mPerPacketRingBuffer);
+ } else {
+ if (DBG) Log.d(TAG, "There is no per packet ring buffer");
+ }
+ }
+
+ public synchronized void stopLogging() {
+ if (mLogLevel != VERBOSE_NO_LOG) {
+ //resetLogHandler only can be used when you terminate all logging since all handler will
+ //be removed. This also stop alert logging
+ if(!WifiNative.resetLogHandler()) {
+ Log.e(TAG, "Fail to reset log handler");
+ } else {
+ if (DBG) Log.d(TAG,"Reset log handler");
+ }
+ stopLoggingAllBuffers();
+ mRingBuffers = null;
+ mLogLevel = VERBOSE_NO_LOG;
+ }
+ }
+
+ public synchronized void captureBugReportData(int reason) {
+ BugReport report = captureBugreport(reason, true);
+ mLastBugReports.addLast(report);
+ }
+
+ public synchronized void captureAlertData(int errorCode, byte[] alertData) {
+ BugReport report = captureBugreport(errorCode, /* captureFWDump = */ true);
+ report.alertData = alertData;
+ mLastAlerts.addLast(report);
+ }
+
+ public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("Chipset information :-----------------------------------------------");
+ pw.println("FW Version is: " + mFirmwareVersion);
+ pw.println("Driver Version is: " + mDriverVersion);
+ pw.println("Supported Feature set: " + mSupportedFeatureSet);
+
+ for (int i = 0; i < mLastAlerts.size(); i++) {
+ pw.println("--------------------------------------------------------------------");
+ pw.println("Alert dump " + i);
+ pw.print(mLastAlerts.get(i));
+ pw.println("--------------------------------------------------------------------");
+ }
+
+ for (int i = 0; i < mLastBugReports.size(); i++) {
+ pw.println("--------------------------------------------------------------------");
+ pw.println("Bug dump " + i);
+ pw.print(mLastBugReports.get(i));
+ pw.println("--------------------------------------------------------------------");
+ }
+
+ pw.println("--------------------------------------------------------------------");
+ }
+
+ /* private methods and data */
+ private static class BugReport {
+ long systemTimeMs;
+ long kernelTimeNanos;
+ int errorCode;
+ HashMap<String, byte[][]> ringBuffers = new HashMap();
+ byte[] fwMemoryDump;
+ byte[] alertData;
+
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+
+ Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(systemTimeMs);
+ builder.append("system time = ").append(
+ String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)).append("\n");
+
+ long kernelTimeMs = kernelTimeNanos/(1000*1000);
+ builder.append("kernel time = ").append(kernelTimeMs/1000).append(".").append
+ (kernelTimeMs%1000).append("\n");
+
+ if (alertData == null)
+ builder.append("reason = ").append(errorCode).append("\n");
+ else {
+ builder.append("errorCode = ").append(errorCode);
+ builder.append("data \n");
+ builder.append(compressToBase64(alertData)).append("\n");
+ }
+
+ for (HashMap.Entry<String, byte[][]> e : ringBuffers.entrySet()) {
+ String ringName = e.getKey();
+ byte[][] buffers = e.getValue();
+ builder.append("ring-buffer = ").append(ringName).append("\n");
+
+ int size = 0;
+ for (int i = 0; i < buffers.length; i++) {
+ size += buffers[i].length;
+ }
+
+ byte[] buffer = new byte[size];
+ int index = 0;
+ for (int i = 0; i < buffers.length; i++) {
+ System.arraycopy(buffers[i], 0, buffer, index, buffers[i].length);
+ index += buffers[i].length;
+ }
+
+ builder.append(compressToBase64(buffer));
+ builder.append("\n");
+ }
+
+ if (fwMemoryDump != null) {
+ builder.append("FW Memory dump \n");
+ builder.append(compressToBase64(fwMemoryDump));
+ }
+
+ return builder.toString();
+ }
+ }
+
+ static class LimitedCircularArray<E> {
+ private CircularArray<E> mArray;
+ private int mMax;
+ LimitedCircularArray(int max) {
+ mArray = new CircularArray<E>();
+ mMax = max;
+ }
+
+ public final void addLast(E e) {
+ if (mArray.size() >= mMax)
+ mArray.popFirst();
+ mArray.addLast(e);
+ }
+
+ public final int size() {
+ return mArray.size();
+ }
+
+ public final E get(int i) {
+ return mArray.get(i);
+ }
+ }
+
+ private final LimitedCircularArray<BugReport> mLastAlerts =
+ new LimitedCircularArray<BugReport>(MAX_ALERT_REPORTS);
+ private final LimitedCircularArray<BugReport> mLastBugReports =
+ new LimitedCircularArray<BugReport>(MAX_BUG_REPORTS);
+ private final HashMap<String, LimitedCircularArray<byte[]>> mRingBufferData = new HashMap();
+
+ private final WifiNative.WifiLoggerEventHandler mHandler =
+ new WifiNative.WifiLoggerEventHandler() {
+ @Override
+ public void onRingBufferData(WifiNative.RingBufferStatus status, byte[] buffer) {
+ WifiLogger.this.onRingBufferData(status, buffer);
+ }
+
+ @Override
+ public void onWifiAlert(int errorCode, byte[] buffer) {
+ WifiLogger.this.onWifiAlert(errorCode, buffer);
+ }
+ };
+
+ synchronized void onRingBufferData(WifiNative.RingBufferStatus status, byte[] buffer) {
+ LimitedCircularArray<byte[]> ring = mRingBufferData.get(status.name);
+ if (ring != null) {
+ ring.addLast(buffer);
+ }
+ }
+
+ synchronized void onWifiAlert(int errorCode, byte[] buffer) {
+ if (mWifiStateMachine != null) {
+ mWifiStateMachine.sendMessage(
+ WifiStateMachine.CMD_FIRMWARE_ALERT, errorCode, 0, buffer);
+ }
+ }
+
+ private boolean fetchRingBuffers() {
+ if (mRingBuffers != null) return true;
+
+ mRingBuffers = WifiNative.getRingBufferStatus();
+ if (mRingBuffers != null) {
+ for (WifiNative.RingBufferStatus buffer : mRingBuffers) {
+ if (DBG) Log.d(TAG, "RingBufferStatus is: \n" + buffer.name);
+ if (mRingBufferData.containsKey(buffer.name) == false) {
+ mRingBufferData.put(buffer.name,
+ new LimitedCircularArray<byte[]>(MAX_RING_BUFFERS));
+ }
+ if ((buffer.flag & RING_BUFFER_FLAG_HAS_PER_PACKET_ENTRIES) != 0) {
+ mPerPacketRingBuffer = buffer;
+ }
+ }
+ } else {
+ Log.e(TAG, "no ring buffers found");
+ }
+
+ return mRingBuffers != null;
+ }
+
+ private boolean startLoggingAllExceptPerPacketBuffers() {
+
+ if (mRingBuffers == null) {
+ if (DBG) Log.d(TAG, "No ring buffers to log anything!");
+ return false;
+ }
+
+ for (WifiNative.RingBufferStatus buffer : mRingBuffers){
+
+ if ((buffer.flag & RING_BUFFER_FLAG_HAS_PER_PACKET_ENTRIES) != 0) {
+ /* skip per-packet-buffer */
+ if (DBG) Log.d(TAG, "skipped per packet logging ring " + buffer.name);
+ continue;
+ }
+
+ startLoggingRingBuffer(buffer);
+ }
+
+ return true;
+ }
+
+ private boolean startLoggingRingBuffer(WifiNative.RingBufferStatus buffer) {
+
+ int minInterval = MinWakeupIntervals[mLogLevel];
+ int minDataSize = MinBufferSizes[mLogLevel];
+
+ if (WifiNative.startLoggingRingBuffer(
+ mLogLevel, 0, minInterval, minDataSize, buffer.name) == false) {
+ if (DBG) Log.e(TAG, "Could not start logging ring " + buffer.name);
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean stopLoggingRingBuffer(WifiNative.RingBufferStatus buffer) {
+ if (WifiNative.startLoggingRingBuffer(0, 0, 0, 0, buffer.name) == false) {
+ if (DBG) Log.e(TAG, "Could not stop logging ring " + buffer.name);
+ }
+ return true;
+ }
+
+ private boolean stopLoggingAllBuffers() {
+ if (mRingBuffers != null) {
+ for (WifiNative.RingBufferStatus buffer : mRingBuffers) {
+ stopLoggingRingBuffer(buffer);
+ }
+ }
+ return true;
+ }
+
+ private boolean getAllRingBufferData() {
+ if (mRingBuffers == null) {
+ Log.e(TAG, "Not ring buffers available to collect data!");
+ return false;
+ }
+
+ for (WifiNative.RingBufferStatus element : mRingBuffers){
+ boolean result = WifiNative.getRingBufferData(element.name);
+ if (!result) {
+ Log.e(TAG, "Fail to get ring buffer data of: " + element.name);
+ return false;
+ }
+ }
+
+ Log.d(TAG, "getAllRingBufferData Successfully!");
+ return true;
+ }
+
+ private BugReport captureBugreport(int errorCode, boolean captureFWDump) {
+ BugReport report = new BugReport();
+ report.errorCode = errorCode;
+ report.systemTimeMs = System.currentTimeMillis();
+ report.kernelTimeNanos = System.nanoTime();
+
+ if (mRingBuffers != null) {
+ for (WifiNative.RingBufferStatus buffer : mRingBuffers) {
+ /* this will push data in mRingBuffers */
+ WifiNative.getRingBufferData(buffer.name);
+ LimitedCircularArray<byte[]> data = mRingBufferData.get(buffer.name);
+ byte[][] buffers = new byte[data.size()][];
+ for (int i = 0; i < data.size(); i++) {
+ buffers[i] = data.get(i).clone();
+ }
+ report.ringBuffers.put(buffer.name, buffers);
+ }
+ }
+
+ if (captureFWDump) {
+ report.fwMemoryDump = WifiNative.getFwMemoryDump();
+ }
+ return report;
+ }
+
+ private static String compressToBase64(byte[] input) {
+ String result;
+ //compress
+ Deflater compressor = new Deflater();
+ compressor.setLevel(Deflater.BEST_COMPRESSION);
+ compressor.setInput(input);
+ compressor.finish();
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(input.length);
+ final byte[] buf = new byte[1024];
+
+ while (!compressor.finished()) {
+ int count = compressor.deflate(buf);
+ bos.write(buf, 0, count);
+ }
+
+ try {
+ compressor.end();
+ bos.close();
+ } catch (IOException e) {
+ Log.e(TAG, "ByteArrayOutputStream close error");
+ result = android.util.Base64.encodeToString(input, Base64.DEFAULT);
+ return result;
+ }
+
+ byte[] compressed = bos.toByteArray();
+ if (DBG) {
+ Log.d(TAG," length is:" + (compressed == null? "0" : compressed.length));
+ }
+
+ //encode
+ result = android.util.Base64.encodeToString(
+ compressed.length < input.length ? compressed : input , Base64.DEFAULT);
+
+ if (DBG) {
+ Log.d(TAG, "FwMemoryDump length is :" + result.length());
+ }
+
+ return result;
+ }
+}
diff --git a/service/java/com/android/server/wifi/WifiMonitor.java b/service/java/com/android/server/wifi/WifiMonitor.java
index b807a92..9758d57 100644
--- a/service/java/com/android/server/wifi/WifiMonitor.java
+++ b/service/java/com/android/server/wifi/WifiMonitor.java
@@ -27,10 +27,11 @@
import android.net.wifi.p2p.WifiP2pProvDiscEvent;
import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
import android.os.Message;
-import android.os.SystemClock;
import android.text.TextUtils;
+import android.util.LocalLog;
import android.util.Log;
+import com.android.server.wifi.hotspot2.Utils;
import com.android.server.wifi.p2p.WifiP2pServiceImpl.P2pStatus;
import com.android.internal.util.Protocol;
@@ -66,9 +67,10 @@
private static final int ASSOC_REJECT = 9;
private static final int SSID_TEMP_DISABLE = 10;
private static final int SSID_REENABLE = 11;
- private static final int BSS_ADDED = 12;
- private static final int BSS_REMOVED = 13;
+ private static final int BSS_ADDED = 12;
+ private static final int BSS_REMOVED = 13;
private static final int UNKNOWN = 14;
+ private static final int SCAN_FAILED = 15;
/** All events coming from the supplicant start with this prefix */
private static final String EVENT_PREFIX_STR = "CTRL-EVENT-";
@@ -158,6 +160,13 @@
/**
* <pre>
+ * CTRL-EVENT-SCAN-FAILED ret=code[ retry=1]
+ * </pre>
+ */
+ private static final String SCAN_FAILED_STR = "SCAN-FAILED";
+
+ /**
+ * <pre>
* CTRL-EVENT-LINK-SPEED x Mb/s
* </pre>
* {@code x} is the link speed in Mb/sec.
@@ -189,6 +198,10 @@
*/
private static final String EAP_AUTH_FAILURE_STR = "EAP authentication failed";
+ /* EAP authentication timeout events */
+ private static final String AUTH_EVENT_PREFIX_STR = "Authentication with";
+ private static final String AUTH_TIMEOUT_STR = "timed out.";
+
/**
* This indicates an assoc reject event
*/
@@ -263,7 +276,7 @@
Pattern.compile("Associated with ((?:[0-9a-f]{2}:){5}[0-9a-f]{2}).*");
/**
- * Regex pattern for extracting SSIDs from request identity string.
+ * Regex pattern for extracting an external GSM sim authentication request from a string.
* Matches a strings like the following:<pre>
* CTRL-REQ-SIM-<network id>:GSM-AUTH:<RAND1>:<RAND2>[:<RAND3>] needed for SSID <SSID>
* This pattern should find
@@ -277,6 +290,19 @@
Pattern.compile("SIM-([0-9]*):GSM-AUTH((:[0-9a-f]+)+) needed for SSID (.+)");
/**
+ * Regex pattern for extracting an external 3G sim authentication request from a string.
+ * Matches a strings like the following:<pre>
+ * CTRL-REQ-SIM-<network id>:UMTS-AUTH:<RAND>:<AUTN> needed for SSID <SSID>
+ * This pattern should find
+ * 1 - id
+ * 2 - Rand
+ * 3 - Autn
+ * 4 - SSID
+ */
+ private static Pattern mRequestUmtsAuthPattern =
+ Pattern.compile("SIM-([0-9]*):UMTS-AUTH:([0-9a-f]+):([0-9a-f]+) needed for SSID (.+)");
+
+ /**
* Regex pattern for extracting SSIDs from request identity string.
* Matches a strings like the following:<pre>
* CTRL-REQ-IDENTITY-xx:Identity needed for SSID XXXX</pre>
@@ -396,6 +422,7 @@
private static final String AP_STA_CONNECTED_STR = "AP-STA-CONNECTED";
/* AP-STA-DISCONNECTED 42:fc:89:a8:96:09 */
private static final String AP_STA_DISCONNECTED_STR = "AP-STA-DISCONNECTED";
+ private static final String ANQP_DONE_STR = "ANQP-QUERY-DONE";
/* Supplicant events reported to a state machine */
private static final int BASE = Protocol.BASE_WIFI_MONITOR;
@@ -436,6 +463,8 @@
/* Request SIM Auth */
public static final int SUP_REQUEST_SIM_AUTH = BASE + 16;
+ public static final int SCAN_FAILED_EVENT = BASE + 17;
+
/* P2P events */
public static final int P2P_DEVICE_FOUND_EVENT = BASE + 21;
public static final int P2P_DEVICE_LOST_EVENT = BASE + 22;
@@ -462,6 +491,7 @@
/* Indicates assoc reject event */
public static final int ASSOCIATION_REJECTION_EVENT = BASE + 43;
+ public static final int ANQP_DONE_EVENT = BASE + 44;
/* hotspot 2.0 ANQP events */
public static final int GAS_QUERY_START_EVENT = BASE + 51;
@@ -569,8 +599,8 @@
if (mWifiNative.connectToSupplicant()) {
m.mMonitoring = true;
m.mStateMachine.sendMessage(SUP_CONNECTION_EVENT);
- new MonitorThread(mWifiNative, this).start();
mConnected = true;
+ new MonitorThread(mWifiNative, this).start();
break;
}
if (connectTries++ < 5) {
@@ -579,7 +609,6 @@
} catch (InterruptedException ignore) {
}
} else {
- mIfaceMap.remove(iface);
m.mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
Log.e(TAG, "startMonitoring(" + iface + ") failed!");
break;
@@ -633,6 +662,7 @@
private synchronized boolean dispatchEvent(String eventStr) {
String iface;
+ // IFNAME=wlan0 ANQP-QUERY-DONE addr=18:cf:5e:26:a4:88 result=SUCCESS
if (eventStr.startsWith("IFNAME=")) {
int space = eventStr.indexOf(' ');
if (space != -1) {
@@ -705,6 +735,7 @@
private static class MonitorThread extends Thread {
private final WifiNative mWifiNative;
private final WifiMonitorSingleton mWifiMonitorSingleton;
+ private final LocalLog mLocalLog = WifiNative.getLocalLog();
public MonitorThread(WifiNative wifiNative, WifiMonitorSingleton wifiMonitorSingleton) {
super("WifiMonitor");
@@ -713,13 +744,23 @@
}
public void run() {
+ if (DBG) {
+ Log.d(TAG, "MonitorThread start with mConnected=" +
+ mWifiMonitorSingleton.mConnected);
+ }
//noinspection InfiniteLoopStatement
for (;;) {
+ if (!mWifiMonitorSingleton.mConnected) {
+ if (DBG) Log.d(TAG, "MonitorThread exit because mConnected is false");
+ break;
+ }
String eventStr = mWifiNative.waitForEvent();
- // Skip logging the common but mostly uninteresting scan-results event
- if (DBG && eventStr.indexOf(SCAN_RESULTS_STR) == -1) {
- Log.d(TAG, "Event [" + eventStr + "]");
+ // Skip logging the common but mostly uninteresting events
+ if (eventStr.indexOf(BSS_ADDED_STR) == -1
+ && eventStr.indexOf(BSS_REMOVED_STR) == -1) {
+ if (DBG) Log.d(TAG, "Event [" + eventStr + "]");
+ mLocalLog.log("Event [" + eventStr + "]");
}
if (mWifiMonitorSingleton.dispatchEvent(eventStr)) {
@@ -764,13 +805,20 @@
handleP2pEvents(eventStr);
} else if (eventStr.startsWith(HOST_AP_EVENT_PREFIX_STR)) {
handleHostApEvents(eventStr);
- } else if (eventStr.startsWith(GAS_QUERY_PREFIX_STR)) {
+ } else if (eventStr.startsWith(ANQP_DONE_STR)) {
+ try {
+ handleAnqpResult(eventStr);
+ }
+ catch (IllegalArgumentException iae) {
+ Log.e(TAG, "Bad ANQP event string: '" + eventStr + "': " + iae);
+ }
+ } else if (eventStr.startsWith(GAS_QUERY_PREFIX_STR)) { // !!! clean >>End
handleGasQueryEvents(eventStr);
} else if (eventStr.startsWith(RX_HS20_ANQP_ICON_STR)) {
if (mStateMachine2 != null)
mStateMachine2.sendMessage(RX_HS20_ANQP_ICON_EVENT,
eventStr.substring(RX_HS20_ANQP_ICON_STR_LEN + 1));
- } else if (eventStr.startsWith(HS20_PREFIX_STR)) {
+ } else if (eventStr.startsWith(HS20_PREFIX_STR)) { // !!! <<End
handleHs20Events(eventStr);
} else if (eventStr.startsWith(REQUEST_PREFIX_STR)) {
handleRequests(eventStr);
@@ -778,6 +826,9 @@
handleTargetBSSIDEvent(eventStr);
} else if (eventStr.startsWith(ASSOCIATED_WITH_STR)) {
handleAssociatedBSSIDEvent(eventStr);
+ } else if (eventStr.startsWith(AUTH_EVENT_PREFIX_STR) &&
+ eventStr.endsWith(AUTH_TIMEOUT_STR)) {
+ mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT);
} else {
if (DBG) Log.w(TAG, "couldn't identify event type - " + eventStr);
}
@@ -806,6 +857,8 @@
event = STATE_CHANGE;
else if (eventName.equals(SCAN_RESULTS_STR))
event = SCAN_RESULTS;
+ else if (eventName.equals(SCAN_FAILED_STR))
+ event = SCAN_FAILED;
else if (eventName.equals(LINK_SPEED_STR))
event = LINK_SPEED;
else if (eventName.equals(TERMINATING_STR))
@@ -824,8 +877,7 @@
event = BSS_ADDED;
} else if (eventName.equals(BSS_REMOVED_STR)) {
event = BSS_REMOVED;
- }
- else
+ } else
event = UNKNOWN;
String eventData = eventStr;
@@ -958,6 +1010,10 @@
mStateMachine.sendMessage(SCAN_RESULTS_EVENT);
break;
+ case SCAN_FAILED:
+ mStateMachine.sendMessage(SCAN_FAILED_EVENT);
+ break;
+
case UNKNOWN:
if (DBG) {
logDbg("handleEvent unknown: " + Integer.toString(event) + " " + remainder);
@@ -1053,14 +1109,34 @@
return err;
}
+ WifiP2pDevice getWifiP2pDevice(String dataString) {
+ try {
+ WifiP2pDevice device = new WifiP2pDevice(dataString);
+ return device;
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ WifiP2pGroup getWifiP2pGroup(String dataString) {
+ try {
+ WifiP2pGroup group = new WifiP2pGroup(dataString);
+ return group;
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
/**
* Handle p2p events
*/
private void handleP2pEvents(String dataString) {
if (dataString.startsWith(P2P_DEVICE_FOUND_STR)) {
- mStateMachine.sendMessage(P2P_DEVICE_FOUND_EVENT, new WifiP2pDevice(dataString));
+ WifiP2pDevice device = getWifiP2pDevice(dataString);
+ if (device != null) mStateMachine.sendMessage(P2P_DEVICE_FOUND_EVENT, device);
} else if (dataString.startsWith(P2P_DEVICE_LOST_STR)) {
- mStateMachine.sendMessage(P2P_DEVICE_LOST_EVENT, new WifiP2pDevice(dataString));
+ WifiP2pDevice device = getWifiP2pDevice(dataString);
+ if (device != null) mStateMachine.sendMessage(P2P_DEVICE_LOST_EVENT, device);
} else if (dataString.startsWith(P2P_FIND_STOPPED_STR)) {
mStateMachine.sendMessage(P2P_FIND_STOPPED_EVENT);
} else if (dataString.startsWith(P2P_GO_NEG_REQUEST_STR)) {
@@ -1075,9 +1151,11 @@
} else if (dataString.startsWith(P2P_GROUP_FORMATION_FAILURE_STR)) {
mStateMachine.sendMessage(P2P_GROUP_FORMATION_FAILURE_EVENT, p2pError(dataString));
} else if (dataString.startsWith(P2P_GROUP_STARTED_STR)) {
- mStateMachine.sendMessage(P2P_GROUP_STARTED_EVENT, new WifiP2pGroup(dataString));
+ WifiP2pGroup group = getWifiP2pGroup(dataString);
+ if (group != null) mStateMachine.sendMessage(P2P_GROUP_STARTED_EVENT, group);
} else if (dataString.startsWith(P2P_GROUP_REMOVED_STR)) {
- mStateMachine.sendMessage(P2P_GROUP_REMOVED_EVENT, new WifiP2pGroup(dataString));
+ WifiP2pGroup group = getWifiP2pGroup(dataString);
+ if (group != null) mStateMachine.sendMessage(P2P_GROUP_REMOVED_EVENT, group);
} else if (dataString.startsWith(P2P_INVITATION_RECEIVED_STR)) {
mStateMachine.sendMessage(P2P_INVITATION_RECEIVED_EVENT,
new WifiP2pGroup(dataString));
@@ -1121,6 +1199,38 @@
}
}
+ private static final String ADDR_STRING = "addr=";
+ private static final String RESULT_STRING = "result=";
+
+ // ANQP-QUERY-DONE addr=18:cf:5e:26:a4:88 result=SUCCESS
+
+ private void handleAnqpResult(String eventStr) {
+ int addrPos = eventStr.indexOf(ADDR_STRING);
+ int resPos = eventStr.indexOf(RESULT_STRING);
+ if (addrPos < 0 || resPos < 0) {
+ throw new IllegalArgumentException("Unexpected ANQP result notification");
+ }
+ int eoaddr = eventStr.indexOf(' ', addrPos + ADDR_STRING.length());
+ if (eoaddr < 0) {
+ eoaddr = eventStr.length();
+ }
+ int eoresult = eventStr.indexOf(' ', resPos + RESULT_STRING.length());
+ if (eoresult < 0) {
+ eoresult = eventStr.length();
+ }
+
+ try {
+ long bssid = Utils.parseMac(eventStr.substring(addrPos + ADDR_STRING.length(), eoaddr));
+ int result = eventStr.substring(
+ resPos + RESULT_STRING.length(), eoresult).equalsIgnoreCase("success") ? 1 : 0;
+
+ mStateMachine.sendMessage(ANQP_DONE_EVENT, result, 0, bssid);
+ }
+ catch (IllegalArgumentException iae) {
+ Log.e(TAG, "Bad MAC address in ANQP response: " + iae.getMessage());
+ }
+ }
+
/**
* Handle ANQP events
*/
@@ -1208,15 +1318,24 @@
Log.e(TAG, "didn't find SSID " + requestName);
}
mStateMachine.sendMessage(SUP_REQUEST_IDENTITY, eventLogCounter, reason, SSID);
- } if (requestName.startsWith(SIM_STR)) {
- Matcher match = mRequestGsmAuthPattern.matcher(requestName);
- if (match.find()) {
- WifiStateMachine.SimAuthRequestData data =
- new WifiStateMachine.SimAuthRequestData();
- data.networkId = Integer.parseInt(match.group(1));
+ } else if (requestName.startsWith(SIM_STR)) {
+ Matcher matchGsm = mRequestGsmAuthPattern.matcher(requestName);
+ Matcher matchUmts = mRequestUmtsAuthPattern.matcher(requestName);
+ WifiStateMachine.SimAuthRequestData data =
+ new WifiStateMachine.SimAuthRequestData();
+ if (matchGsm.find()) {
+ data.networkId = Integer.parseInt(matchGsm.group(1));
data.protocol = WifiEnterpriseConfig.Eap.SIM;
- data.ssid = match.group(4);
- data.challenges = match.group(2).split(":");
+ data.ssid = matchGsm.group(4);
+ data.data = matchGsm.group(2).split(":");
+ mStateMachine.sendMessage(SUP_REQUEST_SIM_AUTH, data);
+ } else if (matchUmts.find()) {
+ data.networkId = Integer.parseInt(matchUmts.group(1));
+ data.protocol = WifiEnterpriseConfig.Eap.AKA;
+ data.ssid = matchUmts.group(4);
+ data.data = new String[2];
+ data.data[0] = matchUmts.group(2);
+ data.data[1] = matchUmts.group(3);
mStateMachine.sendMessage(SUP_REQUEST_SIM_AUTH, data);
} else {
Log.e(TAG, "couldn't parse SIM auth request - " + requestName);
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index b95db28..899529d 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -19,21 +19,35 @@
import android.net.wifi.BatchedScanSettings;
import android.net.wifi.RttManager;
import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiLinkLayerStats;
+import android.net.wifi.WifiManager;
import android.net.wifi.WifiScanner;
+import android.net.wifi.RttManager;
+import android.net.wifi.WifiSsid;
import android.net.wifi.WpsInfo;
import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
+import android.net.wifi.WifiEnterpriseConfig;
import android.os.SystemClock;
import android.text.TextUtils;
-import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
+import android.util.Base64;
import android.util.LocalLog;
import android.util.Log;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
-
+import java.util.zip.Deflater;
+import libcore.util.HexEncoding;
/**
* Native calls for bring up/shut down of the supplicant daemon and for
* sending requests to the supplicant daemon
@@ -65,6 +79,10 @@
private boolean mSuspendOptEnabled = false;
+ private static final int EID_HT_OPERATION = 61;
+ private static final int EID_VHT_OPERATION = 192;
+ private static final int EID_EXTENDED_CAPS = 127;
+ private static final int RTT_RESP_ENABLE_BIT = 70;
/* Register native functions */
static {
@@ -122,12 +140,12 @@
}
}
- private static final LocalLog mLocalLog = new LocalLog(1024);
+ private static final LocalLog mLocalLog = new LocalLog(16384);
// hold mLock before accessing mCmdIdLock
private static int sCmdId;
- public LocalLog getLocalLog() {
+ public static LocalLog getLocalLog() {
return mLocalLog;
}
@@ -171,6 +189,16 @@
}
}
+ private boolean doBooleanCommandWithoutLogging(String command) {
+ if (DBG) Log.d(mTAG, "doBooleanCommandWithoutLogging: " + command);
+ synchronized (mLock) {
+ int cmdId = getNewCmdIdLocked();
+ boolean result = doBooleanCommandNative(mInterfacePrefix + command);
+ if (DBG) Log.d(mTAG, command + ": returned " + result);
+ return result;
+ }
+ }
+
private int doIntCommand(String command) {
if (DBG) Log.d(mTAG, "doInt: " + command);
synchronized (mLock) {
@@ -267,7 +295,12 @@
public boolean setNetworkVariable(int netId, String name, String value) {
if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) return false;
- return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value);
+ if (name.equals(WifiConfiguration.pskVarName)
+ || name.equals(WifiEnterpriseConfig.PASSWORD_KEY)) {
+ return doBooleanCommandWithoutLogging("SET_NETWORK " + netId + " " + name + " " + value);
+ } else {
+ return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value);
+ }
}
public String getNetworkVariable(int netId, String name) {
@@ -308,6 +341,11 @@
return doBooleanCommand("DISABLE_NETWORK " + netId);
}
+ public boolean selectNetwork(int netId) {
+ if (DBG) logDbg("selectNetwork nid=" + Integer.toString(netId));
+ return doBooleanCommand("SELECT_NETWORK " + netId);
+ }
+
public boolean reconnect() {
if (DBG) logDbg("RECONNECT ");
return doBooleanCommand("RECONNECT");
@@ -345,6 +383,8 @@
return null;
}
+
+
/**
* Format of results:
* =================
@@ -361,9 +401,29 @@
* RANGE=ALL gets all scan results
* RANGE=ID- gets results from ID
* MASK=<N> see wpa_supplicant/src/common/wpa_ctrl.h for details
+ * 0 0 1 0 2
+ * WPA_BSS_MASK_MESH_SCAN | WPA_BSS_MASK_DELIM | WPA_BSS_MASK_WIFI_DISPLAY
+ * 0 0 0 1 1 -> 9
+ * WPA_BSS_MASK_INTERNETW | WPA_BSS_MASK_P2P_SCAN | WPA_BSS_MASK_WPS_SCAN | WPA_BSS_MASK_SSID
+ * 1 0 0 1 9 -> d
+ * WPA_BSS_MASK_FLAGS | WPA_BSS_MASK_IE | WPA_BSS_MASK_AGE | WPA_BSS_MASK_TSF
+ * 1 0 0 0 8
+ * WPA_BSS_MASK_LEVEL | WPA_BSS_MASK_NOISE | WPA_BSS_MASK_QUAL | WPA_BSS_MASK_CAPABILITIES
+ * 0 1 1 1 7
+ * WPA_BSS_MASK_BEACON_INT | WPA_BSS_MASK_FREQ | WPA_BSS_MASK_BSSID | WPA_BSS_MASK_ID
+ *
+ * WPA_BSS_MASK_INTERNETW adds ANQP info (ctrl_iface:4151-4176)
+ *
+ * ctrl_iface.c:wpa_supplicant_ctrl_iface_process:7884
+ * wpa_supplicant_ctrl_iface_bss:4315
+ * print_bss_info
*/
public String scanResults(int sid) {
- return doStringCommandWithoutLogging("BSS RANGE=" + sid + "- MASK=0x21987");
+ return doStringCommandWithoutLogging("BSS RANGE=" + sid + "- MASK=0x29d87");
+ }
+
+ public String doCustomCommand(String command) {
+ return doStringCommand(command);
}
/**
@@ -510,22 +570,24 @@
&& doBooleanCommand("DRIVER RXFILTER-START");
}
- public int getBand() {
- String ret = doStringCommand("DRIVER GETBAND");
- if (!TextUtils.isEmpty(ret)) {
- //reply is "BAND X" where X is the band
- String[] tokens = ret.split(" ");
- try {
- if (tokens.length == 2) return Integer.parseInt(tokens[1]);
- } catch (NumberFormatException e) {
- return -1;
- }
- }
- return -1;
- }
-
+ /**
+ * Set the operational frequency band
+ * @param band One of
+ * {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO},
+ * {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ},
+ * {@link WifiManager#WIFI_FREQUENCY_BAND_2GHZ},
+ * @return {@code true} if the operation succeeded, {@code false} otherwise
+ */
public boolean setBand(int band) {
- return doBooleanCommand("DRIVER SETBAND " + band);
+ String bandstr;
+
+ if (band == WifiManager.WIFI_FREQUENCY_BAND_5GHZ)
+ bandstr = "5G";
+ else if (band == WifiManager.WIFI_FREQUENCY_BAND_2GHZ)
+ bandstr = "2G";
+ else
+ bandstr = "AUTO";
+ return doBooleanCommand("SET SETBAND " + bandstr);
}
/**
@@ -586,15 +648,20 @@
}
public boolean setCountryCode(String countryCode) {
- return doBooleanCommand("DRIVER COUNTRY " + countryCode.toUpperCase(Locale.ROOT));
+ if (countryCode != null)
+ return doBooleanCommand("DRIVER COUNTRY " + countryCode.toUpperCase(Locale.ROOT));
+ else
+ return doBooleanCommand("DRIVER COUNTRY");
}
- public void enableBackgroundScan(boolean enable) {
+ public boolean enableBackgroundScan(boolean enable) {
+ boolean ret;
if (enable) {
- doBooleanCommand("SET pno 1");
+ ret = doBooleanCommand("SET pno 1");
} else {
- doBooleanCommand("SET pno 0");
+ ret = doBooleanCommand("SET pno 0");
}
+ return ret;
}
public void enableAutoConnect(boolean enable) {
@@ -697,9 +764,16 @@
}
}
- public boolean simAuthResponse(int id, String response) {
+ public boolean simAuthResponse(int id, String type, String response) {
+ // with type = GSM-AUTH, UMTS-AUTH or UMTS-AUTS
synchronized (mLock) {
- return doBooleanCommand("CTRL-RSP-SIM-" + id + ":GSM-AUTH" + response);
+ return doBooleanCommand("CTRL-RSP-SIM-" + id + ":" + type + response);
+ }
+ }
+
+ public boolean simIdentityResponse(int id, String response) {
+ synchronized (mLock) {
+ return doBooleanCommand("CTRL-RSP-IDENTITY-" + id + ":" + response);
}
}
@@ -1132,379 +1206,6 @@
return doBooleanCommand("ANQP_GET " + bssid + " " + subtypes);
}
- /* WIFI HAL support */
-
- private static final String TAG = "WifiNative-HAL";
- private static long sWifiHalHandle = 0; /* used by JNI to save wifi_handle */
- private static long[] sWifiIfaceHandles = null; /* used by JNI to save interface handles */
- private static int sWlan0Index = -1;
- private static int sP2p0Index = -1;
-
- private static boolean sHalIsStarted = false;
- private static boolean sHalFailed = false;
-
- private static native boolean startHalNative();
- private static native void stopHalNative();
- private static native void waitForHalEventNative();
-
- private static class MonitorThread extends Thread {
- public void run() {
- Log.i(TAG, "Waiting for HAL events mWifiHalHandle=" + Long.toString(sWifiHalHandle));
- waitForHalEventNative();
- }
- }
-
- synchronized public static boolean startHal() {
- Log.i(TAG, "startHal");
- synchronized (mLock) {
- if (sHalIsStarted)
- return true;
- if (sHalFailed)
- return false;
- if (startHalNative() && (getInterfaces() != 0) && (sWlan0Index != -1)) {
- new MonitorThread().start();
- sHalIsStarted = true;
- return true;
- } else {
- Log.i(TAG, "Could not start hal");
- sHalIsStarted = false;
- sHalFailed = true;
- return false;
- }
- }
- }
-
- synchronized public static void stopHal() {
- stopHalNative();
- }
-
- private static native int getInterfacesNative();
-
- synchronized public static int getInterfaces() {
- synchronized (mLock) {
- if (sWifiIfaceHandles == null) {
- int num = getInterfacesNative();
- int wifi_num = 0;
- for (int i = 0; i < num; i++) {
- String name = getInterfaceNameNative(i);
- Log.i(TAG, "interface[" + i + "] = " + name);
- if (name.equals("wlan0")) {
- sWlan0Index = i;
- wifi_num++;
- } else if (name.equals("p2p0")) {
- sP2p0Index = i;
- wifi_num++;
- }
- }
- return wifi_num;
- } else {
- return sWifiIfaceHandles.length;
- }
- }
- }
-
- private static native String getInterfaceNameNative(int index);
- synchronized public static String getInterfaceName(int index) {
- return getInterfaceNameNative(index);
- }
-
- 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;
- }
-
- public static boolean getScanCapabilities(ScanCapabilities capabilities) {
- return getScanCapabilitiesNative(sWlan0Index, capabilities);
- }
-
- private static native boolean getScanCapabilitiesNative(
- int iface, ScanCapabilities capabilities);
-
- private static native boolean startScanNative(int iface, int id, ScanSettings settings);
- private static native boolean stopScanNative(int iface, int id);
- private static native ScanResult[] getScanResultsNative(int iface, boolean flush);
- private static native WifiLinkLayerStats getWifiLinkLayerStatsNative(int iface);
-
- 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[];
- }
-
- public static class ScanSettings {
- int base_period_ms;
- int max_ap_per_scan;
- int report_threshold;
- int num_buckets;
- BucketSettings buckets[];
- }
-
- public static interface ScanEventHandler {
- void onScanResultsAvailable();
- void onFullScanResult(ScanResult fullScanResult);
- void onSingleScanComplete();
- void onScanPaused();
- void onScanRestarted();
- }
-
- synchronized static void onScanResultsAvailable(int id) {
- if (sScanEventHandler != null) {
- sScanEventHandler.onScanResultsAvailable();
- }
- }
-
- /* scan status, keep these values in sync with gscan.h */
- private static int WIFI_SCAN_BUFFER_FULL = 0;
- private static int WIFI_SCAN_COMPLETE = 1;
-
- synchronized static void onScanStatus(int status) {
- Log.i(TAG, "Got a scan status changed event, status = " + status);
-
- if (status == WIFI_SCAN_BUFFER_FULL) {
- /* we have a separate event to take care of this */
- } else if (status == WIFI_SCAN_COMPLETE) {
- if (sScanEventHandler != null) {
- sScanEventHandler.onSingleScanComplete();
- }
- }
- }
-
- synchronized static void onFullScanResult(int id, ScanResult result, byte bytes[]) {
- if (DBG) Log.i(TAG, "Got a full scan results event, ssid = " + result.SSID + ", " +
- "num = " + bytes.length);
-
- if (sScanEventHandler == null) {
- return;
- }
-
- int num = 0;
- for (int i = 0; i < bytes.length; ) {
- int type = bytes[i] & 0xFF;
- int len = bytes[i + 1] & 0xFF;
-
- if (i + len + 2 > bytes.length) {
- Log.w(TAG, "bad length " + len + " of IE " + type + " from " + result.BSSID);
- Log.w(TAG, "ignoring the rest of the IEs");
- break;
- }
- num++;
- i += len + 2;
- if (DBG) Log.i(TAG, "bytes[" + i + "] = [" + type + ", " + len + "]" + ", " +
- "next = " + i);
- }
-
- ScanResult.InformationElement elements[] = new ScanResult.InformationElement[num];
- for (int i = 0, index = 0; i < num; i++) {
- int type = bytes[index] & 0xFF;
- int len = bytes[index + 1] & 0xFF;
- if (DBG) Log.i(TAG, "index = " + index + ", type = " + type + ", len = " + len);
- ScanResult.InformationElement elem = new ScanResult.InformationElement();
- elem.id = type;
- elem.bytes = new byte[len];
- for (int j = 0; j < len; j++) {
- elem.bytes[j] = bytes[index + j + 2];
- }
- elements[i] = elem;
- index += (len + 2);
- }
-
- result.informationElements = elements;
- sScanEventHandler.onFullScanResult(result);
- }
-
- private static int sScanCmdId = 0;
- private static ScanEventHandler sScanEventHandler;
- private static ScanSettings sScanSettings;
-
- synchronized public static boolean startScan(
- ScanSettings settings, ScanEventHandler eventHandler) {
- synchronized (mLock) {
-
- if (sScanCmdId != 0) {
- stopScan();
- } else if (sScanSettings != null || sScanEventHandler != null) {
- /* current scan is paused; no need to stop it */
- }
-
- sScanCmdId = getNewCmdIdLocked();
-
- sScanSettings = settings;
- sScanEventHandler = eventHandler;
-
- if (startScanNative(sWlan0Index, sScanCmdId, settings) == false) {
- sScanEventHandler = null;
- sScanSettings = null;
- return false;
- }
-
- return true;
- }
- }
-
- synchronized public static void stopScan() {
- synchronized (mLock) {
- stopScanNative(sWlan0Index, sScanCmdId);
- sScanSettings = null;
- sScanEventHandler = null;
- sScanCmdId = 0;
- }
- }
-
- synchronized public static void pauseScan() {
- synchronized (mLock) {
- if (sScanCmdId != 0 && sScanSettings != null && sScanEventHandler != null) {
- Log.d(TAG, "Pausing scan");
- stopScanNative(sWlan0Index, sScanCmdId);
- sScanCmdId = 0;
- sScanEventHandler.onScanPaused();
- }
- }
- }
-
- synchronized public static void restartScan() {
- synchronized (mLock) {
- if (sScanCmdId == 0 && sScanSettings != null && sScanEventHandler != null) {
- Log.d(TAG, "Restarting scan");
- startScan(sScanSettings, sScanEventHandler);
- sScanEventHandler.onScanRestarted();
- }
- }
- }
-
- synchronized public static ScanResult[] getScanResults() {
- synchronized (mLock) {
- return getScanResultsNative(sWlan0Index, /* flush = */ false);
- }
- }
-
- public static interface HotlistEventHandler {
- void onHotlistApFound (ScanResult[]result);
- }
-
- private static int sHotlistCmdId = 0;
- private static HotlistEventHandler sHotlistEventHandler;
-
- private native static boolean setHotlistNative(int iface, int id,
- WifiScanner.HotlistSettings settings);
- private native static boolean resetHotlistNative(int iface, int id);
-
- synchronized public static boolean setHotlist(WifiScanner.HotlistSettings settings,
- HotlistEventHandler eventHandler) {
- synchronized (mLock) {
- if (sHotlistCmdId != 0) {
- return false;
- } else {
- sHotlistCmdId = getNewCmdIdLocked();
- }
-
- sHotlistEventHandler = eventHandler;
- if (setHotlistNative(sWlan0Index, sScanCmdId, settings) == false) {
- sHotlistEventHandler = null;
- return false;
- }
-
- return true;
- }
- }
-
- synchronized public static void resetHotlist() {
- synchronized (mLock) {
- if (sHotlistCmdId != 0) {
- resetHotlistNative(sWlan0Index, sHotlistCmdId);
- sHotlistCmdId = 0;
- sHotlistEventHandler = null;
- }
- }
- }
-
- synchronized public static void onHotlistApFound(int id, ScanResult[] results) {
- synchronized (mLock) {
- if (sHotlistCmdId != 0) {
- sHotlistEventHandler.onHotlistApFound(results);
- } else {
- /* this can happen because of race conditions */
- Log.d(TAG, "Ignoring hotlist AP found change");
- }
- }
- }
-
- public static interface SignificantWifiChangeEventHandler {
- void onChangesFound(ScanResult[] result);
- }
-
- private static SignificantWifiChangeEventHandler sSignificantWifiChangeHandler;
- private static int sSignificantWifiChangeCmdId;
-
- private static native boolean trackSignificantWifiChangeNative(
- int iface, int id, WifiScanner.WifiChangeSettings settings);
- private static native boolean untrackSignificantWifiChangeNative(int iface, int id);
-
- synchronized public static boolean trackSignificantWifiChange(
- WifiScanner.WifiChangeSettings settings, SignificantWifiChangeEventHandler handler) {
- synchronized (mLock) {
- if (sSignificantWifiChangeCmdId != 0) {
- return false;
- } else {
- sSignificantWifiChangeCmdId = getNewCmdIdLocked();
- }
-
- sSignificantWifiChangeHandler = handler;
- if (trackSignificantWifiChangeNative(sWlan0Index, sScanCmdId, settings) == false) {
- sSignificantWifiChangeHandler = null;
- return false;
- }
-
- return true;
- }
- }
-
- synchronized static void untrackSignificantWifiChange() {
- synchronized (mLock) {
- if (sSignificantWifiChangeCmdId != 0) {
- untrackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId);
- sSignificantWifiChangeCmdId = 0;
- sSignificantWifiChangeHandler = null;
- }
- }
- }
-
- synchronized static void onSignificantWifiChange(int id, ScanResult[] results) {
- synchronized (mLock) {
- if (sSignificantWifiChangeCmdId != 0) {
- sSignificantWifiChangeHandler.onChangesFound(results);
- } else {
- /* this can happen because of race conditions */
- Log.d(TAG, "Ignoring significant wifi change");
- }
- }
- }
-
- synchronized public static WifiLinkLayerStats getWifiLinkLayerStats(String iface) {
- // TODO: use correct iface name to Index translation
- if (iface == null) return null;
- synchronized (mLock) {
- if (!sHalIsStarted)
- startHal();
- if (sHalIsStarted)
- return getWifiLinkLayerStatsNative(sWlan0Index);
- }
- return null;
- }
-
/*
* NFC-related calls
*/
@@ -1528,9 +1229,594 @@
return doBooleanCommand("NFC_REPORT_HANDOVER RESP P2P " + requestMessage + " 00");
}
+ /* WIFI HAL support */
+
+ private static final String TAG = "WifiNative-HAL";
+ private static long sWifiHalHandle = 0; /* used by JNI to save wifi_handle */
+ private static long[] sWifiIfaceHandles = null; /* used by JNI to save interface handles */
+ private static int sWlan0Index = -1;
+ private static int sP2p0Index = -1;
+ private static MonitorThread sThread;
+ private static final int STOP_HAL_TIMEOUT_MS = 1000;
+
+ private static native boolean startHalNative();
+ private static native void stopHalNative();
+ private static native void waitForHalEventNative();
+
+ private static class MonitorThread extends Thread {
+ public void run() {
+ Log.i(TAG, "Waiting for HAL events mWifiHalHandle=" + Long.toString(sWifiHalHandle));
+ waitForHalEventNative();
+ }
+ }
+
+ synchronized public static boolean startHal() {
+
+ String debugLog = "startHal stack: ";
+ java.lang.StackTraceElement[] elements = Thread.currentThread().getStackTrace();
+ for (int i = 2; i < elements.length && i <= 7; i++ ) {
+ debugLog = debugLog + " - " + elements[i].getMethodName();
+ }
+
+ mLocalLog.log(debugLog);
+
+ synchronized (mLock) {
+ if (startHalNative() && (getInterfaces() != 0) && (sWlan0Index != -1)) {
+ sThread = new MonitorThread();
+ sThread.start();
+ return true;
+ } else {
+ if (DBG) mLocalLog.log("Could not start hal");
+ Log.e(TAG, "Could not start hal");
+ return false;
+ }
+ }
+ }
+
+ synchronized public static void stopHal() {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ stopHalNative();
+ try {
+ sThread.join(STOP_HAL_TIMEOUT_MS);
+ Log.d(TAG, "HAL event thread stopped successfully");
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Could not stop HAL cleanly");
+ }
+ sThread = null;
+ sWifiHalHandle = 0;
+ sWifiIfaceHandles = null;
+ sWlan0Index = -1;
+ sP2p0Index = -1;
+ }
+ }
+ }
+
+ public static boolean isHalStarted() {
+ return (sWifiHalHandle != 0);
+ }
+ private static native int getInterfacesNative();
+
+ synchronized public static int getInterfaces() {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ if (sWifiIfaceHandles == null) {
+ int num = getInterfacesNative();
+ int wifi_num = 0;
+ for (int i = 0; i < num; i++) {
+ String name = getInterfaceNameNative(i);
+ Log.i(TAG, "interface[" + i + "] = " + name);
+ if (name.equals("wlan0")) {
+ sWlan0Index = i;
+ wifi_num++;
+ } else if (name.equals("p2p0")) {
+ sP2p0Index = i;
+ wifi_num++;
+ }
+ }
+ return wifi_num;
+ } else {
+ return sWifiIfaceHandles.length;
+ }
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ private static native String getInterfaceNameNative(int index);
+ synchronized public static String getInterfaceName(int index) {
+ return getInterfaceNameNative(index);
+ }
+
+ 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_bssids;
+ public int max_significant_wifi_change_aps;
+ }
+
+ synchronized public static boolean getScanCapabilities(ScanCapabilities capabilities) {
+ synchronized (mLock) {
+ return isHalStarted() && getScanCapabilitiesNative(sWlan0Index, capabilities);
+ }
+ }
+
+ private static native boolean getScanCapabilitiesNative(
+ int iface, ScanCapabilities capabilities);
+
+ private static native boolean startScanNative(int iface, int id, ScanSettings settings);
+ private static native boolean stopScanNative(int iface, int id);
+ private static native WifiScanner.ScanData[] getScanResultsNative(int iface, boolean flush);
+ private static native WifiLinkLayerStats getWifiLinkLayerStatsNative(int iface);
+ private static native void setWifiLinkLayerStatsNative(int iface, int enable);
+
+ 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[];
+ }
+
+ public static class ScanSettings {
+ int base_period_ms;
+ int max_ap_per_scan;
+ int report_threshold_percent;
+ int report_threshold_num_scans;
+ int num_buckets;
+ BucketSettings buckets[];
+ }
+
+ public static interface ScanEventHandler {
+ void onScanResultsAvailable();
+ void onFullScanResult(ScanResult fullScanResult);
+ void onScanStatus();
+ void onScanPaused(WifiScanner.ScanData[] data);
+ void onScanRestarted();
+ }
+
+ synchronized static void onScanResultsAvailable(int id) {
+ if (sScanEventHandler != null) {
+ sScanEventHandler.onScanResultsAvailable();
+ }
+ }
+
+ /* scan status, keep these values in sync with gscan.h */
+ private static int WIFI_SCAN_BUFFER_FULL = 0;
+ private static int WIFI_SCAN_COMPLETE = 1;
+
+ synchronized static void onScanStatus(int status) {
+ if (status == WIFI_SCAN_BUFFER_FULL) {
+ /* we have a separate event to take care of this */
+ } else if (status == WIFI_SCAN_COMPLETE) {
+ if (sScanEventHandler != null) {
+ sScanEventHandler.onScanStatus();
+ }
+ }
+ }
+
+ public static WifiSsid createWifiSsid (byte[] rawSsid) {
+ String ssidHexString = String.valueOf(HexEncoding.encode(rawSsid));
+
+ if (ssidHexString == null) {
+ return null;
+ }
+
+ WifiSsid wifiSsid = WifiSsid.createFromHex(ssidHexString);
+
+ return wifiSsid;
+ }
+
+ public static String ssidConvert(byte[] rawSsid) {
+ String ssid;
+
+ CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
+ try {
+ CharBuffer decoded = decoder.decode(ByteBuffer.wrap(rawSsid));
+ ssid = decoded.toString();
+ } catch (CharacterCodingException cce) {
+ ssid = null;
+ }
+
+ if (ssid == null) {
+ ssid = new String(rawSsid, StandardCharsets.ISO_8859_1);
+ }
+
+ return ssid;
+ }
+
+ public static boolean setSsid(byte[] rawSsid, ScanResult result) {
+ if (rawSsid == null || rawSsid.length == 0 || result == null) {
+ return false;
+ }
+
+ result.SSID = ssidConvert(rawSsid);
+ result.wifiSsid = createWifiSsid(rawSsid);
+ return true;
+ }
+
+ static void populateScanResult(ScanResult result, byte bytes[], String dbg) {
+ int num = 0;
+ if (bytes == null) return;
+ if (dbg == null) dbg = "";
+ for (int i = 0; i < bytes.length - 1; ) {
+ int type = bytes[i] & 0xFF;
+ int len = bytes[i + 1] & 0xFF;
+ if (i + len + 2 > bytes.length) {
+ Log.w(TAG, dbg + "bad length " + len + " of IE " + type + " from " + result.BSSID);
+ Log.w(TAG, dbg + "ignoring the rest of the IEs");
+ break;
+ }
+ num++;
+ if (DBG) Log.i(TAG, dbg + "bytes[" + i + "] = [" + type + ", " + len + "]" + ", " +
+ "next = " + (i + len + 2));
+ i += len + 2;
+ }
+
+ int secondChanelOffset = 0;
+ byte channelMode = 0;
+ byte centerFreqIndex1 = 0;
+ byte centerFreqIndex2 = 0;
+
+ boolean is80211McRTTResponder = false;
+
+ ScanResult.InformationElement elements[] = new ScanResult.InformationElement[num];
+ for (int i = 0, index = 0; i < num; i++) {
+ int type = bytes[index] & 0xFF;
+ int len = bytes[index + 1] & 0xFF;
+ if (DBG) Log.i(TAG, dbg + "index = " + index + ", type = " + type + ", len = " + len);
+ ScanResult.InformationElement elem = new ScanResult.InformationElement();
+ elem.id = type;
+ elem.bytes = new byte[len];
+ for (int j = 0; j < len; j++) {
+ elem.bytes[j] = bytes[index + j + 2];
+ }
+ elements[i] = elem;
+ int inforStart = index + 2;
+ index += (len + 2);
+
+ if(type == EID_HT_OPERATION) {
+ secondChanelOffset = bytes[inforStart + 1] & 0x3;
+ } else if(type == EID_VHT_OPERATION) {
+ channelMode = bytes[inforStart];
+ centerFreqIndex1 = bytes[inforStart + 1];
+ centerFreqIndex2 = bytes[inforStart + 2];
+ } else if (type == EID_EXTENDED_CAPS) {
+ int tempIndex = RTT_RESP_ENABLE_BIT / 8;
+ byte offset = RTT_RESP_ENABLE_BIT % 8;
+
+ if(len < tempIndex + 1) {
+ is80211McRTTResponder = false;
+ } else {
+ if ((bytes[inforStart + tempIndex] & ((byte)0x1 << offset)) != 0) {
+ is80211McRTTResponder = true;
+ } else {
+ is80211McRTTResponder = false;
+ }
+ }
+ }
+ }
+
+ if (is80211McRTTResponder) {
+ result.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
+ } else {
+ result.clearFlag(ScanResult.FLAG_80211mc_RESPONDER);
+ }
+
+ //handle RTT related information
+ if (channelMode != 0) {
+ // 80 or 160 MHz
+ result.channelWidth = channelMode + 1;
+
+ //convert channel index to frequency in MHz, channel 36 is 5180MHz
+ result.centerFreq0 = (centerFreqIndex1 - 36) * 5 + 5180;
+
+ if(channelMode > 1) { //160MHz
+ result.centerFreq1 = (centerFreqIndex2 - 36) * 5 + 5180;
+ } else {
+ result.centerFreq1 = 0;
+ }
+ } else {
+ //20 or 40 MHz
+ if (secondChanelOffset != 0) {//40MHz
+ result.channelWidth = 1;
+ if (secondChanelOffset == 1) {
+ result.centerFreq0 = result.frequency + 20;
+ } else if (secondChanelOffset == 3) {
+ result.centerFreq0 = result.frequency - 20;
+ } else {
+ result.centerFreq0 = 0;
+ Log.e(TAG, dbg + ": Error on secondChanelOffset");
+ }
+ } else {
+ result.centerFreq0 = 0;
+ result.centerFreq1 = 0;
+ }
+ result.centerFreq1 = 0;
+ }
+ if(DBG) {
+ Log.d(TAG, dbg + "SSID: " + result.SSID + " ChannelWidth is: " + result.channelWidth +
+ " PrimaryFreq: " + result.frequency +" mCenterfreq0: " + result.centerFreq0 +
+ " mCenterfreq1: " + result.centerFreq1 + (is80211McRTTResponder ?
+ "Support RTT reponder: " : "Do not support RTT responder"));
+ }
+
+ result.informationElements = elements;
+ }
+
+ synchronized static void onFullScanResult(int id, ScanResult result, byte bytes[]) {
+ if (DBG) Log.i(TAG, "Got a full scan results event, ssid = " + result.SSID + ", " +
+ "num = " + bytes.length);
+
+ if (sScanEventHandler == null) {
+ return;
+ }
+ populateScanResult(result, bytes, " onFullScanResult ");
+
+ sScanEventHandler.onFullScanResult(result);
+ }
+
+ private static int sScanCmdId = 0;
+ private static ScanEventHandler sScanEventHandler;
+ private static ScanSettings sScanSettings;
+
+ synchronized public static boolean startScan(
+ ScanSettings settings, ScanEventHandler eventHandler) {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+
+ if (sScanCmdId != 0) {
+ stopScan();
+ } else if (sScanSettings != null || sScanEventHandler != null) {
+ /* current scan is paused; no need to stop it */
+ }
+
+ sScanCmdId = getNewCmdIdLocked();
+
+ sScanSettings = settings;
+ sScanEventHandler = eventHandler;
+
+ if (startScanNative(sWlan0Index, sScanCmdId, settings) == false) {
+ sScanEventHandler = null;
+ sScanSettings = null;
+ sScanCmdId = 0;
+ return false;
+ }
+
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ synchronized public static void stopScan() {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ stopScanNative(sWlan0Index, sScanCmdId);
+ sScanSettings = null;
+ sScanEventHandler = null;
+ sScanCmdId = 0;
+ }
+ }
+ }
+
+ synchronized public static void pauseScan() {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ if (sScanCmdId != 0 && sScanSettings != null && sScanEventHandler != null) {
+ Log.d(TAG, "Pausing scan");
+ WifiScanner.ScanData scanData[] = getScanResultsNative(sWlan0Index, true);
+ stopScanNative(sWlan0Index, sScanCmdId);
+ sScanCmdId = 0;
+ sScanEventHandler.onScanPaused(scanData);
+ }
+ }
+ }
+ }
+
+ synchronized public static void restartScan() {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ if (sScanCmdId == 0 && sScanSettings != null && sScanEventHandler != null) {
+ Log.d(TAG, "Restarting scan");
+ ScanEventHandler handler = sScanEventHandler;
+ ScanSettings settings = sScanSettings;
+ if (startScan(sScanSettings, sScanEventHandler)) {
+ sScanEventHandler.onScanRestarted();
+ } else {
+ /* we are still paused; don't change state */
+ sScanEventHandler = handler;
+ sScanSettings = settings;
+ }
+ }
+ }
+ }
+ }
+
+ synchronized public static WifiScanner.ScanData[] getScanResults(boolean flush) {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ return getScanResultsNative(sWlan0Index, flush);
+ } else {
+ return null;
+ }
+ }
+ }
+
+ public static interface HotlistEventHandler {
+ void onHotlistApFound (ScanResult[] result);
+ void onHotlistApLost (ScanResult[] result);
+ }
+
+ private static int sHotlistCmdId = 0;
+ private static HotlistEventHandler sHotlistEventHandler;
+
+ private native static boolean setHotlistNative(int iface, int id,
+ WifiScanner.HotlistSettings settings);
+ private native static boolean resetHotlistNative(int iface, int id);
+
+ synchronized public static boolean setHotlist(WifiScanner.HotlistSettings settings,
+ HotlistEventHandler eventHandler) {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ if (sHotlistCmdId != 0) {
+ return false;
+ } else {
+ sHotlistCmdId = getNewCmdIdLocked();
+ }
+
+ sHotlistEventHandler = eventHandler;
+ if (setHotlistNative(sWlan0Index, sHotlistCmdId, settings) == false) {
+ sHotlistEventHandler = null;
+ return false;
+ }
+
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ synchronized public static void resetHotlist() {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ if (sHotlistCmdId != 0) {
+ resetHotlistNative(sWlan0Index, sHotlistCmdId);
+ sHotlistCmdId = 0;
+ sHotlistEventHandler = null;
+ }
+ }
+ }
+ }
+
+ synchronized public static void onHotlistApFound(int id, ScanResult[] results) {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ if (sHotlistCmdId != 0) {
+ sHotlistEventHandler.onHotlistApFound(results);
+ } else {
+ /* this can happen because of race conditions */
+ Log.d(TAG, "Ignoring hotlist AP found event");
+ }
+ }
+ }
+ }
+
+ synchronized public static void onHotlistApLost(int id, ScanResult[] results) {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ if (sHotlistCmdId != 0) {
+ sHotlistEventHandler.onHotlistApLost(results);
+ } else {
+ /* this can happen because of race conditions */
+ Log.d(TAG, "Ignoring hotlist AP lost event");
+ }
+ }
+ }
+ }
+
+ public static interface SignificantWifiChangeEventHandler {
+ void onChangesFound(ScanResult[] result);
+ }
+
+ private static SignificantWifiChangeEventHandler sSignificantWifiChangeHandler;
+ private static int sSignificantWifiChangeCmdId;
+
+ private static native boolean trackSignificantWifiChangeNative(
+ int iface, int id, WifiScanner.WifiChangeSettings settings);
+ private static native boolean untrackSignificantWifiChangeNative(int iface, int id);
+
+ synchronized public static boolean trackSignificantWifiChange(
+ WifiScanner.WifiChangeSettings settings, SignificantWifiChangeEventHandler handler) {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ if (sSignificantWifiChangeCmdId != 0) {
+ return false;
+ } else {
+ sSignificantWifiChangeCmdId = getNewCmdIdLocked();
+ }
+
+ sSignificantWifiChangeHandler = handler;
+ if (trackSignificantWifiChangeNative(sWlan0Index, sScanCmdId, settings) == false) {
+ sSignificantWifiChangeHandler = null;
+ return false;
+ }
+
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+ }
+
+ synchronized static void untrackSignificantWifiChange() {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ if (sSignificantWifiChangeCmdId != 0) {
+ untrackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId);
+ sSignificantWifiChangeCmdId = 0;
+ sSignificantWifiChangeHandler = null;
+ }
+ }
+ }
+ }
+
+ synchronized static void onSignificantWifiChange(int id, ScanResult[] results) {
+ synchronized (mLock) {
+ if (sSignificantWifiChangeCmdId != 0) {
+ sSignificantWifiChangeHandler.onChangesFound(results);
+ } else {
+ /* this can happen because of race conditions */
+ Log.d(TAG, "Ignoring significant wifi change");
+ }
+ }
+ }
+
+ synchronized public static WifiLinkLayerStats getWifiLinkLayerStats(String iface) {
+ // TODO: use correct iface name to Index translation
+ if (iface == null) return null;
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ return getWifiLinkLayerStatsNative(sWlan0Index);
+ } else {
+ return null;
+ }
+ }
+ }
+
+ synchronized public static void setWifiLinkLayerStats(String iface, int enable) {
+ if (iface == null) return;
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ setWifiLinkLayerStatsNative(sWlan0Index, enable);
+ }
+ }
+ }
+
public static native int getSupportedFeatureSetNative(int iface);
synchronized public static int getSupportedFeatureSet() {
- return getSupportedFeatureSetNative(sWlan0Index);
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ return getSupportedFeatureSetNative(sWlan0Index);
+ } else {
+ Log.d(TAG, "Failing getSupportedFeatureset because HAL isn't started");
+ return 0;
+ }
+ }
}
/* Rtt related commands/events */
@@ -1547,7 +1833,7 @@
sRttEventHandler.onRttResults(results);
sRttCmdId = 0;
} else {
- Log.d(TAG, "Received event for unknown cmd = " + id + ", current id = " + sRttCmdId);
+ Log.d(TAG, "RTT Received event for unknown cmd = " + id + ", current id = " + sRttCmdId);
}
}
@@ -1559,25 +1845,39 @@
synchronized public static boolean requestRtt(
RttManager.RttParams[] params, RttEventHandler handler) {
synchronized (mLock) {
- if (sRttCmdId != 0) {
- return false;
+ if (isHalStarted()) {
+ if (sRttCmdId != 0) {
+ Log.v("TAG", "Last one is still under measurement!");
+ return false;
+ } else {
+ sRttCmdId = getNewCmdIdLocked();
+ }
+ sRttEventHandler = handler;
+ Log.v(TAG, "native issue RTT request");
+ return requestRangeNative(sWlan0Index, sRttCmdId, params);
} else {
- sRttCmdId = getNewCmdIdLocked();
+ return false;
}
- sRttEventHandler = handler;
- return requestRangeNative(sWlan0Index, sRttCmdId, params);
}
}
synchronized public static boolean cancelRtt(RttManager.RttParams[] params) {
synchronized(mLock) {
- if (sRttCmdId == 0) {
- return false;
- }
+ if (isHalStarted()) {
+ if (sRttCmdId == 0) {
+ return false;
+ }
- if (cancelRangeRequestNative(sWlan0Index, sRttCmdId, params)) {
- sRttEventHandler = null;
- return true;
+ sRttCmdId = 0;
+
+ if (cancelRangeRequestNative(sWlan0Index, sRttCmdId, params)) {
+ sRttEventHandler = null;
+ Log.v(TAG, "RTT cancel Request Successfully");
+ return true;
+ } else {
+ Log.e(TAG, "RTT cancel Request failed");
+ return false;
+ }
} else {
return false;
}
@@ -1588,7 +1888,7 @@
synchronized public static boolean setScanningMacOui(byte[] oui) {
synchronized (mLock) {
- if (startHal()) {
+ if (isHalStarted()) {
return setScanningMacOuiNative(sWlan0Index, oui);
} else {
return false;
@@ -1601,11 +1901,494 @@
synchronized public static int [] getChannelsForBand(int band) {
synchronized (mLock) {
- if (startHal()) {
+ if (isHalStarted()) {
return getChannelsForBandNative(sWlan0Index, band);
+ } else {
+ return null;
+ }
+ }
+ }
+
+ private static native boolean isGetChannelsForBandSupportedNative();
+ synchronized public static boolean isGetChannelsForBandSupported(){
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ return isGetChannelsForBandSupportedNative();
+ } else {
+ return false;
+ }
+ }
+ }
+
+ private static native boolean setDfsFlagNative(int iface, boolean dfsOn);
+ synchronized public static boolean setDfsFlag(boolean dfsOn) {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ return setDfsFlagNative(sWlan0Index, dfsOn);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ private static native boolean toggleInterfaceNative(int on);
+ synchronized public static boolean toggleInterface(int on) {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ return toggleInterfaceNative(0);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ private static native RttManager.RttCapabilities getRttCapabilitiesNative(int iface);
+ synchronized public static RttManager.RttCapabilities getRttCapabilities() {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ return getRttCapabilitiesNative(sWlan0Index);
+ }else {
+ return null;
+ }
+ }
+ }
+
+ private static native boolean setCountryCodeHalNative(int iface, String CountryCode);
+ synchronized public static boolean setCountryCodeHal( String CountryCode) {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ return setCountryCodeHalNative(sWlan0Index, CountryCode);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /* Rtt related commands/events */
+ public abstract class TdlsEventHandler {
+ abstract public void onTdlsStatus(String macAddr, int status, int reason);
+ }
+
+ private static TdlsEventHandler sTdlsEventHandler;
+
+ private static native boolean enableDisableTdlsNative(int iface, boolean enable,
+ String macAddr);
+ synchronized public static boolean enableDisableTdls(boolean enable, String macAdd,
+ TdlsEventHandler tdlsCallBack) {
+ synchronized (mLock) {
+ sTdlsEventHandler = tdlsCallBack;
+ return enableDisableTdlsNative(sWlan0Index, enable, macAdd);
+ }
+ }
+
+ // Once TDLS per mac and event feature is implemented, this class definition should be
+ // moved to the right place, like WifiManager etc
+ public static class TdlsStatus {
+ int channel;
+ int global_operating_class;
+ int state;
+ int reason;
+ }
+ private static native TdlsStatus getTdlsStatusNative(int iface, String macAddr);
+ synchronized public static TdlsStatus getTdlsStatus (String macAdd) {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ return getTdlsStatusNative(sWlan0Index, macAdd);
} else {
return null;
}
}
}
+
+ //ToFix: Once TDLS per mac and event feature is implemented, this class definition should be
+ // moved to the right place, like WifiStateMachine etc
+ public static class TdlsCapabilities {
+ /* Maximum TDLS session number can be supported by the Firmware and hardware */
+ int maxConcurrentTdlsSessionNumber;
+ boolean isGlobalTdlsSupported;
+ boolean isPerMacTdlsSupported;
+ boolean isOffChannelTdlsSupported;
+ }
+
+
+
+ private static native TdlsCapabilities getTdlsCapabilitiesNative(int iface);
+ synchronized public static TdlsCapabilities getTdlsCapabilities () {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ return getTdlsCapabilitiesNative(sWlan0Index);
+ } else {
+ return null;
+ }
+ }
+ }
+
+ synchronized private static boolean onTdlsStatus(String macAddr, int status, int reason) {
+ if (sTdlsEventHandler == null) {
+ return false;
+ } else {
+ sTdlsEventHandler.onTdlsStatus(macAddr, status, reason);
+ return true;
+ }
+ }
+
+ //---------------------------------------------------------------------------------
+
+ /* Wifi Logger commands/events */
+
+ public static native boolean startLogging(int iface);
+
+ public static interface WifiLoggerEventHandler {
+ void onRingBufferData(RingBufferStatus status, byte[] buffer);
+ void onWifiAlert(int errorCode, byte[] buffer);
+ }
+
+ private static WifiLoggerEventHandler sWifiLoggerEventHandler = null;
+
+ private static void onRingBufferData(RingBufferStatus status, byte[] buffer) {
+ if (sWifiLoggerEventHandler != null)
+ sWifiLoggerEventHandler.onRingBufferData(status, buffer);
+ }
+
+ private static void onWifiAlert(byte[] buffer, int errorCode) {
+ if (sWifiLoggerEventHandler != null)
+ sWifiLoggerEventHandler.onWifiAlert(errorCode, buffer);
+ }
+
+ private static int sLogCmdId = -1;
+ private static native boolean setLoggingEventHandlerNative(int iface, int id);
+ synchronized public static boolean setLoggingEventHandler(WifiLoggerEventHandler handler) {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ int oldId = sLogCmdId;
+ sLogCmdId = getNewCmdIdLocked();
+ if (!setLoggingEventHandlerNative(sWlan0Index, sLogCmdId)) {
+ sLogCmdId = oldId;
+ return false;
+ }
+ sWifiLoggerEventHandler = handler;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ private static native boolean startLoggingRingBufferNative(int iface, int verboseLevel,
+ int flags, int minIntervalSec ,int minDataSize, String ringName);
+ synchronized public static boolean startLoggingRingBuffer(int verboseLevel, int flags, int maxInterval,
+ int minDataSize, String ringName){
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ return startLoggingRingBufferNative(sWlan0Index, verboseLevel, flags, maxInterval,
+ minDataSize, ringName);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ private static native int getSupportedLoggerFeatureSetNative(int iface);
+ synchronized public static int getSupportedLoggerFeatureSet() {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ return getSupportedLoggerFeatureSetNative(sWlan0Index);
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ private static native boolean resetLogHandlerNative(int iface, int id);
+ synchronized public static boolean resetLogHandler() {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ if (sLogCmdId == -1) {
+ Log.e(TAG,"Can not reset handler Before set any handler");
+ return false;
+ }
+ sWifiLoggerEventHandler = null;
+ if (resetLogHandlerNative(sWlan0Index, sLogCmdId)) {
+ sLogCmdId = -1;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+
+ private static native String getDriverVersionNative(int iface);
+ synchronized public static String getDriverVersion() {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ return getDriverVersionNative(sWlan0Index);
+ } else {
+ return "";
+ }
+ }
+ }
+
+
+ private static native String getFirmwareVersionNative(int iface);
+ synchronized public static String getFirmwareVersion() {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ return getFirmwareVersionNative(sWlan0Index);
+ } else {
+ return "";
+ }
+ }
+ }
+
+ public static class RingBufferStatus{
+ String name;
+ int flag;
+ int ringBufferId;
+ int ringBufferByteSize;
+ int verboseLevel;
+ int writtenBytes;
+ int readBytes;
+ int writtenRecords;
+
+ @Override
+ public String toString() {
+ return "name: " + name + " flag: " + flag + " ringBufferId: " + ringBufferId +
+ " ringBufferByteSize: " +ringBufferByteSize + " verboseLevel: " +verboseLevel +
+ " writtenBytes: " + writtenBytes + " readBytes: " + readBytes +
+ " writtenRecords: " + writtenRecords;
+ }
+ }
+
+ private static native RingBufferStatus[] getRingBufferStatusNative(int iface);
+ synchronized public static RingBufferStatus[] getRingBufferStatus() {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ return getRingBufferStatusNative(sWlan0Index);
+ } else {
+ return null;
+ }
+ }
+ }
+
+ private static native boolean getRingBufferDataNative(int iface, String ringName);
+ synchronized public static boolean getRingBufferData(String ringName) {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ return getRingBufferDataNative(sWlan0Index, ringName);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ static private byte[] mFwMemoryDump;
+ private static void onWifiFwMemoryAvailable(byte[] buffer) {
+ mFwMemoryDump = buffer;
+ if (DBG) {
+ Log.d(TAG, "onWifiFwMemoryAvailable is called and buffer length is: " +
+ (buffer == null ? 0 : buffer.length));
+ }
+ }
+
+ private static native boolean getFwMemoryDumpNative(int iface);
+ synchronized public static byte[] getFwMemoryDump() {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ if(getFwMemoryDumpNative(sWlan0Index)) {
+ byte[] fwMemoryDump = mFwMemoryDump;
+ mFwMemoryDump = null;
+ return fwMemoryDump;
+ } else {
+ return null;
+ }
+ }
+
+ return null;
+ }
+ }
+
+ //---------------------------------------------------------------------------------
+ /* Configure ePNO */
+
+ public class WifiPnoNetwork {
+ String SSID;
+ int rssi_threshold;
+ int flags;
+ int auth;
+ String configKey; // kept for reference
+
+ WifiPnoNetwork(WifiConfiguration config, int threshold) {
+ if (config.SSID == null) {
+ this.SSID = "";
+ this.flags = 1;
+ } else {
+ this.SSID = config.SSID;
+ }
+ this.rssi_threshold = threshold;
+ if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
+ auth |= 2;
+ } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP) ||
+ config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) {
+ auth |= 4;
+ } else if (config.wepKeys[0] != null) {
+ auth |= 1;
+ } else {
+ auth |= 1;
+ }
+// auth = 0;
+ flags |= 6; //A and G
+ configKey = config.configKey();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sbuf = new StringBuilder();
+ sbuf.append(this.SSID);
+ sbuf.append(" flags=").append(this.flags);
+ sbuf.append(" rssi=").append(this.rssi_threshold);
+ sbuf.append(" auth=").append(this.auth);
+ return sbuf.toString();
+ }
+ }
+
+ public static interface WifiPnoEventHandler {
+ void onPnoNetworkFound(ScanResult results[]);
+ }
+
+ private static WifiPnoEventHandler sWifiPnoEventHandler;
+
+ private static int sPnoCmdId = 0;
+
+ private native static boolean setPnoListNative(int iface, int id, WifiPnoNetwork list[]);
+
+ synchronized public static boolean setPnoList(WifiPnoNetwork list[],
+ WifiPnoEventHandler eventHandler) {
+ Log.e(TAG, "setPnoList cmd " + sPnoCmdId);
+
+ synchronized (mLock) {
+ if (isHalStarted()) {
+
+ sPnoCmdId = getNewCmdIdLocked();
+
+ sWifiPnoEventHandler = eventHandler;
+ if (setPnoListNative(sWlan0Index, sPnoCmdId, list)) {
+ return true;
+ }
+ }
+
+ sWifiPnoEventHandler = null;
+ return false;
+ }
+ }
+
+ synchronized public static void onPnoNetworkFound(int id, ScanResult[] results) {
+
+ if (results == null) {
+ Log.e(TAG, "onPnoNetworkFound null results");
+ return;
+
+ }
+ Log.d(TAG, "WifiNative.onPnoNetworkFound result " + results.length);
+
+ //Log.e(TAG, "onPnoNetworkFound length " + results.length);
+ //return;
+ for (int i=0; i<results.length; i++) {
+ Log.e(TAG, "onPnoNetworkFound SSID " + results[i].SSID
+ + " " + results[i].level + " " + results[i].frequency);
+
+ populateScanResult(results[i], results[i].bytes, "onPnoNetworkFound ");
+ results[i].wifiSsid = WifiSsid.createFromAsciiEncoded(results[i].SSID);
+ }
+ synchronized (mLock) {
+ if (sPnoCmdId != 0 && sWifiPnoEventHandler != null) {
+ sWifiPnoEventHandler.onPnoNetworkFound(results);
+ } else {
+ /* this can happen because of race conditions */
+ Log.d(TAG, "Ignoring Pno Network found event");
+ }
+ }
+ }
+
+ public class WifiLazyRoamParams {
+ int A_band_boost_threshold;
+ int A_band_penalty_threshold;
+ int A_band_boost_factor;
+ int A_band_penalty_factor;
+ int A_band_max_boost;
+ int lazy_roam_hysteresis;
+ int alert_roam_rssi_trigger;
+
+ WifiLazyRoamParams() {
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sbuf = new StringBuilder();
+ sbuf.append(" A_band_boost_threshold=").append(this.A_band_boost_threshold);
+ sbuf.append(" A_band_penalty_threshold=").append(this.A_band_penalty_threshold);
+ sbuf.append(" A_band_boost_factor=").append(this.A_band_boost_factor);
+ sbuf.append(" A_band_penalty_factor=").append(this.A_band_penalty_factor);
+ sbuf.append(" A_band_max_boost=").append(this.A_band_max_boost);
+ sbuf.append(" lazy_roam_hysteresis=").append(this.lazy_roam_hysteresis);
+ sbuf.append(" alert_roam_rssi_trigger=").append(this.alert_roam_rssi_trigger);
+ return sbuf.toString();
+ }
+ }
+
+ private native static boolean setLazyRoamNative(int iface, int id,
+ boolean enabled, WifiLazyRoamParams param);
+
+ synchronized public static boolean setLazyRoam(boolean enabled, WifiLazyRoamParams params) {
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ sPnoCmdId = getNewCmdIdLocked();
+ return setLazyRoamNative(sWlan0Index, sPnoCmdId, enabled, params);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ private native static boolean setBssidBlacklistNative(int iface, int id,
+ String list[]);
+
+ synchronized public static boolean setBssidBlacklist(String list[]) {
+ int size = 0;
+ if (list != null) {
+ size = list.length;
+ }
+ Log.e(TAG, "setBssidBlacklist cmd " + sPnoCmdId + " size " + size);
+
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ sPnoCmdId = getNewCmdIdLocked();
+ return setBssidBlacklistNative(sWlan0Index, sPnoCmdId, list);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ private native static boolean setSsidWhitelistNative(int iface, int id, String list[]);
+
+ synchronized public static boolean setSsidWhitelist(String list[]) {
+ int size = 0;
+ if (list != null) {
+ size = list.length;
+ }
+ Log.e(TAG, "setSsidWhitelist cmd " + sPnoCmdId + " size " + size);
+
+ synchronized (mLock) {
+ if (isHalStarted()) {
+ sPnoCmdId = getNewCmdIdLocked();
+
+ return setSsidWhitelistNative(sWlan0Index, sPnoCmdId, list);
+ } else {
+ return false;
+ }
+ }
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiNetworkScoreCache.java b/service/java/com/android/server/wifi/WifiNetworkScoreCache.java
index 0a7df0b..cf14c5f 100644
--- a/service/java/com/android/server/wifi/WifiNetworkScoreCache.java
+++ b/service/java/com/android/server/wifi/WifiNetworkScoreCache.java
@@ -139,7 +139,7 @@
}
private String buildNetworkKey(ScoredNetwork network) {
- if (network.networkKey == null) return null;
+ if (network == null || network.networkKey == null) return null;
if (network.networkKey.wifiKey == null) return null;
if (network.networkKey.type == NetworkKey.TYPE_WIFI) {
String key = network.networkKey.wifiKey.ssid;
@@ -153,7 +153,7 @@
}
private String buildNetworkKey(ScanResult result) {
- if (result.SSID == null) {
+ if (result == null || result.SSID == null) {
return null;
}
StringBuilder key = new StringBuilder("\"");
diff --git a/service/java/com/android/server/wifi/WifiNotificationController.java b/service/java/com/android/server/wifi/WifiNotificationController.java
index 20d64c9..406a764 100644
--- a/service/java/com/android/server/wifi/WifiNotificationController.java
+++ b/service/java/com/android/server/wifi/WifiNotificationController.java
@@ -65,7 +65,7 @@
/**
* The Notification object given to the NotificationManager.
*/
- private Notification mNotification;
+ private Notification.Builder mNotificationBuilder;
/**
* Whether the notification is being shown, as set by us. That is, if the
* user cancels the notification, we will not receive the callback so this
@@ -229,31 +229,32 @@
return;
}
- if (mNotification == null) {
- // Cache the Notification object.
- mNotification = new Notification();
- mNotification.when = 0;
- mNotification.icon = ICON_NETWORKS_AVAILABLE;
- mNotification.flags = Notification.FLAG_AUTO_CANCEL;
- mNotification.contentIntent = TaskStackBuilder.create(mContext)
- .addNextIntentWithParentStack(
- new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK))
- .getPendingIntent(0, 0, null, UserHandle.CURRENT);
+ if (mNotificationBuilder == null) {
+ // Cache the Notification builder object.
+ mNotificationBuilder = new Notification.Builder(mContext)
+ .setWhen(0)
+ .setSmallIcon(ICON_NETWORKS_AVAILABLE)
+ .setAutoCancel(true)
+ .setContentIntent(TaskStackBuilder.create(mContext)
+ .addNextIntentWithParentStack(
+ new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK))
+ .getPendingIntent(0, 0, null, UserHandle.CURRENT))
+ .setColor(mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color));
}
CharSequence title = mContext.getResources().getQuantityText(
com.android.internal.R.plurals.wifi_available, numNetworks);
CharSequence details = mContext.getResources().getQuantityText(
com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
- mNotification.tickerText = title;
- mNotification.color = mContext.getResources().getColor(
- com.android.internal.R.color.system_notification_accent_color);
- mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
+ mNotificationBuilder.setTicker(title);
+ mNotificationBuilder.setContentTitle(title);
+ mNotificationBuilder.setContentText(details);
mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
- notificationManager.notifyAsUser(null, ICON_NETWORKS_AVAILABLE, mNotification,
- UserHandle.ALL);
+ notificationManager.notifyAsUser(null, ICON_NETWORKS_AVAILABLE,
+ mNotificationBuilder.build(), UserHandle.ALL);
} else {
notificationManager.cancelAsUser(null, ICON_NETWORKS_AVAILABLE, UserHandle.ALL);
}
diff --git a/service/java/com/android/server/wifi/WifiScanningServiceImpl.java b/service/java/com/android/server/wifi/WifiScanningServiceImpl.java
index 6dacfb4..8f2cd3b 100644
--- a/service/java/com/android/server/wifi/WifiScanningServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiScanningServiceImpl.java
@@ -16,18 +16,24 @@
package com.android.server.wifi;
+import android.Manifest;
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.content.pm.PackageManager;
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.BssidInfo;
+import android.net.wifi.WifiScanner.ChannelSpec;
+import android.net.wifi.WifiScanner.ScanData;
import android.net.wifi.WifiScanner.ScanSettings;
import android.net.wifi.WifiSsid;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
@@ -36,13 +42,16 @@
import android.os.Messenger;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.WorkSource;
+import android.util.LocalLog;
import android.util.Log;
-import android.util.Slog;
+import com.android.internal.app.IBatteryStats;
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 com.android.server.am.BatteryStatsService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -51,26 +60,49 @@
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 boolean DBG = false;
+ private static final boolean VDBG = false;
+
private static final int INVALID_KEY = 0; // same as WifiScanner
private static final int MIN_PERIOD_PER_CHANNEL_MS = 200; // DFS needs 120 ms
+ private static final int UNKNOWN_PID = -1;
+
+ private static final LocalLog mLocalLog = new LocalLog(1024);
+
+ private static void localLog(String message) {
+ mLocalLog.log(message);
+ }
+
+ private static void logw(String message) {
+ Log.w(TAG, message);
+ mLocalLog.log(message);
+ }
+
+ private static void loge(String message) {
+ Log.e(TAG, message);
+ mLocalLog.log(message);
+ }
@Override
public Messenger getMessenger() {
- return new Messenger(mClientHandler);
+ if (mClientHandler != null) {
+ return new Messenger(mClientHandler);
+ } else {
+ loge("WifiScanningServiceImpl trying to get messenger w/o initialization");
+ return null;
+ }
}
@Override
public Bundle getAvailableChannels(int band) {
- WifiScanner.ChannelSpec channelSpecs[] = getChannelsForBand(band);
+ ChannelSpec channelSpecs[] = getChannelsForBand(band);
ArrayList<Integer> list = new ArrayList<Integer>(channelSpecs.length);
- for (WifiScanner.ChannelSpec channelSpec : channelSpecs) {
+ for (ChannelSpec channelSpec : channelSpecs) {
list.add(channelSpec.frequency);
}
Bundle b = new Bundle();
@@ -78,10 +110,11 @@
return b;
}
- private void enforceConnectivityInternalPermission() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CONNECTIVITY_INTERNAL,
- "WifiScanningServiceImpl");
+ private void enforceLocationHardwarePermission(int uid) {
+ mContext.enforcePermission(
+ Manifest.permission.LOCATION_HARDWARE,
+ UNKNOWN_PID, uid,
+ "LocationHardware");
}
private class ClientHandler extends Handler {
@@ -93,50 +126,53 @@
@Override
public void handleMessage(Message msg) {
- if (DBG) Log.d(TAG, "ClientHandler got" + msg);
+ if (DBG) localLog("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);
- ClientInfo ci = mClients.remove(msg.replyTo);
- if (ci != null) { /* can be null if send failed above */
- ci.cleanup();
+ if (msg.arg1 != AsyncChannel.STATUS_SUCCESSFUL) {
+ loge("Client connection failure, error=" + msg.arg1);
}
return;
case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
AsyncChannel ac = new AsyncChannel();
ac.connect(mContext, this, msg.replyTo);
+ if (DBG) localLog("New client connected : " + msg.sendingUid + msg.replyTo);
+ ClientInfo cInfo = new ClientInfo(msg.sendingUid, ac, msg.replyTo);
+ mClients.put(msg.replyTo, cInfo);
+ return;
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+ if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
+ loge("Send failed, client connection lost");
+ } else {
+ if (DBG) localLog("Client connection lost with reason: " + msg.arg1);
+ }
+ if (DBG) localLog("closing client " + msg.replyTo);
+ ClientInfo ci = mClients.remove(msg.replyTo);
+ if (ci != null) { /* can be null if send failed above */
+ if (DBG) localLog("closing client " + ci.mUid);
+ ci.cleanup();
+ }
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, "Could not find listener");
+ try {
+ enforceLocationHardwarePermission(msg.sendingUid);
+ } catch (SecurityException e) {
+ localLog("failed to authorize app: " + e);
+ replyFailed(msg, WifiScanner.REASON_NOT_AUTHORIZED, "Not authorized");
return;
}
- try {
- enforceConnectivityInternalPermission();
- } catch (SecurityException e) {
- replyFailed(msg, WifiScanner.REASON_NOT_AUTHORIZED, "Not authorized");
+ if (msg.what == WifiScanner.CMD_GET_SCAN_RESULTS) {
+ mStateMachine.sendMessage(Message.obtain(msg));
+ return;
+ }
+ ClientInfo ci = mClients.get(msg.replyTo);
+ if (ci == null) {
+ loge("Could not find client info for message " + msg.replyTo);
+ replyFailed(msg, WifiScanner.REASON_INVALID_LISTENER, "Could not find listener");
return;
}
@@ -144,6 +180,8 @@
WifiScanner.CMD_SCAN,
WifiScanner.CMD_START_BACKGROUND_SCAN,
WifiScanner.CMD_STOP_BACKGROUND_SCAN,
+ WifiScanner.CMD_START_SINGLE_SCAN,
+ WifiScanner.CMD_STOP_SINGLE_SCAN,
WifiScanner.CMD_SET_HOTLIST,
WifiScanner.CMD_RESET_HOTLIST,
WifiScanner.CMD_CONFIGURE_WIFI_CHANGE,
@@ -173,10 +211,13 @@
private static final int CMD_DRIVER_UNLOADED = BASE + 7;
private static final int CMD_SCAN_PAUSED = BASE + 8;
private static final int CMD_SCAN_RESTARTED = BASE + 9;
+ private static final int CMD_STOP_SCAN_INTERNAL = BASE + 10;
private Context mContext;
private WifiScanningStateMachine mStateMachine;
private ClientHandler mClientHandler;
+ private IBatteryStats mBatteryStats;
+ private final WifiNative.ScanCapabilities mScanCapabilities = new WifiNative.ScanCapabilities();
WifiScanningServiceImpl() { }
@@ -193,6 +234,7 @@
mClientHandler = new ClientHandler(thread.getLooper());
mStateMachine = new WifiScanningStateMachine(thread.getLooper());
mWifiChangeStateMachine = new WifiChangeStateMachine(thread.getLooper());
+ mBatteryStats = BatteryStatsService.getService();
mContext.registerReceiver(
new BroadcastReceiver() {
@@ -200,7 +242,7 @@
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 (DBG) localLog("SCAN_AVAILABLE : " + state);
if (state == WifiManager.WIFI_STATE_ENABLED) {
mStateMachine.sendMessage(CMD_DRIVER_LOADED);
} else if (state == WifiManager.WIFI_STATE_DISABLED) {
@@ -236,62 +278,67 @@
@Override
public void onScanResultsAvailable() {
- if (DBG) Log.d(TAG, "onScanResultAvailable event received");
+ if (DBG) localLog("onScanResultAvailable event received");
sendMessage(CMD_SCAN_RESULTS_AVAILABLE);
}
@Override
- public void onSingleScanComplete() {
- if (DBG) Log.d(TAG, "onSingleScanComplete event received");
+ public void onScanStatus() {
+ if (DBG) localLog("onScanStatus event received");
sendMessage(CMD_SCAN_RESULTS_AVAILABLE);
}
@Override
public void onFullScanResult(ScanResult fullScanResult) {
- if (DBG) Log.d(TAG, "Full scanresult received");
+ if (DBG) localLog("Full scanresult received");
sendMessage(CMD_FULL_SCAN_RESULTS, 0, 0, fullScanResult);
}
@Override
- public void onScanPaused() {
- sendMessage(CMD_SCAN_PAUSED);
+ public void onScanPaused(ScanData scanData[]) {
+ sendMessage(CMD_SCAN_PAUSED, scanData);
}
@Override
public void onScanRestarted() {
+ if (DBG) localLog("onScanRestarted() event received");
sendMessage(CMD_SCAN_RESTARTED);
}
@Override
public void onHotlistApFound(ScanResult[] results) {
- if (DBG) Log.d(TAG, "HotlistApFound event received");
+ if (DBG) localLog("HotlistApFound event received");
sendMessage(CMD_HOTLIST_AP_FOUND, 0, 0, results);
}
@Override
+ public void onHotlistApLost(ScanResult[] results) {
+ if (DBG) localLog("HotlistApLost event received");
+ sendMessage(CMD_HOTLIST_AP_LOST, 0, 0, results);
+ }
+
+ @Override
public void onChangesFound(ScanResult[] results) {
- if (DBG) Log.d(TAG, "onWifiChangesFound event received");
+ if (DBG) localLog("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");
+ if (DBG) localLog("DefaultState");
}
@Override
public boolean processMessage(Message msg) {
- if (DBG) Log.d(TAG, "DefaultState got" + msg);
+ if (DBG) localLog("DefaultState got" + msg);
ClientInfo ci = mClients.get(msg.replyTo);
switch (msg.what) {
case CMD_DRIVER_LOADED:
- if (WifiNative.startHal() && WifiNative.getInterfaces() != 0) {
- WifiNative.ScanCapabilities capabilities =
- new WifiNative.ScanCapabilities();
- if (WifiNative.getScanCapabilities(capabilities)) {
+ if (WifiNative.getInterfaces() != 0) {
+ if (WifiNative.getScanCapabilities(mScanCapabilities)) {
transitionTo(mStartedState);
} else {
loge("could not get scan capabilities");
@@ -303,20 +350,23 @@
case WifiScanner.CMD_SCAN:
case WifiScanner.CMD_START_BACKGROUND_SCAN:
case WifiScanner.CMD_STOP_BACKGROUND_SCAN:
+ case WifiScanner.CMD_START_SINGLE_SCAN:
+ case WifiScanner.CMD_STOP_SINGLE_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:
+ case WifiScanner.CMD_GET_SCAN_RESULTS:
replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available");
break;
case CMD_SCAN_RESULTS_AVAILABLE:
- if (DBG) log("ignored scan results available event");
+ if (DBG) localLog("ignored scan results available event");
break;
case CMD_FULL_SCAN_RESULTS:
- if (DBG) log("ignored full scan result event");
+ if (DBG) localLog("ignored full scan result event");
break;
default:
@@ -331,13 +381,13 @@
@Override
public void enter() {
- if (DBG) Log.d(TAG, "StartedState");
+ if (DBG) localLog("StartedState");
}
@Override
public boolean processMessage(Message msg) {
- if (DBG) Log.d(TAG, "StartedState got" + msg);
+ if (DBG) localLog("StartedState got" + msg);
ClientInfo ci = mClients.get(msg.replyTo);
@@ -350,7 +400,7 @@
break;
case WifiScanner.CMD_START_BACKGROUND_SCAN:
if (addScanRequest(ci, msg.arg2, (ScanSettings) msg.obj)) {
- replySucceeded(msg, null);
+ replySucceeded(msg);
} else {
replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
}
@@ -359,18 +409,33 @@
removeScanRequest(ci, msg.arg2);
break;
case WifiScanner.CMD_GET_SCAN_RESULTS:
- replySucceeded(msg, getScanResults(ci));
+ reportScanResults();
+ replySucceeded(msg);
+ break;
+ case WifiScanner.CMD_START_SINGLE_SCAN:
+ if (addSingleScanRequest(ci, msg.arg2, (ScanSettings) msg.obj)) {
+ replySucceeded(msg);
+ } else {
+ replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
+ }
+ break;
+ case WifiScanner.CMD_STOP_SINGLE_SCAN:
+ removeScanRequest(ci, msg.arg2);
+ break;
+ case CMD_STOP_SCAN_INTERNAL:
+ localLog("Removing single shot scan");
+ removeScanRequest((ClientInfo) msg.obj, msg.arg2);
break;
case WifiScanner.CMD_SET_HOTLIST:
setHotlist(ci, msg.arg2, (WifiScanner.HotlistSettings) msg.obj);
- replySucceeded(msg, null);
+ replySucceeded(msg);
break;
case WifiScanner.CMD_RESET_HOTLIST:
resetHotlist(ci, msg.arg2);
break;
case WifiScanner.CMD_START_TRACKING_CHANGE:
trackWifiChanges(ci, msg.arg2);
- replySucceeded(msg, null);
+ replySucceeded(msg);
break;
case WifiScanner.CMD_STOP_TRACKING_CHANGE:
untrackWifiChanges(ci, msg.arg2);
@@ -379,7 +444,7 @@
configureWifiChange((WifiScanner.WifiChangeSettings) msg.obj);
break;
case CMD_SCAN_RESULTS_AVAILABLE: {
- ScanResult[] results = WifiNative.getScanResults();
+ ScanData[] results = WifiNative.getScanResults(/* flush = */ true);
Collection<ClientInfo> clients = mClients.values();
for (ClientInfo ci2 : clients) {
ci2.reportScanResults(results);
@@ -388,7 +453,7 @@
break;
case CMD_FULL_SCAN_RESULTS: {
ScanResult result = (ScanResult) msg.obj;
- if (DBG) Log.d(TAG, "reporting fullscan result for " + result.SSID);
+ if (DBG) localLog("reporting fullscan result for " + result.SSID);
Collection<ClientInfo> clients = mClients.values();
for (ClientInfo ci2 : clients) {
ci2.reportFullScanResult(result);
@@ -398,10 +463,19 @@
case CMD_HOTLIST_AP_FOUND: {
ScanResult[] results = (ScanResult[])msg.obj;
- if (DBG) Log.d(TAG, "Found " + results.length + " results");
+ if (DBG) localLog("Found " + results.length + " results");
Collection<ClientInfo> clients = mClients.values();
for (ClientInfo ci2 : clients) {
- ci2.reportHotlistResults(results);
+ ci2.reportHotlistResults(WifiScanner.CMD_AP_FOUND, results);
+ }
+ }
+ break;
+ case CMD_HOTLIST_AP_LOST: {
+ ScanResult[] results = (ScanResult[])msg.obj;
+ if (DBG) localLog("Lost " + results.length + " results");
+ Collection<ClientInfo> clients = mClients.values();
+ for (ClientInfo ci2 : clients) {
+ ci2.reportHotlistResults(WifiScanner.CMD_AP_LOST, results);
}
}
break;
@@ -415,7 +489,15 @@
reportWifiStabilized(results);
}
break;
-
+ case CMD_SCAN_PAUSED: {
+ ScanData results[] = (ScanData[]) msg.obj;
+ Collection<ClientInfo> clients = mClients.values();
+ for (ClientInfo ci2 : clients) {
+ ci2.reportScanResults(results);
+ }
+ transitionTo(mPausedState);
+ }
+ break;
default:
return NOT_HANDLED;
}
@@ -427,13 +509,13 @@
class PausedState extends State {
@Override
public void enter() {
- if (DBG) Log.d(TAG, "PausedState");
+ if (DBG) localLog("PausedState");
}
@Override
public boolean processMessage(Message msg) {
- if (DBG) Log.d(TAG, "PausedState got" + msg);
+ if (DBG) localLog("PausedState got" + msg);
switch (msg.what) {
case CMD_SCAN_RESTARTED:
@@ -450,11 +532,17 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- super.dump(fd, pw, args);
pw.println("number of clients : " + mClients.size());
+ for (ClientInfo client : mClients.values()) {
+ client.dump(fd, pw, args);
+ pw.append("------\n");
+ }
pw.println();
+ pw.println("localLog : ");
+ mLocalLog.dump(fd, pw, args);
+ pw.append("\n\n");
+ super.dump(fd, pw, args);
}
-
}
/* client management */
@@ -464,28 +552,93 @@
private static final int MAX_LIMIT = 16;
private final AsyncChannel mChannel;
private final Messenger mMessenger;
+ private final int mUid;
+ private final WorkSource mWorkSource;
+ private boolean mScanWorkReported = false;
- ClientInfo(AsyncChannel c, Messenger m) {
+ ClientInfo(int uid, AsyncChannel c, Messenger m) {
mChannel = c;
mMessenger = m;
- if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
+ mUid = uid;
+ mWorkSource = new WorkSource(uid, TAG);
+ if (DBG) localLog("New client, channel: " + c + " messenger: " + m);
+ }
+
+ void reportBatchedScanStart() {
+ if (mUid == 0)
+ return;
+
+ int csph = getCsph();
+
+ try {
+ mBatteryStats.noteWifiBatchedScanStartedFromSource(mWorkSource, csph);
+ localLog("started scanning for UID " + mUid + ", csph = " + csph);
+ } catch (RemoteException e) {
+ logw("failed to report scan work: " + e.toString());
+ }
+ }
+
+ void reportBatchedScanStop() {
+ if (mUid == 0)
+ return;
+
+ try {
+ mBatteryStats.noteWifiBatchedScanStoppedFromSource(mWorkSource);
+ localLog("stopped scanning for UID " + mUid);
+ } catch (RemoteException e) {
+ logw("failed to cleanup scan work: " + e.toString());
+ }
+ }
+
+ int getCsph() {
+ int csph = 0;
+ for (ScanSettings settings : getScanSettings()) {
+ int num_channels = settings.channels == null ? 0 : settings.channels.length;
+ if (num_channels == 0 && settings.band != 0) {
+ num_channels = getChannelsForBand(settings.band).length;
+ }
+
+ int scans_per_Hour = settings.periodInMs == 0 ? 1 : (3600 * 1000) / settings.periodInMs;
+ csph += num_channels * scans_per_Hour;
+ }
+
+ return csph;
+ }
+
+ void reportScanWorkUpdate() {
+ if (mScanWorkReported) {
+ reportBatchedScanStop();
+ mScanWorkReported = false;
+ }
+ if (mScanSettings.isEmpty() == false) {
+ reportBatchedScanStart();
+ mScanWorkReported = true;
+ }
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("mChannel ").append(mChannel).append("\n");
- sb.append("mMessenger ").append(mMessenger).append("\n");
+ sb.append("mMessenger ").append(mMessenger);
+ return sb.toString();
+ }
+
+ void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(toString());
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("]");
+ sb.append("ScanId ").append(entry.getKey()).append("\n");
+
+ ScanSettings scanSettings = entry.getValue();
+ sb.append(describe(scanSettings));
+ sb.append("\n");
}
- return sb.toString();
+ pw.println(sb.toString());
}
HashMap<Integer, ScanSettings> mScanSettings = new HashMap<Integer, ScanSettings>(4);
@@ -493,13 +646,19 @@
void addScanRequest(ScanSettings settings, int id) {
mScanSettings.put(id, settings);
+ reportScanWorkUpdate();
}
void removeScanRequest(int id) {
- mScanSettings.remove(id);
+ ScanSettings settings = mScanSettings.remove(id);
+ if (settings != null && settings.periodInMs == 0) {
+ /* this was a single shot scan */
+ mChannel.sendMessage(WifiScanner.CMD_SINGLE_SCAN_COMPLETED, 0, id);
+ }
+ reportScanWorkUpdate();
}
- Iterator<Map.Entry<Integer, WifiScanner.ScanSettings>> getScans() {
+ Iterator<Map.Entry<Integer, ScanSettings>> getScans() {
return mScanSettings.entrySet().iterator();
}
@@ -507,7 +666,7 @@
return mScanSettings.values();
}
- void reportScanResults(ScanResult[] results) {
+ void reportScanResults(ScanData[] results) {
Iterator<Integer> it = mScanSettings.keySet().iterator();
while (it.hasNext()) {
int handler = it.next();
@@ -515,9 +674,9 @@
}
}
- void reportScanResults(ScanResult[] results, int handler) {
+ void reportScanResults(ScanData[] results, int handler) {
ScanSettings settings = mScanSettings.get(handler);
- WifiScanner.ChannelSpec desiredChannels[] = settings.channels;
+ ChannelSpec desiredChannels[] = settings.channels;
if (settings.band != WifiScanner.WIFI_BAND_UNSPECIFIED
|| desiredChannels == null || desiredChannels.length == 0) {
desiredChannels = getChannelsForBand(settings.band);
@@ -525,42 +684,62 @@
// check the channels this client asked for ..
int num_results = 0;
- for (ScanResult result : results) {
- for (WifiScanner.ChannelSpec channelSpec : desiredChannels) {
- if (channelSpec.frequency == result.frequency) {
+ for (ScanData result : results) {
+ boolean copyScanData = false;
+ for (ScanResult scanResult : result.getResults()) {
+ for (ChannelSpec channelSpec : desiredChannels) {
+ if (channelSpec.frequency == scanResult.frequency) {
+ copyScanData = true;
+ break;
+ }
+ }
+ if (copyScanData) {
num_results++;
break;
}
}
}
- if (num_results == 0) {
- // nothing to report
- return;
- }
+ localLog("results = " + results.length + ", num_results = " + num_results);
- ScanResult results2[] = new ScanResult[num_results];
+ ScanData results2[] = new ScanData[num_results];
int index = 0;
- for (ScanResult result : results) {
- for (WifiScanner.ChannelSpec channelSpec : desiredChannels) {
- 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++;
+ for (ScanData result : results) {
+ boolean copyScanData = false;
+ for (ScanResult scanResult : result.getResults()) {
+ for (ChannelSpec channelSpec : desiredChannels) {
+ if (channelSpec.frequency == scanResult.frequency) {
+ copyScanData = true;
+ break;
+ }
+ }
+ if (copyScanData) {
break;
}
}
+
+ if (copyScanData) {
+ if (VDBG) {
+ localLog("adding at " + index);
+ }
+ results2[index] = new WifiScanner.ScanData(result);
+ index++;
+ }
}
+
+ localLog("delivering results, num = " + results2.length);
deliverScanResults(handler, results2);
+ if (settings.periodInMs == 0) {
+ /* this is a single shot scan; stop the scan now */
+ mStateMachine.sendMessage(CMD_STOP_SCAN_INTERNAL, 0, handler, this);
+ }
}
- void deliverScanResults(int handler, ScanResult results[]) {
- WifiScanner.ParcelableScanResults parcelableScanResults =
- new WifiScanner.ParcelableScanResults(results);
- mChannel.sendMessage(WifiScanner.CMD_SCAN_RESULT, 0, handler, parcelableScanResults);
+ void deliverScanResults(int handler, ScanData results[]) {
+ WifiScanner.ParcelableScanData parcelableScanData =
+ new WifiScanner.ParcelableScanData(results);
+ mChannel.sendMessage(WifiScanner.CMD_SCAN_RESULT, 0, handler, parcelableScanData);
}
void reportFullScanResult(ScanResult result) {
@@ -568,17 +747,15 @@
while (it.hasNext()) {
int handler = it.next();
ScanSettings settings = mScanSettings.get(handler);
- WifiScanner.ChannelSpec desiredChannels[] = settings.channels;
+ ChannelSpec desiredChannels[] = settings.channels;
if (settings.band != WifiScanner.WIFI_BAND_UNSPECIFIED
|| desiredChannels == null || desiredChannels.length == 0) {
desiredChannels = getChannelsForBand(settings.band);
}
- for (WifiScanner.ChannelSpec channelSpec : desiredChannels) {
+ for (ChannelSpec channelSpec : desiredChannels) {
if (channelSpec.frequency == result.frequency) {
- WifiSsid wifiSsid = WifiSsid.createFromAsciiEncoded(result.SSID);
- ScanResult newResult = new ScanResult(wifiSsid, result.BSSID, "",
- result.level, result.frequency, result.timestamp);
- if (DBG) Log.d(TAG, "sending it to " + handler);
+ ScanResult newResult = new ScanResult(result);
+ if (DBG) localLog("sending it to " + handler);
newResult.informationElements = result.informationElements.clone();
mChannel.sendMessage(
WifiScanner.CMD_FULL_SCAN_RESULT, 0, handler, newResult);
@@ -614,7 +791,7 @@
return mHotlistSettings.values();
}
- void reportHotlistResults(ScanResult[] results) {
+ void reportHotlistResults(int what, ScanResult[] results) {
Iterator<Map.Entry<Integer, WifiScanner.HotlistSettings>> it =
mHotlistSettings.entrySet().iterator();
while (it.hasNext()) {
@@ -623,7 +800,7 @@
WifiScanner.HotlistSettings settings = entry.getValue();
int num_results = 0;
for (ScanResult result : results) {
- for (WifiScanner.BssidInfo BssidInfo : settings.bssidInfos) {
+ for (BssidInfo BssidInfo : settings.bssidInfos) {
if (result.BSSID.equalsIgnoreCase(BssidInfo.bssid)) {
num_results++;
break;
@@ -639,7 +816,7 @@
ScanResult results2[] = new ScanResult[num_results];
int index = 0;
for (ScanResult result : results) {
- for (WifiScanner.BssidInfo BssidInfo : settings.bssidInfos) {
+ for (BssidInfo BssidInfo : settings.bssidInfos) {
if (result.BSSID.equalsIgnoreCase(BssidInfo.bssid)) {
results2[index] = result;
index++;
@@ -650,7 +827,7 @@
WifiScanner.ParcelableScanResults parcelableScanResults =
new WifiScanner.ParcelableScanResults(results2);
- mChannel.sendMessage(WifiScanner.CMD_AP_FOUND, 0, handler, parcelableScanResults);
+ mChannel.sendMessage(what, 0, handler, parcelableScanResults);
}
}
@@ -701,16 +878,15 @@
}
mSignificantWifiHandlers.clear();
- Log.d(TAG, "Successfully stopped all requests for client " + this);
+ localLog("Successfully stopped all requests for client " + this);
}
}
- void replySucceeded(Message msg, Object obj) {
+ void replySucceeded(Message msg) {
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) {
@@ -737,9 +913,9 @@
}
}
- private static class SettingsComputer {
+ private class SettingsComputer {
- private static class TimeBucket {
+ private class TimeBucket {
int periodInSecond;
int periodMinInSecond;
int periodMaxInSecond;
@@ -751,53 +927,49 @@
}
}
- private static final TimeBucket[] mTimeBuckets = new TimeBucket[] {
+ private final TimeBucket[] mTimeBuckets = new TimeBucket[] {
new TimeBucket( 1, 0, 5 ),
new TimeBucket( 5, 5, 10 ),
new TimeBucket( 10, 10, 25 ),
new TimeBucket( 30, 25, 55 ),
- new TimeBucket( 60, 55, 100),
+ new TimeBucket( 60, 55, 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 static final int DEFAULT_MAX_AP_PER_SCAN = 10;
- private static final int DEFAULT_REPORT_THRESHOLD = 10;
+ private static final int MAX_CHANNELS = 32;
private static final int DEFAULT_BASE_PERIOD_MS = 5000;
+ private static final int DEFAULT_REPORT_THRESHOLD_NUM_SCANS = 10;
+ private static final int DEFAULT_REPORT_THRESHOLD_PERCENT = 100;
private WifiNative.ScanSettings mSettings;
{
mSettings = new WifiNative.ScanSettings();
- mSettings.max_ap_per_scan = DEFAULT_MAX_AP_PER_SCAN;
+ mSettings.max_ap_per_scan = mScanCapabilities.max_ap_cache_per_scan;
mSettings.base_period_ms = DEFAULT_BASE_PERIOD_MS;
- mSettings.report_threshold = DEFAULT_REPORT_THRESHOLD;
+ mSettings.report_threshold_percent = DEFAULT_REPORT_THRESHOLD_PERCENT;
+ mSettings.report_threshold_num_scans = DEFAULT_REPORT_THRESHOLD_NUM_SCANS;
- mSettings.buckets = new WifiNative.BucketSettings[MAX_BUCKETS];
+ mSettings.buckets = new WifiNative.BucketSettings[mScanCapabilities.max_scan_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) {
+ private int getBestBucket(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;
+ ChannelSpec channels[] = settings.channels;
if (channels == null) {
// set channels based on band
channels = getChannelsForBand(settings.band);
@@ -805,13 +977,13 @@
if (channels == null) {
// still no channels; then there's nothing to scan
- Log.e(TAG, "No channels to scan!!");
+ loge("No channels to scan!!");
return -1;
}
int mostFrequentBucketIndex = mTimeBuckets.length;
- for (WifiScanner.ChannelSpec desiredChannelSpec : channels) {
+ for (ChannelSpec desiredChannelSpec : channels) {
if (mChannelToBucketMap.containsKey(desiredChannelSpec.frequency)) {
int bucket = mChannelToBucketMap.get(desiredChannelSpec.frequency);
if (bucket < mostFrequentBucketIndex) {
@@ -832,36 +1004,36 @@
}
if (mostFrequentBucketIndex < bestBucketIndex) {
- for (WifiScanner.ChannelSpec desiredChannelSpec : channels) {
+ for (ChannelSpec desiredChannelSpec : channels) {
mChannelToBucketMap.put(desiredChannelSpec.frequency, mostFrequentBucketIndex);
}
- Log.d(TAG, "returning mf bucket number " + mostFrequentBucketIndex);
+ localLog("returning mf bucket number " + mostFrequentBucketIndex);
return mostFrequentBucketIndex;
} else if (bestBucketIndex != -1) {
- for (WifiScanner.ChannelSpec desiredChannelSpec : channels) {
+ for (ChannelSpec desiredChannelSpec : channels) {
mChannelToBucketMap.put(desiredChannelSpec.frequency, bestBucketIndex);
}
- Log.d(TAG, "returning best bucket number " + bestBucketIndex);
+ localLog("returning best bucket number " + bestBucketIndex);
return bestBucketIndex;
}
- Log.e(TAG, "Could not find suitable bucket for period " + settings.periodInMs);
+ loge("Could not find suitable bucket for period " + settings.periodInMs);
return -1;
}
- void prepChannelMap(WifiScanner.ScanSettings settings) {
+ void prepChannelMap(ScanSettings settings) {
getBestBucket(settings);
}
- int addScanRequestToBucket(WifiScanner.ScanSettings settings) {
+ int addScanRequestToBucket(ScanSettings settings) {
int bucketIndex = getBestBucket(settings);
if (bucketIndex == -1) {
- Log.e(TAG, "Ignoring invalid settings");
+ loge("Ignoring invalid settings");
return -1;
}
- WifiScanner.ChannelSpec desiredChannels[] = settings.channels;
+ ChannelSpec desiredChannels[] = settings.channels;
if (settings.band != WifiScanner.WIFI_BAND_UNSPECIFIED
|| desiredChannels == null
|| desiredChannels.length == 0) {
@@ -869,28 +1041,29 @@
desiredChannels = getChannelsForBand(settings.band);
if (desiredChannels == null) {
// still no channels; then there's nothing to scan
- Log.e(TAG, "No channels to scan!!");
+ loge("No channels to scan!!");
return -1;
}
}
// merge the channel lists for these buckets
- Log.d(TAG, "merging " + desiredChannels.length + " channels "
- + " for period " + settings.periodInMs);
+ localLog("merging " + desiredChannels.length + " channels "
+ + " for period " + settings.periodInMs
+ + " maxScans " + settings.maxScansToCache);
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 ");
+ localLog("existing " + bucket.num_channels + " channels ");
- HashSet<WifiScanner.ChannelSpec> newChannels = new HashSet<WifiScanner.ChannelSpec>();
- for (WifiScanner.ChannelSpec desiredChannelSpec : desiredChannels) {
+ HashSet<ChannelSpec> newChannels = new HashSet<ChannelSpec>();
+ for (ChannelSpec desiredChannelSpec : desiredChannels) {
- Log.d(TAG, "desired channel " + desiredChannelSpec.frequency);
+ if (DBG) localLog("desired channel " + desiredChannelSpec.frequency);
boolean found = false;
- for (WifiNative.ChannelSettings existingChannelSpec : bucket.channels) {
- if (desiredChannelSpec.frequency == existingChannelSpec.frequency) {
+ for (int i = 0; i < bucket.num_channels; i++) {
+ if (desiredChannelSpec.frequency == bucket.channels[i].frequency) {
found = true;
break;
}
@@ -899,7 +1072,7 @@
if (!found) {
newChannels.add(desiredChannelSpec);
} else {
- if (DBG) Log.d(TAG, "Already scanning channel " + desiredChannelSpec.frequency);
+ if (DBG) localLog("Already scanning channel " + desiredChannelSpec.frequency);
}
}
@@ -910,24 +1083,25 @@
bucket.band = getBandFromChannels(bucket.channels)
| getBandFromChannels(desiredChannels);
bucket.channels = new WifiNative.ChannelSettings[0];
- Log.d(TAG, "switching to using band " + bucket.band);
+ localLog("switching to using band " + bucket.band);
} else {
- for (WifiScanner.ChannelSpec desiredChannelSpec : newChannels) {
+ for (ChannelSpec desiredChannelSpec : newChannels) {
- Log.d(TAG, "adding new channel spec " + desiredChannelSpec.frequency);
+ localLog("adding new channel spec " + desiredChannelSpec.frequency);
- WifiNative.ChannelSettings channelSettings = bucket.channels[bucket.num_channels];
+ WifiNative.ChannelSettings channelSettings = new WifiNative.ChannelSettings();
channelSettings.frequency = desiredChannelSpec.frequency;
+ bucket.channels[bucket.num_channels] = channelSettings;
bucket.num_channels++;
mChannelToBucketMap.put(bucketIndex, channelSettings.frequency);
}
}
if (bucket.report_events < settings.reportEvents) {
- if (DBG) Log.d(TAG, "setting report_events to " + settings.reportEvents);
+ if (DBG) localLog("setting report_events to " + settings.reportEvents);
bucket.report_events = settings.reportEvents;
} else {
- if (DBG) Log.d(TAG, "report_events is " + settings.reportEvents);
+ if (DBG) localLog("report_events is " + settings.reportEvents);
}
if (added) {
@@ -935,8 +1109,16 @@
mSettings.num_buckets++;
}
- if (mSettings.max_ap_per_scan < settings.numBssidsPerScan) {
- mSettings.max_ap_per_scan = settings.numBssidsPerScan;
+ if ( settings.numBssidsPerScan != 0) {
+ if (mSettings.max_ap_per_scan > settings.numBssidsPerScan) {
+ mSettings.max_ap_per_scan = settings.numBssidsPerScan;
+ }
+ }
+
+ if (settings.maxScansToCache != 0) {
+ if (mSettings.report_threshold_num_scans > settings.maxScansToCache) {
+ mSettings.report_threshold_num_scans = settings.maxScansToCache;
+ }
}
return bucket.period_ms;
@@ -980,13 +1162,13 @@
for (ClientInfo ci : clients) {
Iterator it = ci.getScans();
while (it.hasNext()) {
- Map.Entry<Integer, WifiScanner.ScanSettings> entry =
- (Map.Entry<Integer,WifiScanner.ScanSettings>)it.next();
+ Map.Entry<Integer, ScanSettings> entry =
+ (Map.Entry<Integer,ScanSettings>)it.next();
int id = entry.getKey();
ScanSettings s = entry.getValue();
int newPeriodInMs = c.addScanRequestToBucket(s);
if (newPeriodInMs == -1) {
- if (DBG) Log.d(TAG, "could not find a good bucket");
+ if (DBG) localLog("could not find a good bucket");
return false;
}
if (newPeriodInMs != s.periodInMs) {
@@ -999,25 +1181,46 @@
WifiNative.ScanSettings s = c.getComputedSettings();
if (s.num_buckets == 0) {
- if (DBG) Log.d(TAG, "Stopping scan because there are no buckets");
+ if (DBG) localLog("Stopping scan because there are no buckets");
WifiNative.stopScan();
return true;
} else {
if (WifiNative.startScan(s, mStateMachine)) {
- if (DBG) Log.d(TAG, "Successfully started scan of " + s.num_buckets + " buckets at"
- + "time = " + SystemClock.elapsedRealtimeNanos()/1000);
+ localLog("Successfully started scan of " + s.num_buckets + " buckets at"
+ + "time = " + SystemClock.elapsedRealtimeNanos() / 1000 + " period "
+ + s.base_period_ms);
return true;
} else {
- if (DBG) Log.d(TAG, "Failed to start scan of " + s.num_buckets + " buckets");
+ loge("Failed to start scan of " + s.num_buckets + " buckets");
return false;
}
}
}
+ void logScanRequest(String request, ClientInfo ci, int id, ScanSettings settings) {
+ StringBuffer sb = new StringBuffer();
+ sb.append(request);
+ sb.append("\nClient ");
+ sb.append(ci.toString());
+ sb.append("\nId ");
+ sb.append(id);
+ sb.append("\n");
+ if (settings != null) {
+ sb.append(describe(settings));
+ sb.append("\n");
+ }
+ sb.append("\n");
+ localLog(sb.toString());
+ }
+
boolean addScanRequest(ClientInfo ci, int handler, ScanSettings settings) {
// sanity check the input
+ if (ci == null) {
+ Log.d(TAG, "Failing scan request ClientInfo not found " + handler);
+ return false;
+ }
if (settings.periodInMs < WifiScanner.MIN_SCAN_PERIOD_MS) {
- Log.d(TAG, "Failing scan request because periodInMs is " + settings.periodInMs);
+ localLog("Failing scan request because periodInMs is " + settings.periodInMs);
return false;
}
@@ -1040,30 +1243,63 @@
}
if (settings.periodInMs < minSupportedPeriodMs) {
- Log.d(TAG, "Failing scan request because minSupportedPeriodMs is "
+ localLog("Failing scan request because minSupportedPeriodMs is "
+ minSupportedPeriodMs + " but the request wants " + settings.periodInMs);
return false;
}
+ logScanRequest("addScanRequest", ci, handler, settings);
ci.addScanRequest(settings, handler);
if (resetBuckets()) {
return true;
} else {
ci.removeScanRequest(handler);
- Log.d(TAG, "Failing scan request because failed to reset scan");
+ localLog("Failing scan request because failed to reset scan");
+ return false;
+ }
+ }
+
+ boolean addSingleScanRequest(ClientInfo ci, int handler, ScanSettings settings) {
+ if (ci == null) {
+ Log.d(TAG, "Failing single scan request ClientInfo not found " + handler);
+ return false;
+ }
+ if (settings.reportEvents == 0) {
+ settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
+ }
+ if (settings.periodInMs == 0) {
+ settings.periodInMs = 10000; // 10s - although second scan should never happen
+ }
+
+ logScanRequest("addSingleScanRequest", ci, handler, settings);
+ ci.addScanRequest(settings, handler);
+ if (resetBuckets()) {
+ /* reset periodInMs to 0 to indicate single shot scan */
+ settings.periodInMs = 0;
+ return true;
+ } else {
+ ci.removeScanRequest(handler);
+ localLog("Failing scan request because failed to reset scan");
return false;
}
}
void removeScanRequest(ClientInfo ci, int handler) {
- ci.removeScanRequest(handler);
- resetBuckets();
+ if (ci != null) {
+ logScanRequest("removeScanRequest", ci, handler, null);
+ ci.removeScanRequest(handler);
+ resetBuckets();
+ }
}
- ScanResult[] getScanResults(ClientInfo ci) {
- ScanResult results[] = WifiNative.getScanResults();
- ci.reportScanResults(results);
- return results;
+ boolean reportScanResults() {
+ ScanData results[] = WifiNative.getScanResults(/* flush = */ true);
+ Collection<ClientInfo> clients = mClients.values();
+ for (ClientInfo ci2 : clients) {
+ ci2.reportScanResults(results);
+ }
+
+ return true;
}
void resetHotlist() {
@@ -1080,7 +1316,7 @@
if (num_hotlist_ap == 0) {
WifiNative.resetHotlist();
} else {
- WifiScanner.BssidInfo bssidInfos[] = new WifiScanner.BssidInfo[num_hotlist_ap];
+ BssidInfo bssidInfos[] = new BssidInfo[num_hotlist_ap];
int index = 0;
for (ClientInfo ci : clients) {
Collection<WifiScanner.HotlistSettings> settings = ci.getHotlistSettings();
@@ -1220,12 +1456,12 @@
class DefaultState extends State {
@Override
public void enter() {
- if (DBG) Log.d(TAG, "Entering IdleState");
+ if (DBG) localLog("Entering IdleState");
}
@Override
public boolean processMessage(Message msg) {
- if (DBG) Log.d(TAG, "DefaultState state got " + msg);
+ if (DBG) localLog("DefaultState state got " + msg);
switch (msg.what) {
case WIFI_CHANGE_CMD_ENABLE :
transitionTo(mMovingState);
@@ -1250,24 +1486,24 @@
class StationaryState extends State {
@Override
public void enter() {
- if (DBG) Log.d(TAG, "Entering StationaryState");
+ if (DBG) localLog("Entering StationaryState");
reportWifiStabilized(mCurrentBssids);
}
@Override
public boolean processMessage(Message msg) {
- if (DBG) Log.d(TAG, "Stationary state got " + msg);
+ if (DBG) localLog("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");
+ if (DBG) localLog("Got wifi change detected");
reportWifiChanged((ScanResult[])msg.obj);
transitionTo(mMovingState);
break;
case WIFI_CHANGE_CMD_DISABLE:
- if (DBG) Log.d(TAG, "Got Disable Wifi Change");
+ if (DBG) localLog("Got Disable Wifi Change");
mCurrentBssids = null;
removeScanRequest();
untrackSignificantWifiChange();
@@ -1290,29 +1526,30 @@
@Override
public void enter() {
- if (DBG) Log.d(TAG, "Entering MovingState");
+ if (DBG) localLog("Entering MovingState");
issueFullScan();
}
@Override
public boolean processMessage(Message msg) {
- if (DBG) Log.d(TAG, "MovingState state got " + msg);
+ if (DBG) localLog("MovingState state got " + msg);
switch (msg.what) {
case WIFI_CHANGE_CMD_ENABLE :
// do nothing
break;
case WIFI_CHANGE_CMD_DISABLE:
- if (DBG) Log.d(TAG, "Got Disable Wifi Change");
+ if (DBG) localLog("Got Disable Wifi Change");
mCurrentBssids = null;
removeScanRequest();
untrackSignificantWifiChange();
transitionTo(mDefaultState);
break;
case WIFI_CHANGE_CMD_NEW_SCAN_RESULTS:
- if (DBG) Log.d(TAG, "Got scan results");
+ if (DBG) localLog("Got scan results");
if (mScanResultsPending) {
- if (DBG) Log.d(TAG, "reconfiguring scan");
- reconfigureScan((ScanResult[])msg.obj, STATIONARY_SCAN_PERIOD_MS);
+ if (DBG) localLog("reconfiguring scan");
+ reconfigureScan((ScanData[])msg.obj,
+ STATIONARY_SCAN_PERIOD_MS);
mWifiChangeDetected = false;
mAlarmManager.setExact(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + MOVING_STATE_TIMEOUT_MS,
@@ -1321,7 +1558,7 @@
}
break;
case WIFI_CHANGE_CMD_CONFIGURE:
- if (DBG) Log.d(TAG, "Got configuration from app");
+ if (DBG) localLog("Got configuration from app");
WifiScanner.WifiChangeSettings settings =
(WifiScanner.WifiChangeSettings) msg.obj;
reconfigureScan(settings);
@@ -1333,14 +1570,14 @@
mTimeoutIntent);
break;
case WIFI_CHANGE_CMD_CHANGE_DETECTED:
- if (DBG) Log.d(TAG, "Change detected");
+ if (DBG) localLog("Change detected");
mAlarmManager.cancel(mTimeoutIntent);
reportWifiChanged((ScanResult[])msg.obj);
mWifiChangeDetected = true;
issueFullScan();
break;
case WIFI_CHANGE_CMD_CHANGE_TIMEOUT:
- if (DBG) Log.d(TAG, "Got timeout event");
+ if (DBG) localLog("Got timeout event");
if (mWifiChangeDetected == false) {
transitionTo(mStationaryState);
}
@@ -1357,8 +1594,8 @@
}
void issueFullScan() {
- if (DBG) Log.d(TAG, "Issuing full scan");
- WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings();
+ if (DBG) localLog("Issuing full scan");
+ ScanSettings settings = new ScanSettings();
settings.band = WifiScanner.WIFI_BAND_BOTH;
settings.periodInMs = MOVING_SCAN_PERIOD_MS;
settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
@@ -1368,10 +1605,10 @@
}
- void reconfigureScan(ScanResult[] results, int period) {
+ void reconfigureScan(ScanData[] results, int period) {
// find brightest APs and set them as sentinels
if (results.length < MAX_APS_TO_TRACK) {
- Log.d(TAG, "too few APs (" + results.length + ") available to track wifi change");
+ localLog("too few APs (" + results.length + ") available to track wifi change");
return;
}
@@ -1379,7 +1616,7 @@
// remove duplicate BSSIDs
HashMap<String, ScanResult> bssidToScanResult = new HashMap<String, ScanResult>();
- for (ScanResult result : results) {
+ for (ScanResult result : results[0].getResults()) {
ScanResult saved = bssidToScanResult.get(result.BSSID);
if (saved == null) {
bssidToScanResult.put(result.BSSID, result);
@@ -1418,14 +1655,14 @@
}
}
- if (DBG) Log.d(TAG, "Found " + channels.size() + " channels");
+ if (DBG) localLog("Found " + channels.size() + " channels");
// set scanning schedule
- WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings();
+ ScanSettings settings = new ScanSettings();
settings.band = WifiScanner.WIFI_BAND_UNSPECIFIED;
- settings.channels = new WifiScanner.ChannelSpec[channels.size()];
+ settings.channels = new ChannelSpec[channels.size()];
for (int i = 0; i < channels.size(); i++) {
- settings.channels[i] = new WifiScanner.ChannelSpec(channels.get(i));
+ settings.channels[i] = new ChannelSpec(channels.get(i));
}
settings.periodInMs = period;
@@ -1436,17 +1673,17 @@
settings2.lostApSampleSize = 3;
settings2.unchangedSampleSize = 3;
settings2.minApsBreachingThreshold = 2;
- settings2.bssidInfos = new WifiScanner.BssidInfo[brightest.length];
+ settings2.bssidInfos = new BssidInfo[brightest.length];
for (int i = 0; i < brightest.length; i++) {
- WifiScanner.BssidInfo BssidInfo = new WifiScanner.BssidInfo();
+ BssidInfo BssidInfo = new BssidInfo();
BssidInfo.bssid = brightest[i].BSSID;
int threshold = (100 + brightest[i].level) / 32 + 2;
BssidInfo.low = brightest[i].level - threshold;
BssidInfo.high = brightest[i].level + threshold;
settings2.bssidInfos[i] = BssidInfo;
- if (DBG) Log.d(TAG, "Setting bssid=" + BssidInfo.bssid + ", " +
+ if (DBG) localLog("Setting bssid=" + BssidInfo.bssid + ", " +
"low=" + BssidInfo.low + ", high=" + BssidInfo.high);
}
@@ -1457,12 +1694,12 @@
void reconfigureScan(WifiScanner.WifiChangeSettings settings) {
if (settings.bssidInfos.length < MAX_APS_TO_TRACK) {
- Log.d(TAG, "too few APs (" + settings.bssidInfos.length
+ localLog("too few APs (" + settings.bssidInfos.length
+ ") available to track wifi change");
return;
}
- if (DBG) Log.d(TAG, "Setting configuration specified by app");
+ if (DBG) localLog("Setting configuration specified by app");
mCurrentBssids = new ScanResult[settings.bssidInfos.length];
HashSet<Integer> channels = new HashSet<Integer>();
@@ -1478,12 +1715,12 @@
removeScanRequest();
// set new scanning schedule
- WifiScanner.ScanSettings settings2 = new WifiScanner.ScanSettings();
+ ScanSettings settings2 = new ScanSettings();
settings2.band = WifiScanner.WIFI_BAND_UNSPECIFIED;
- settings2.channels = new WifiScanner.ChannelSpec[channels.size()];
+ settings2.channels = new ChannelSpec[channels.size()];
int i = 0;
for (Integer channel : channels) {
- settings2.channels[i++] = new WifiScanner.ChannelSpec(channel);
+ settings2.channels[i++] = new ChannelSpec(channel);
}
settings2.periodInMs = settings.periodInMs;
@@ -1495,11 +1732,11 @@
class ClientInfoLocal extends ClientInfo {
ClientInfoLocal() {
- super(null, null);
+ super(0, null, null);
}
@Override
- void deliverScanResults(int handler, ScanResult results[]) {
- if (DBG) Log.d(TAG, "Delivering messages directly");
+ void deliverScanResults(int handler, ScanData results[]) {
+ if (DBG) localLog("Delivering messages directly");
sendMessage(WIFI_CHANGE_CMD_NEW_SCAN_RESULTS, 0, 0, results);
}
@Override
@@ -1516,8 +1753,8 @@
ClientInfo mClientInfo = new ClientInfoLocal();
private static final int SCAN_COMMAND_ID = 1;
- void addScanRequest(WifiScanner.ScanSettings settings) {
- if (DBG) Log.d(TAG, "Starting scans");
+ void addScanRequest(ScanSettings settings) {
+ if (DBG) localLog("Starting scans");
Message msg = Message.obtain();
msg.what = WifiScanner.CMD_START_BACKGROUND_SCAN;
msg.arg2 = SCAN_COMMAND_ID;
@@ -1526,7 +1763,7 @@
}
void removeScanRequest() {
- if (DBG) Log.d(TAG, "Stopping scans");
+ if (DBG) localLog("Stopping scans");
Message msg = Message.obtain();
msg.what = WifiScanner.CMD_STOP_BACKGROUND_SCAN;
msg.arg2 = SCAN_COMMAND_ID;
@@ -1544,44 +1781,156 @@
}
- private static WifiScanner.ChannelSpec[] getChannelsForBand(int band) {
- int channels[] = WifiNative.getChannelsForBand(band);
- if (channels != null) {
- WifiScanner.ChannelSpec channelSpecs[] = new WifiScanner.ChannelSpec[channels.length];
- for (int i = 0; i < channels.length; i++) {
- channelSpecs[i] = new WifiScanner.ChannelSpec(channels[i]);
- }
- return channelSpecs;
- } else {
- return new WifiScanner.ChannelSpec[0];
+ private static ChannelSpec mChannels[][];
+
+ private static void copyChannels(
+ ChannelSpec channelSpec[], int offset, int channels[]) {
+ for (int i = 0; i < channels.length; i++) {
+ channelSpec[offset +i] = new ChannelSpec(channels[i]);
}
}
- private static int getBandFromChannels(WifiScanner.ChannelSpec[] channels) {
+ private static boolean initChannels() {
+ if (mChannels != null) {
+ /* already initialized */
+ return true;
+ }
+
+ int channels24[] = WifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_24_GHZ);
+ if (channels24 == null) {
+ loge("Could not get channels for 2.4 GHz");
+ return false;
+ }
+
+ int channels5[] = WifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ);
+ if (channels5 == null) {
+ loge("Could not get channels for 5 GHz");
+ return false;
+ }
+
+ int channelsDfs[] = WifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY);
+ if (channelsDfs == null) {
+ loge("Could not get channels for DFS");
+ return false;
+ }
+
+ mChannels = new ChannelSpec[8][];
+
+ mChannels[0] = new ChannelSpec[0];
+
+ mChannels[1] = new ChannelSpec[channels24.length];
+ copyChannels(mChannels[1], 0, channels24);
+
+ mChannels[2] = new ChannelSpec[channels5.length];
+ copyChannels(mChannels[2], 0, channels5);
+
+ mChannels[3] = new ChannelSpec[channels24.length + channels5.length];
+ copyChannels(mChannels[3], 0, channels24);
+ copyChannels(mChannels[3], channels24.length, channels5);
+
+ mChannels[4] = new ChannelSpec[channelsDfs.length];
+ copyChannels(mChannels[4], 0, channelsDfs);
+
+ mChannels[5] = new ChannelSpec[channels24.length + channelsDfs.length];
+ copyChannels(mChannels[5], 0, channels24);
+ copyChannels(mChannels[5], channels24.length, channelsDfs);
+
+ mChannels[6] = new ChannelSpec[channels5.length + channelsDfs.length];
+ copyChannels(mChannels[6], 0, channels5);
+ copyChannels(mChannels[6], channels5.length, channelsDfs);
+
+ mChannels[7] = new ChannelSpec[
+ channels24.length + channels5.length + channelsDfs.length];
+ copyChannels(mChannels[7], 0, channels24);
+ copyChannels(mChannels[7], channels24.length, channels5);
+ copyChannels(mChannels[7], channels24.length + channels5.length, channelsDfs);
+
+ return true;
+ }
+
+ private static ChannelSpec[] getChannelsForBand(int band) {
+ initChannels();
+
+ if (band < WifiScanner.WIFI_BAND_24_GHZ || band > WifiScanner.WIFI_BAND_BOTH_WITH_DFS)
+ /* invalid value for band */
+ return mChannels[0];
+ else
+ return mChannels[band];
+ }
+
+ private static boolean isDfs(int channel) {
+ ChannelSpec[] dfsChannels = getChannelsForBand(WifiScanner
+ .WIFI_BAND_5_GHZ_DFS_ONLY);
+ for (int i = 0; i < dfsChannels.length; i++) {
+ if (channel == dfsChannels[i].frequency) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static int getBandFromChannels(ChannelSpec[] channels) {
int band = WifiScanner.WIFI_BAND_UNSPECIFIED;
- for (WifiScanner.ChannelSpec channel : channels) {
+ for (ChannelSpec channel : channels) {
if (2400 <= channel.frequency && channel.frequency < 2500) {
band |= WifiScanner.WIFI_BAND_24_GHZ;
+ } else if ( isDfs(channel.frequency)) {
+ band |= WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY;
} 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 */
+ if (channel != null) {
+ if (2400 <= channel.frequency && channel.frequency < 2500) {
+ band |= WifiScanner.WIFI_BAND_24_GHZ;
+ } else if ( isDfs(channel.frequency)) {
+ band |= WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY;
+ } else if (5100 <= channel.frequency && channel.frequency < 6000) {
+ band |= WifiScanner.WIFI_BAND_5_GHZ;
+ }
}
}
return band;
}
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump WifiScanner from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " without permission "
+ + android.Manifest.permission.DUMP);
+ return;
+ }
+ mStateMachine.dump(fd, pw, args);
+ }
+
+ static String describe(ScanSettings scanSettings) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(" band:").append(scanSettings.band);
+ sb.append(" period:").append(scanSettings.periodInMs);
+ sb.append(" reportEvents:").append(scanSettings.reportEvents);
+ sb.append(" numBssidsPerScan:").append(scanSettings.numBssidsPerScan);
+ sb.append(" maxScansToCache:").append(scanSettings.maxScansToCache).append("\n");
+
+ sb.append(" channels: ");
+
+ if (scanSettings.channels != null) {
+ for (int i = 0; i < scanSettings.channels.length; i++) {
+ sb.append(scanSettings.channels[i].frequency);
+ sb.append(" ");
+ }
+ }
+ sb.append("\n");
+ return sb.toString();
+ }
+
}
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index cc5824e..ca85fbc 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -30,45 +30,71 @@
import android.net.ConnectivityManager;
import android.net.DhcpInfo;
import android.net.DhcpResults;
-import android.net.IpConfiguration.ProxySettings;
-import android.net.LinkAddress;
+import android.net.Network;
+import android.net.NetworkScorerAppManager;
import android.net.NetworkUtils;
-import android.net.RouteInfo;
-import android.net.wifi.*;
+import android.net.Uri;
+import android.net.wifi.BatchedScanResult;
+import android.net.wifi.BatchedScanSettings;
import android.net.wifi.IWifiManager;
+import android.net.wifi.ScanResult;
+import android.net.wifi.ScanSettings;
+import android.net.wifi.WifiActivityEnergyInfo;
+import android.net.wifi.WifiChannel;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConnectionStatistics;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiLinkLayerStats;
+import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
+import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
-import java.io.FileNotFoundException;
-import java.io.BufferedReader;
-import java.io.FileDescriptor;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.lang.Override;
-import java.net.InetAddress;
-import java.net.Inet4Address;
-import java.util.ArrayList;
-import java.util.List;
-
import com.android.internal.R;
import com.android.internal.app.IBatteryStats;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.AsyncChannel;
import com.android.server.am.BatteryStatsService;
+import com.android.server.wifi.configparse.ConfigBuilder;
+
+import org.xml.sax.SAXException;
+
+import java.io.BufferedReader;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.PKIXParameters;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import static com.android.server.wifi.WifiController.CMD_AIRPLANE_TOGGLED;
import static com.android.server.wifi.WifiController.CMD_BATTERY_CHANGED;
@@ -89,6 +115,7 @@
public final class WifiServiceImpl extends IWifiManager.Stub {
private static final String TAG = "WifiService";
private static final boolean DBG = true;
+ private static final boolean VDBG = false;
final WifiStateMachine mWifiStateMachine;
@@ -109,7 +136,9 @@
private int mMulticastDisabled;
private final IBatteryStats mBatteryStats;
+ private final PowerManager mPowerManager;
private final AppOpsManager mAppOps;
+ private final UserManager mUserManager;
private String mInterfaceName;
@@ -123,8 +152,6 @@
/* Tracks the persisted states for wi-fi & airplane mode */
final WifiSettingsStore mSettingsStore;
- final boolean mBatchedScanSupported;
-
/**
* Asynchronous channel to WifiStateMachine
*/
@@ -173,31 +200,21 @@
WifiConfiguration config = (WifiConfiguration) msg.obj;
int networkId = msg.arg1;
if (msg.what == WifiManager.SAVE_NETWORK) {
- if (config != null) {
- if (config.networkId == WifiConfiguration.INVALID_NETWORK_ID) {
- config.creatorUid = Binder.getCallingUid();
- } else {
- config.lastUpdateUid = Binder.getCallingUid();
- }
- }
Slog.e("WiFiServiceImpl ", "SAVE"
+ " nid=" + Integer.toString(networkId)
- + " uid=" + Integer.toString(config.creatorUid)
- + "/" + Integer.toString(config.lastUpdateUid));
+ + " uid=" + msg.sendingUid
+ + " name="
+ + mContext.getPackageManager().getNameForUid(msg.sendingUid));
}
if (msg.what == WifiManager.CONNECT_NETWORK) {
- if (config != null) {
- if (config.networkId == WifiConfiguration.INVALID_NETWORK_ID) {
- config.creatorUid = Binder.getCallingUid();
- } else {
- config.lastUpdateUid = Binder.getCallingUid();
- }
- }
Slog.e("WiFiServiceImpl ", "CONNECT "
+ " nid=" + Integer.toString(networkId)
- + " uid=" + Binder.getCallingUid());
+ + " uid=" + msg.sendingUid
+ + " name="
+ + mContext.getPackageManager().getNameForUid(msg.sendingUid));
}
- if (config != null && config.isValid()) {
+
+ if (config != null && isValid(config)) {
if (DBG) Slog.d(TAG, "Connect with config" + config);
mWifiStateMachine.sendMessage(Message.obtain(msg));
} else if (config == null
@@ -306,7 +323,9 @@
mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName, mTrafficPoller);
mWifiStateMachine.enableRssiPolling(true);
mBatteryStats = BatteryStatsService.getService();
+ mPowerManager = context.getSystemService(PowerManager.class);
mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
+ mUserManager = UserManager.get(mContext);
mNotificationController = new WifiNotificationController(mContext, mWifiStateMachine);
mSettingsStore = new WifiSettingsStore(mContext);
@@ -316,9 +335,6 @@
mClientHandler = new ClientHandler(wifiThread.getLooper());
mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper());
mWifiController = new WifiController(mContext, this, wifiThread.getLooper());
-
- mBatchedScanSupported = mContext.getResources().getBoolean(
- R.bool.config_wifi_batched_scan_supported);
}
@@ -350,6 +366,8 @@
// can result in race conditions when apps toggle wifi in the background
// without active user involvement. Always receive broadcasts.
registerForBroadcasts();
+ registerForPackageOrUserRemoval();
+ mInIdleMode = mPowerManager.isDeviceIdleMode();
mWifiController.start();
@@ -390,6 +408,7 @@
// Start a location scan.
// L release: A location scan is implemented as a normal scan and avoids scanning DFS channels
+ // Deprecated: Will soon remove implementation
public void startLocationRestrictedScan(WorkSource workSource) {
enforceChangePermission();
enforceLocationHardwarePermission();
@@ -421,6 +440,23 @@
*/
public void startScan(ScanSettings settings, WorkSource workSource) {
enforceChangePermission();
+ synchronized (this) {
+ if (mInIdleMode) {
+ // Need to send an immediate scan result broadcast in case the
+ // caller is waiting for a result ..
+
+ // clear calling identity to send broadcast
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mWifiStateMachine.sendScanResultsAvailableBroadcast(/* scanSucceeded = */ false);
+ } finally {
+ // restore calling identity
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ mScanPending = true;
+ return;
+ }
+ }
if (settings != null) {
settings = new ScanSettings(settings);
if (!settings.isValid()) {
@@ -438,42 +474,11 @@
settings, workSource);
}
- private class BatchedScanRequest extends DeathRecipient {
- final BatchedScanSettings settings;
- final int uid;
- final int pid;
- final WorkSource workSource;
-
- BatchedScanRequest(BatchedScanSettings settings, IBinder binder, WorkSource ws) {
- super(0, null, binder, null);
- this.settings = settings;
- this.uid = getCallingUid();
- this.pid = getCallingPid();
- workSource = ws;
- }
- public void binderDied() {
- stopBatchedScan(settings, uid, pid);
- }
- public String toString() {
- return "BatchedScanRequest{settings=" + settings + ", binder=" + mBinder + "}";
- }
-
- public boolean isSameApp(int uid, int pid) {
- return (this.uid == uid && this.pid == pid);
- }
- }
-
- private final List<BatchedScanRequest> mBatchedScanners = new ArrayList<BatchedScanRequest>();
-
public boolean isBatchedScanSupported() {
- return mBatchedScanSupported;
+ return false;
}
- public void pollBatchedScan() {
- enforceChangePermission();
- if (mBatchedScanSupported == false) return;
- mWifiStateMachine.requestBatchedScanPoll();
- }
+ public void pollBatchedScan() { }
public String getWpsNfcConfigurationToken(int netId) {
enforceConnectivityInternalPermission();
@@ -485,145 +490,36 @@
*/
public boolean requestBatchedScan(BatchedScanSettings requested, IBinder binder,
WorkSource workSource) {
- enforceChangePermission();
- if (workSource != null) {
- enforceWorkSourcePermission();
- // WifiManager currently doesn't use names, so need to clear names out of the
- // supplied WorkSource to allow future WorkSource combining.
- workSource.clearNames();
- }
- if (mBatchedScanSupported == false) return false;
- requested = new BatchedScanSettings(requested);
- if (requested.isInvalid()) return false;
- BatchedScanRequest r = new BatchedScanRequest(requested, binder, workSource);
- synchronized(mBatchedScanners) {
- mBatchedScanners.add(r);
- resolveBatchedScannersLocked();
- }
- return true;
+ return false;
}
public List<BatchedScanResult> getBatchedScanResults(String callingPackage) {
- enforceAccessPermission();
- if (mBatchedScanSupported == false) return new ArrayList<BatchedScanResult>();
- int uid = Binder.getCallingUid();
- int userId = UserHandle.getCallingUserId();
- boolean hasInteractUsersFull = checkInteractAcrossUsersFull();
- long ident = Binder.clearCallingIdentity();
- try {
- if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage)
- != AppOpsManager.MODE_ALLOWED) {
- return new ArrayList<BatchedScanResult>();
- }
- if (!isCurrentProfile(userId) && !hasInteractUsersFull) {
- return new ArrayList<BatchedScanResult>();
- }
- return mWifiStateMachine.syncGetBatchedScanResultsList();
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ return null;
}
- public void stopBatchedScan(BatchedScanSettings settings) {
- enforceChangePermission();
- if (mBatchedScanSupported == false) return;
- stopBatchedScan(settings, getCallingUid(), getCallingPid());
- }
+ public void stopBatchedScan(BatchedScanSettings settings) { }
- private void stopBatchedScan(BatchedScanSettings settings, int uid, int pid) {
- ArrayList<BatchedScanRequest> found = new ArrayList<BatchedScanRequest>();
- synchronized(mBatchedScanners) {
- for (BatchedScanRequest r : mBatchedScanners) {
- if (r.isSameApp(uid, pid) && (settings == null || settings.equals(r.settings))) {
- found.add(r);
- if (settings != null) break;
- }
- }
- for (BatchedScanRequest r : found) {
- mBatchedScanners.remove(r);
- }
- if (found.size() != 0) {
- resolveBatchedScannersLocked();
- }
- }
- }
+ boolean mInIdleMode;
+ boolean mScanPending;
- private void resolveBatchedScannersLocked() {
- BatchedScanSettings setting = new BatchedScanSettings();
- WorkSource responsibleWorkSource = null;
- int responsibleUid = 0;
- double responsibleCsph = 0; // Channel Scans Per Hour
-
- if (mBatchedScanners.size() == 0) {
- mWifiStateMachine.setBatchedScanSettings(null, 0, 0, null);
- return;
- }
- for (BatchedScanRequest r : mBatchedScanners) {
- BatchedScanSettings s = r.settings;
-
- // evaluate responsibility
- int currentChannelCount;
- int currentScanInterval;
- double currentCsph;
-
- if (s.channelSet == null || s.channelSet.isEmpty()) {
- // all channels - 11 B and 9 A channels roughly.
- currentChannelCount = 9 + 11;
- } else {
- currentChannelCount = s.channelSet.size();
- // these are rough est - no real need to correct for reg-domain;
- if (s.channelSet.contains("A")) currentChannelCount += (9 - 1);
- if (s.channelSet.contains("B")) currentChannelCount += (11 - 1);
-
- }
- if (s.scanIntervalSec == BatchedScanSettings.UNSPECIFIED) {
- currentScanInterval = BatchedScanSettings.DEFAULT_INTERVAL_SEC;
- } else {
- currentScanInterval = s.scanIntervalSec;
- }
- currentCsph = 60 * 60 * currentChannelCount / currentScanInterval;
-
- if (currentCsph > responsibleCsph) {
- responsibleUid = r.uid;
- responsibleWorkSource = r.workSource;
- responsibleCsph = currentCsph;
- }
-
- if (s.maxScansPerBatch != BatchedScanSettings.UNSPECIFIED &&
- s.maxScansPerBatch < setting.maxScansPerBatch) {
- setting.maxScansPerBatch = s.maxScansPerBatch;
- }
- if (s.maxApPerScan != BatchedScanSettings.UNSPECIFIED &&
- (setting.maxApPerScan == BatchedScanSettings.UNSPECIFIED ||
- s.maxApPerScan > setting.maxApPerScan)) {
- setting.maxApPerScan = s.maxApPerScan;
- }
- if (s.scanIntervalSec != BatchedScanSettings.UNSPECIFIED &&
- s.scanIntervalSec < setting.scanIntervalSec) {
- setting.scanIntervalSec = s.scanIntervalSec;
- }
- if (s.maxApForDistance != BatchedScanSettings.UNSPECIFIED &&
- (setting.maxApForDistance == BatchedScanSettings.UNSPECIFIED ||
- s.maxApForDistance > setting.maxApForDistance)) {
- setting.maxApForDistance = s.maxApForDistance;
- }
- if (s.channelSet != null && s.channelSet.size() != 0) {
- if (setting.channelSet == null || setting.channelSet.size() != 0) {
- if (setting.channelSet == null) setting.channelSet = new ArrayList<String>();
- for (String i : s.channelSet) {
- if (setting.channelSet.contains(i) == false) setting.channelSet.add(i);
+ void handleIdleModeChanged() {
+ boolean doScan = false;
+ synchronized (this) {
+ boolean idle = mPowerManager.isDeviceIdleMode();
+ if (mInIdleMode != idle) {
+ mInIdleMode = idle;
+ if (!idle) {
+ if (mScanPending) {
+ mScanPending = false;
+ doScan = true;
}
- } // else, ignore the constraint - we already use all channels
- } else {
- if (setting.channelSet == null || setting.channelSet.size() != 0) {
- setting.channelSet = new ArrayList<String>();
}
}
}
-
- setting.constrain();
- mWifiStateMachine.setBatchedScanSettings(setting, responsibleUid, (int)responsibleCsph,
- responsibleWorkSource);
+ if (doScan) {
+ // Someone requested a scan while we were idle; do a full scan now.
+ startScan(null, null);
+ }
}
private void enforceAccessPermission() {
@@ -633,7 +529,7 @@
private void enforceChangePermission() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
- "WifiService");
+ "WifiService");
}
private void enforceLocationHardwarePermission() {
@@ -719,12 +615,11 @@
public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
enforceChangePermission();
ConnectivityManager.enforceTetherChangePermission(mContext);
- UserManager um = UserManager.get(mContext);
- if (um.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) {
+ if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) {
throw new SecurityException("DISALLOW_CONFIG_TETHERING is enabled for this user.");
}
// null wifiConfig is a meaningful input for CMD_SET_AP
- if (wifiConfig == null || wifiConfig.isValid()) {
+ if (wifiConfig == null || isValid(wifiConfig)) {
mWifiController.obtainMessage(CMD_SET_AP, enabled ? 1 : 0, 0, wifiConfig).sendToTarget();
} else {
Slog.e(TAG, "Invalid WifiConfiguration");
@@ -754,6 +649,25 @@
}
/**
+ * see {@link WifiManager#buildWifiConfig()}
+ * @return a WifiConfiguration.
+ */
+ public WifiConfiguration buildWifiConfig(String uriString, String mimeType, byte[] data) {
+ if (mimeType.equals(ConfigBuilder.WifiConfigType)) {
+ try {
+ return ConfigBuilder.buildConfig(uriString, data, mContext);
+ }
+ catch (IOException | GeneralSecurityException | SAXException e) {
+ Log.e(TAG, "Failed to parse wi-fi configuration: " + e);
+ }
+ }
+ else {
+ Log.i(TAG, "Unknown wi-fi config type: " + mimeType);
+ }
+ return null;
+ }
+
+ /**
* see {@link WifiManager#setWifiApConfiguration(WifiConfiguration)}
* @param wifiConfig WifiConfiguration details for soft access point
*/
@@ -761,7 +675,7 @@
enforceChangePermission();
if (wifiConfig == null)
return;
- if (wifiConfig.isValid()) {
+ if (isValid(wifiConfig)) {
mWifiStateMachine.setWifiApConfiguration(wifiConfig);
} else {
Slog.e(TAG, "Invalid WifiConfiguration");
@@ -816,7 +730,7 @@
}
/**
- * see {@link android.net.wifi.WifiAdapter#reportActivityInfo}
+ * see {@link android.net.wifi.WifiManager#getControllerActivityEnergyInfo(int)}
*/
public WifiActivityEnergyInfo reportActivityInfo() {
enforceAccessPermission();
@@ -825,11 +739,39 @@
if (mWifiStateMachineChannel != null) {
stats = mWifiStateMachine.syncGetLinkLayerStats(mWifiStateMachineChannel);
if (stats != null) {
+ final long rxIdleCurrent = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_wifi_idle_receive_cur_ma);
+ final long rxCurrent = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_wifi_active_rx_cur_ma);
+ final long txCurrent = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_wifi_tx_cur_ma);
+ final double voltage = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_wifi_operating_voltage_mv)
+ / 1000.0;
+
+ final long rxIdleTime = stats.on_time - stats.tx_time - stats.rx_time;
+ final long energyUsed = (long)((stats.tx_time * txCurrent +
+ stats.rx_time * rxCurrent +
+ rxIdleTime * rxIdleCurrent) * voltage);
+ if (VDBG || rxIdleTime < 0 || stats.on_time < 0 || stats.tx_time < 0 ||
+ stats.rx_time < 0 || energyUsed < 0) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(" rxIdleCur=" + rxIdleCurrent);
+ sb.append(" rxCur=" + rxCurrent);
+ sb.append(" txCur=" + txCurrent);
+ sb.append(" voltage=" + voltage);
+ sb.append(" on_time=" + stats.on_time);
+ sb.append(" tx_time=" + stats.tx_time);
+ sb.append(" rx_time=" + stats.rx_time);
+ sb.append(" rxIdleTime=" + rxIdleTime);
+ sb.append(" energy=" + energyUsed);
+ Log.e(TAG, " reportActivityInfo: " + sb.toString());
+ }
+
// Convert the LinkLayerStats into EnergyActivity
- energyInfo = new WifiActivityEnergyInfo(
+ energyInfo = new WifiActivityEnergyInfo(SystemClock.elapsedRealtime(),
WifiActivityEnergyInfo.STACK_STATE_STATE_IDLE, stats.tx_time,
- stats.rx_time, stats.on_time - stats.tx_time - stats.rx_time,
- 0 /* TBD */);
+ stats.rx_time, rxIdleTime, energyUsed);
}
return energyInfo;
} else {
@@ -869,15 +811,47 @@
}
/**
+ * Returns a WifiConfiguration matching this ScanResult
+ * @param scanResult scanResult that represents the BSSID
+ * @return {@link WifiConfiguration} that matches this BSSID or null
+ */
+ public WifiConfiguration getMatchingWifiConfig(ScanResult scanResult) {
+ enforceAccessPermission();
+ return mWifiStateMachine.syncGetMatchingWifiConfig(scanResult, mWifiStateMachineChannel);
+ }
+
+
+ /**
* see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)}
* @return the supplicant-assigned identifier for the new or updated
* network if the operation succeeds, or {@code -1} if it fails
*/
public int addOrUpdateNetwork(WifiConfiguration config) {
enforceChangePermission();
- if (config.isValid()) {
+ if (isValid(config) && isValidPasspoint(config)) {
+
+ WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
+
+ if (config.isPasspoint() &&
+ (enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS ||
+ enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS)) {
+ try {
+ verifyCert(enterpriseConfig.getCaCertificate());
+ } catch (CertPathValidatorException cpve) {
+ Slog.e(TAG, "CA Cert " +
+ enterpriseConfig.getCaCertificate().getSubjectX500Principal() +
+ " untrusted: " + cpve.getMessage());
+ return -1;
+ } catch (GeneralSecurityException | IOException e) {
+ Slog.e(TAG, "Failed to verify certificate" +
+ enterpriseConfig.getCaCertificate().getSubjectX500Principal() +
+ ": " + e);
+ return -1;
+ }
+ }
+
//TODO: pass the Uid the WifiStateMachine as a message parameter
- Slog.e("addOrUpdateNetwork", " uid = " + Integer.toString(Binder.getCallingUid())
+ Slog.i("addOrUpdateNetwork", " uid = " + Integer.toString(Binder.getCallingUid())
+ " SSID " + config.SSID
+ " nid=" + Integer.toString(config.networkId));
if (config.networkId == WifiConfiguration.INVALID_NETWORK_ID) {
@@ -897,7 +871,21 @@
}
}
- /**
+ public static void verifyCert(X509Certificate caCert)
+ throws GeneralSecurityException, IOException {
+ CertificateFactory factory = CertificateFactory.getInstance("X.509");
+ CertPathValidator validator =
+ CertPathValidator.getInstance(CertPathValidator.getDefaultType());
+ CertPath path = factory.generateCertPath(
+ Arrays.asList(caCert));
+ KeyStore ks = KeyStore.getInstance("AndroidCAStore");
+ ks.load(null, null);
+ PKIXParameters params = new PKIXParameters(ks);
+ params.setRevocationEnabled(false);
+ validator.validate(path, params);
+ }
+
+ /**
* See {@link android.net.wifi.WifiManager#removeNetwork(int)}
* @param netId the integer that identifies the network configuration
* to the supplicant
@@ -975,9 +963,20 @@
enforceAccessPermission();
int userId = UserHandle.getCallingUserId();
int uid = Binder.getCallingUid();
+ boolean canReadPeerMacAddresses = checkPeersMacAddress();
+ boolean isActiveNetworkScorer =
+ NetworkScorerAppManager.isCallerActiveScorer(mContext, uid);
boolean hasInteractUsersFull = checkInteractAcrossUsersFull();
long ident = Binder.clearCallingIdentity();
try {
+ if (!canReadPeerMacAddresses && !isActiveNetworkScorer
+ && !isLocationEnabled()) {
+ return new ArrayList<ScanResult>();
+ }
+ if (!canReadPeerMacAddresses && !isActiveNetworkScorer
+ && !checkCallerCanAccessScanResults(callingPackage, uid)) {
+ return new ArrayList<ScanResult>();
+ }
if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage)
!= AppOpsManager.MODE_ALLOWED) {
return new ArrayList<ScanResult>();
@@ -991,6 +990,11 @@
}
}
+ private boolean isLocationEnabled() {
+ return Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.LOCATION_MODE,
+ Settings.Secure.LOCATION_MODE_OFF) != Settings.Secure.LOCATION_MODE_OFF;
+ }
+
/**
* Returns true if the caller holds INTERACT_ACROSS_USERS_FULL.
*/
@@ -1001,6 +1005,14 @@
}
/**
+ * Returns true if the caller holds PEERS_MAC_ADDRESS.
+ */
+ private boolean checkPeersMacAddress() {
+ return mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.PEERS_MAC_ADDRESS) == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
* Returns true if the calling user is the current one or a profile of the
* current user..
*/
@@ -1009,7 +1021,7 @@
if (userId == currentUser) {
return true;
}
- List<UserInfo> profiles = UserManager.get(mContext).getProfiles(currentUser);
+ List<UserInfo> profiles = mUserManager.getProfiles(currentUser);
for (UserInfo user : profiles) {
if (userId == user.id) {
return true;
@@ -1031,7 +1043,7 @@
if (userId == ownerUser) {
return true;
}
- List<UserInfo> profiles = UserManager.get(mContext).getProfiles(ownerUser);
+ List<UserInfo> profiles = mUserManager.getProfiles(ownerUser);
for (UserInfo profile : profiles) {
if (userId == profile.id) {
return true;
@@ -1083,6 +1095,15 @@
}
}
+ /**
+ * Get the country code
+ * @return ISO 3166 country code.
+ */
+ public String getCountryCode() {
+ enforceConnectivityInternalPermission();
+ String country = mWifiStateMachine.getCountryCode();
+ return country;
+ }
/**
* Set the operational frequency band
* @param band One of
@@ -1350,6 +1371,8 @@
} else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
boolean emergencyMode = intent.getBooleanExtra("phoneinECMState", false);
mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, emergencyMode ? 1 : 0, 0);
+ } else if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) {
+ handleIdleModeChanged();
}
}
};
@@ -1380,9 +1403,41 @@
intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
+ intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
mContext.registerReceiver(mReceiver, intentFilter);
}
+ private void registerForPackageOrUserRemoval() {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ intentFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case Intent.ACTION_PACKAGE_REMOVED: {
+ if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ return;
+ }
+ int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ Uri uri = intent.getData();
+ if (uid == -1 || uri == null) {
+ return;
+ }
+ String pkgName = uri.getSchemeSpecificPart();
+ mWifiStateMachine.removeAppConfigs(pkgName, uid);
+ break;
+ }
+ case Intent.ACTION_USER_REMOVED: {
+ int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ mWifiStateMachine.removeUserConfigs(userHandle);
+ break;
+ }
+ }
+ }
+ }, UserHandle.ALL, intentFilter, null, null);
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -1398,6 +1453,8 @@
Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0));
pw.println("mMulticastEnabled " + mMulticastEnabled);
pw.println("mMulticastDisabled " + mMulticastDisabled);
+ pw.println("mInIdleMode " + mInIdleMode);
+ pw.println("mScanPending " + mScanPending);
mWifiController.dump(fd, pw, args);
mSettingsStore.dump(fd, pw, args);
mNotificationController.dump(fd, pw, args);
@@ -1439,6 +1496,12 @@
pw.println("Locks held:");
mLocks.dump(pw);
+ pw.println("Multicast Locks held:");
+ for (Multicaster l : mMulticasters) {
+ pw.print(" ");
+ pw.println(l);
+ }
+
mWifiWatchdogStateMachine.dump(fd, pw, args);
pw.println();
mWifiStateMachine.dump(fd, pw, args);
@@ -1840,6 +1903,25 @@
return mWifiStateMachine.getAllowScansWithTraffic();
}
+ public boolean enableAutoJoinWhenAssociated(boolean enabled) {
+ enforceChangePermission();
+ return mWifiStateMachine.enableAutoJoinWhenAssociated(enabled);
+ }
+
+ public boolean getEnableAutoJoinWhenAssociated() {
+ enforceAccessPermission();
+ return mWifiStateMachine.getEnableAutoJoinWhenAssociated();
+ }
+ public void setHalBasedAutojoinOffload(int enabled) {
+ enforceConnectivityInternalPermission();
+ mWifiStateMachine.setHalBasedAutojoinOffload(enabled);
+ }
+
+ public int getHalBasedAutojoinOffload() {
+ enforceAccessPermission();
+ return mWifiStateMachine.getHalBasedAutojoinOffload();
+ }
+
/* Return the Wifi Connection statistics object */
public WifiConnectionStatistics getConnectionStatistics() {
enforceAccessPermission();
@@ -1851,4 +1933,162 @@
return null;
}
}
+
+ public void factoryReset() {
+ enforceConnectivityInternalPermission();
+
+ if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)) {
+ return;
+ }
+
+ if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) {
+ // Turn mobile hotspot off
+ setWifiApEnabled(null, false);
+ }
+
+ if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI)) {
+ // Enable wifi
+ setWifiEnabled(true);
+ // Delete all Wifi SSIDs
+ List<WifiConfiguration> networks = getConfiguredNetworks();
+ if (networks != null) {
+ for (WifiConfiguration config : networks) {
+ removeNetwork(config.networkId);
+ }
+ saveConfiguration();
+ }
+ }
+ }
+
+ /* private methods */
+ static boolean logAndReturnFalse(String s) {
+ Log.d(TAG, s);
+ return false;
+ }
+
+ public static boolean isValid(WifiConfiguration config) {
+ String validity = checkValidity(config);
+ return validity == null || logAndReturnFalse(validity);
+ }
+
+ public static boolean isValidPasspoint(WifiConfiguration config) {
+ String validity = checkPasspointValidity(config);
+ return validity == null || logAndReturnFalse(validity);
+ }
+
+ public static String checkValidity(WifiConfiguration config) {
+ if (config.allowedKeyManagement == null)
+ return "allowed kmgmt";
+
+ if (config.allowedKeyManagement.cardinality() > 1) {
+ if (config.allowedKeyManagement.cardinality() != 2) {
+ return "cardinality != 2";
+ }
+ if (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)) {
+ return "not WPA_EAP";
+ }
+ if ((!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X))
+ && (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK))) {
+ return "not PSK or 8021X";
+ }
+ }
+ return null;
+ }
+
+ public static String checkPasspointValidity(WifiConfiguration config) {
+ if (!TextUtils.isEmpty(config.FQDN)) {
+ /* this is passpoint configuration; it must not have an SSID */
+ if (!TextUtils.isEmpty(config.SSID)) {
+ return "SSID not expected for Passpoint: '" + config.SSID +
+ "' FQDN " + toHexString(config.FQDN);
+ }
+ /* this is passpoint configuration; it must have a providerFriendlyName */
+ if (TextUtils.isEmpty(config.providerFriendlyName)) {
+ return "no provider friendly name";
+ }
+ WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
+ /* this is passpoint configuration; it must have enterprise config */
+ if (enterpriseConfig == null
+ || enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.NONE ) {
+ return "no enterprise config";
+ }
+ if ((enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS ||
+ enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS ||
+ enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP) &&
+ enterpriseConfig.getCaCertificate() == null) {
+ return "no CA certificate";
+ }
+ }
+ return null;
+ }
+
+ public Network getCurrentNetwork() {
+ enforceAccessPermission();
+ return mWifiStateMachine.getCurrentNetwork();
+ }
+
+ public static String toHexString(String s) {
+ if (s == null) {
+ return "null";
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append('\'').append(s).append('\'');
+ for (int n = 0; n < s.length(); n++) {
+ sb.append(String.format(" %02x", s.charAt(n) & 0xffff));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Checks that calling process has android.Manifest.permission.ACCESS_COARSE_LOCATION or
+ * android.Manifest.permission.ACCESS_FINE_LOCATION and a corresponding app op is allowed
+ */
+ private boolean checkCallerCanAccessScanResults(String callingPackage, int uid) {
+ if (ActivityManager.checkUidPermission(Manifest.permission.ACCESS_FINE_LOCATION, uid)
+ == PackageManager.PERMISSION_GRANTED
+ && isAppOppAllowed(AppOpsManager.OP_FINE_LOCATION, callingPackage, uid)) {
+ return true;
+ }
+
+ if (ActivityManager.checkUidPermission(Manifest.permission.ACCESS_COARSE_LOCATION, uid)
+ == PackageManager.PERMISSION_GRANTED
+ && isAppOppAllowed(AppOpsManager.OP_COARSE_LOCATION, callingPackage, uid)) {
+ return true;
+ }
+ // Enforce location permission for apps targeting M and later versions
+ boolean enforceLocationPermission = true;
+ try {
+ enforceLocationPermission = mContext.getPackageManager().getApplicationInfo(
+ callingPackage, 0).targetSdkVersion >= Build.VERSION_CODES.M;
+ } catch (PackageManager.NameNotFoundException e) {
+ // In case of exception, enforce permission anyway
+ }
+ if (enforceLocationPermission) {
+ throw new SecurityException("Need ACCESS_COARSE_LOCATION or "
+ + "ACCESS_FINE_LOCATION permission to get scan results");
+ }
+ // Pre-M apps running in the foreground should continue getting scan results
+ if (isForegroundApp(callingPackage)) {
+ return true;
+ }
+ Log.e(TAG, "Permission denial: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION "
+ + "permission to get scan results");
+ return false;
+ }
+
+ private boolean isAppOppAllowed(int op, String callingPackage, int uid) {
+ return mAppOps.noteOp(op, uid, callingPackage) == AppOpsManager.MODE_ALLOWED;
+ }
+
+ /**
+ * Return true if the specified package name is a foreground app.
+ *
+ * @param pkgName application package name.
+ */
+ private boolean isForegroundApp(String pkgName) {
+ ActivityManager am = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
+ return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
+ }
+
}
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index fb9e9fd..4bc62b3 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -31,6 +31,7 @@
*/
import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
+import android.Manifest;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -40,12 +41,18 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.net.ConnectivityManager;
import android.net.DhcpResults;
+import android.net.BaseDhcpStateMachine;
import android.net.DhcpStateMachine;
+import android.net.Network;
+import android.net.dhcp.DhcpClient;
import android.net.InterfaceConfiguration;
+import android.net.IpReachabilityMonitor;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkAgent;
@@ -58,8 +65,6 @@
import android.net.RouteInfo;
import android.net.StaticIpConfiguration;
import android.net.TrafficStats;
-import android.net.wifi.BatchedScanResult;
-import android.net.wifi.BatchedScanSettings;
import android.net.wifi.RssiPacketCountInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.ScanSettings;
@@ -71,12 +76,14 @@
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiLinkLayerStats;
import android.net.wifi.WifiManager;
+import android.net.wifi.WifiScanner;
import android.net.wifi.WifiSsid;
import android.net.wifi.WpsInfo;
import android.net.wifi.WpsResult;
import android.net.wifi.WpsResult.Status;
import android.net.wifi.p2p.IWifiP2pManager;
import android.os.BatteryStats;
+import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.INetworkManagementService;
@@ -104,6 +111,9 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.net.NetlinkTracker;
+import com.android.server.wifi.hotspot2.NetworkDetail;
+import com.android.server.wifi.hotspot2.SupplicantBridge;
+import com.android.server.wifi.hotspot2.Utils;
import com.android.server.wifi.p2p.WifiP2pServiceImpl;
import java.io.BufferedReader;
@@ -115,14 +125,15 @@
import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Queue;
+import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
@@ -136,14 +147,16 @@
*
* @hide
*/
-public class WifiStateMachine extends StateMachine {
+public class WifiStateMachine extends StateMachine implements WifiNative.WifiPnoEventHandler {
private static final String NETWORKTYPE = "WIFI";
private static final String NETWORKTYPE_UNTRUSTED = "WIFI_UT";
private static boolean DBG = false;
private static boolean VDBG = false;
private static boolean VVDBG = false;
+ private static boolean USE_PAUSE_SCANS = false;
private static boolean mLogMessages = false;
+ private static final String TAG = "WifiStateMachine";
private static final int ONE_HOUR_MILLI = 1000 * 60 * 60;
@@ -165,8 +178,11 @@
protected void loge(String s) {
Log.e(getName(), s);
}
+ protected void logd(String s) {
+ Log.d(getName(), s);
+ }
protected void log(String s) {;
- Log.e(getName(), s);
+ Log.d(getName(), s);
}
private WifiMonitor mWifiMonitor;
@@ -175,35 +191,29 @@
private WifiAutoJoinController mWifiAutoJoinController;
private INetworkManagementService mNwService;
private ConnectivityManager mCm;
-
+ private WifiLogger mWifiLogger;
+ private WifiApConfigStore mWifiApConfigStore;
private final boolean mP2pSupported;
private final AtomicBoolean mP2pConnected = new AtomicBoolean(false);
private boolean mTemporarilyDisconnectWifi = false;
private final String mPrimaryDeviceType;
/* Scan results handling */
- private List<ScanResult> mScanResults = new ArrayList<ScanResult>();
+ private List<ScanDetail> mScanResults = new ArrayList<>();
private static final Pattern scanResultPattern = Pattern.compile("\t+");
private static final int SCAN_RESULT_CACHE_SIZE = 160;
- private final LruCache<String, ScanResult> mScanResultCache;
+ private final LruCache<NetworkDetail, ScanDetail> mScanResultCache;
// For debug, number of known scan results that were found as part of last scan result event,
// as well the number of scans results returned by the supplicant with that message
private int mNumScanResultsKnown;
private int mNumScanResultsReturned;
- /* Batch scan results */
- private final List<BatchedScanResult> mBatchedScanResults =
- new ArrayList<BatchedScanResult>();
- private int mBatchedScanOwnerUid = UNKNOWN_SCAN_SOURCE;
- private int mExpectedBatchedScans = 0;
- private long mBatchedScanMinPollTime = 0;
-
private boolean mScreenOn = false;
/* Chipset supports background scan */
private final boolean mBackgroundScanSupported;
- private String mInterfaceName;
+ private final String mInterfaceName;
/* Tethering interface could be separate from wlan interface */
private String mTetherInterfaceName;
@@ -212,12 +222,66 @@
private int mLastNetworkId; // The network Id we successfully joined
private boolean linkDebouncing = false;
+ private boolean mHalBasedPnoDriverSupported = false;
+
+ // Below booleans are configurations coming from the Developper Settings
+ private boolean mEnableAssociatedNetworkSwitchingInDevSettings = true;
+ private boolean mHalBasedPnoEnableInDevSettings = false;
+
+
+ private int mHalFeatureSet = 0;
+ private static int mPnoResultFound = 0;
+
+ @Override
+ public void onPnoNetworkFound(ScanResult results[]) {
+ if (DBG) {
+ Log.e(TAG, "onPnoNetworkFound event received num = " + results.length);
+ for (int i = 0; i < results.length; i++) {
+ Log.e(TAG, results[i].toString());
+ }
+ }
+ sendMessage(CMD_PNO_NETWORK_FOUND, results.length, 0, results);
+ }
+
+ public void processPnoNetworkFound(ScanResult results[]) {
+ ScanSettings settings = new ScanSettings();
+ settings.channelSet = new ArrayList<WifiChannel>();
+ StringBuilder sb = new StringBuilder();
+ sb.append("");
+ for (int i=0; i<results.length; i++) {
+ WifiChannel channel = new WifiChannel();
+ channel.freqMHz = results[i].frequency;
+ settings.channelSet.add(channel);
+ sb.append(results[i].SSID).append(" ");
+ }
+
+ stopPnoOffload();
+
+ Log.e(TAG, "processPnoNetworkFound starting scan cnt=" + mPnoResultFound);
+ startScan(PNO_NETWORK_FOUND_SOURCE, mPnoResultFound, settings, null);
+ mPnoResultFound ++;
+ //sendMessage(CMD_SCAN_RESULTS_AVAILABLE);
+ int delay = 30 * 1000;
+ // reconfigure Pno after 1 minutes if we're still in disconnected state
+ sendMessageDelayed(CMD_RESTART_AUTOJOIN_OFFLOAD, delay,
+ mRestartAutoJoinOffloadCounter, " processPnoNetworkFound " + sb.toString(),
+ (long)delay);
+ mRestartAutoJoinOffloadCounter++;
+ }
+
+ public void registerNetworkDisabled(int netId) {
+ // Restart legacy PNO and autojoin offload if needed
+ sendMessage(CMD_RESTART_AUTOJOIN_OFFLOAD, 0,
+ mRestartAutoJoinOffloadCounter, " registerNetworkDisabled " + netId);
+ mRestartAutoJoinOffloadCounter++;
+ }
+
// Testing various network disconnect cases by sending lots of spurious
// disconnect to supplicant
private boolean testNetworkDisconnect = false;
private boolean mEnableRssiPolling = false;
- private boolean mEnableBackgroundScan = false;
+ private boolean mLegacyPnoEnabled = false;
private int mRssiPollToken = 0;
/* 3 operational states for STA operation: CONNECT_MODE, SCAN_ONLY_MODE, SCAN_ONLY_WIFI_OFF_MODE
* In CONNECT_MODE, the STA can scan and connect to an access point
@@ -237,15 +301,13 @@
private static final int SET_ALLOW_UNTRUSTED_SOURCE = -4;
private static final int ENABLE_WIFI = -5;
public static final int DFS_RESTRICTED_SCAN_REQUEST = -6;
+ public static final int PNO_NETWORK_FOUND_SOURCE = -7;
private static final int SCAN_REQUEST_BUFFER_MAX_SIZE = 10;
private static final String CUSTOMIZED_SCAN_SETTING = "customized_scan_settings";
private static final String CUSTOMIZED_SCAN_WORKSOURCE = "customized_scan_worksource";
private static final String SCAN_REQUEST_TIME = "scan_request_time";
- private static final String BATCHED_SETTING = "batched_settings";
- private static final String BATCHED_WORKSOURCE = "batched_worksource";
-
/* Tracks if state machine has received any screen state change broadcast yet.
* We can miss one of these at boot.
*/
@@ -298,6 +360,12 @@
private int mDriverStartToken = 0;
/**
+ * Don't select new network when previous network selection is
+ * pending connection for this much time
+ */
+ private static final int CONNECT_TIMEOUT_MSEC = 3000;
+
+ /**
* The link properties of the wifi interface.
* Do not modify this directly; use updateLinkProperties instead.
*/
@@ -313,11 +381,13 @@
private final Object mDhcpResultsLock = new Object();
private DhcpResults mDhcpResults;
+
+ // NOTE: Do not return to clients - use #getWiFiInfoForUid(int)
private WifiInfo mWifiInfo;
private NetworkInfo mNetworkInfo;
private NetworkCapabilities mNetworkCapabilities;
private SupplicantStateTracker mSupplicantStateTracker;
- private DhcpStateMachine mDhcpStateMachine;
+ private BaseDhcpStateMachine mDhcpStateMachine;
private boolean mDhcpActive = false;
private int mWifiLinkLayerStatsSupported = 4; // Temporary disable
@@ -346,6 +416,9 @@
// Used as debug to indicate which configuration last was removed
private WifiConfiguration lastForgetConfigurationAttempt = null;
+ //Random used by softAP channel Selection
+ private static Random mRandom = new Random(Calendar.getInstance().getTimeInMillis());
+
boolean isRoaming() {
return mAutoRoaming == WifiAutoJoinController.AUTO_JOIN_ROAMING
|| mAutoRoaming == WifiAutoJoinController.AUTO_JOIN_EXTENDED_ROAMING;
@@ -367,12 +440,11 @@
if (!mTargetRoamBSSID.equals("any") && bssid.equals("any")) {
// Changing to ANY
if (!mWifiConfigStore.roamOnAny) {
- ret = false; // Nothing to do
+ ret = false; // Nothing to do
}
}
if (VDBG) {
- loge("autoRoamSetBSSID " + bssid
- + " key=" + config.configKey());
+ logd("autoRoamSetBSSID " + bssid + " key=" + config.configKey());
}
config.autoJoinBSSID = bssid;
mTargetRoamBSSID = bssid;
@@ -381,17 +453,71 @@
}
/**
+ * Save the UID correctly depending on if this is a new or existing network.
+ * @return true if operation is authorized, false otherwise
+ */
+ boolean recordUidIfAuthorized(WifiConfiguration config, int uid, boolean onlyAnnotate) {
+ if (!mWifiConfigStore.isNetworkConfigured(config)) {
+ config.creatorUid = uid;
+ config.creatorName = mContext.getPackageManager().getNameForUid(uid);
+ } else if (!mWifiConfigStore.canModifyNetwork(uid, config, onlyAnnotate)) {
+ return false;
+ }
+
+ config.lastUpdateUid = uid;
+ config.lastUpdateName = mContext.getPackageManager().getNameForUid(uid);
+
+ return true;
+
+ }
+
+ /**
+ * Checks to see if user has specified if the apps configuration is connectable.
+ * If the user hasn't specified we query the user and return true.
+ *
+ * @param message The message to be deferred
+ * @param netId Network id of the configuration to check against
+ * @param allowOverride If true we won't defer to the user if the uid of the message holds the
+ * CONFIG_OVERRIDE_PERMISSION
+ * @return True if we are waiting for user feedback or netId is invalid. False otherwise.
+ */
+ boolean deferForUserInput(Message message, int netId, boolean allowOverride){
+ final WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(netId);
+
+ // We can only evaluate saved configurations.
+ if (config == null) {
+ logd("deferForUserInput: configuration for netId=" + netId + " not stored");
+ return true;
+ }
+
+ switch (config.userApproved) {
+ case WifiConfiguration.USER_APPROVED:
+ case WifiConfiguration.USER_BANNED:
+ return false;
+ case WifiConfiguration.USER_PENDING:
+ default: // USER_UNSPECIFIED
+ /* the intention was to ask user here; but a dialog box is *
+ * too invasive; so we are going to allow connection for now */
+ config.userApproved = WifiConfiguration.USER_APPROVED;
+ return false;
+ }
+ }
+
+ /**
* Subset of link properties coming from netlink.
* Currently includes IPv4 and IPv6 addresses. In the future will also include IPv6 DNS servers
* and domains obtained from router advertisements (RFC 6106).
*/
private NetlinkTracker mNetlinkTracker;
+ private IpReachabilityMonitor mIpReachabilityMonitor;
+
private AlarmManager mAlarmManager;
private PendingIntent mScanIntent;
private PendingIntent mDriverStopIntent;
- private PendingIntent mBatchedScanIntervalIntent;
+ private PendingIntent mPnoIntent;
+ private int mDisconnectedPnoAlarmCount = 0;
/* Tracks current frequency mode */
private AtomicInteger mFrequencyBand = new AtomicInteger(WifiManager.WIFI_FREQUENCY_BAND_AUTO);
@@ -407,11 +533,15 @@
private AsyncChannel mWifiP2pChannel;
private AsyncChannel mWifiApConfigChannel;
+ private WifiScanner mWifiScanner;
+
private int mConnectionRequests = 0;
private WifiNetworkFactory mNetworkFactory;
private UntrustedWifiNetworkFactory mUntrustedNetworkFactory;
private WifiNetworkAgent mNetworkAgent;
+ private String[] mWhiteListedSsids = null;
+
// Keep track of various statistics, for retrieval by System Apps, i.e. under @SystemApi
// We should really persist that into the networkHistory.txt file, and read it back when
// WifiStateMachine starts up
@@ -423,87 +553,87 @@
/* The base for wifi message types */
static final int BASE = Protocol.BASE_WIFI;
/* Start the supplicant */
- static final int CMD_START_SUPPLICANT = BASE + 11;
+ static final int CMD_START_SUPPLICANT = BASE + 11;
/* Stop the supplicant */
- static final int CMD_STOP_SUPPLICANT = BASE + 12;
+ static final int CMD_STOP_SUPPLICANT = BASE + 12;
/* Start the driver */
- static final int CMD_START_DRIVER = BASE + 13;
+ static final int CMD_START_DRIVER = BASE + 13;
/* Stop the driver */
- static final int CMD_STOP_DRIVER = BASE + 14;
+ static final int CMD_STOP_DRIVER = BASE + 14;
/* Indicates Static IP succeeded */
- static final int CMD_STATIC_IP_SUCCESS = BASE + 15;
+ static final int CMD_STATIC_IP_SUCCESS = BASE + 15;
/* Indicates Static IP failed */
- static final int CMD_STATIC_IP_FAILURE = BASE + 16;
+ static final int CMD_STATIC_IP_FAILURE = BASE + 16;
/* Indicates supplicant stop failed */
- static final int CMD_STOP_SUPPLICANT_FAILED = BASE + 17;
+ static final int CMD_STOP_SUPPLICANT_FAILED = BASE + 17;
/* Delayed stop to avoid shutting down driver too quick*/
- static final int CMD_DELAYED_STOP_DRIVER = BASE + 18;
+ static final int CMD_DELAYED_STOP_DRIVER = BASE + 18;
/* A delayed message sent to start driver when it fail to come up */
- static final int CMD_DRIVER_START_TIMED_OUT = BASE + 19;
+ static final int CMD_DRIVER_START_TIMED_OUT = BASE + 19;
/* Start the soft access point */
- static final int CMD_START_AP = BASE + 21;
+ static final int CMD_START_AP = BASE + 21;
/* Indicates soft ap start succeeded */
- static final int CMD_START_AP_SUCCESS = BASE + 22;
+ static final int CMD_START_AP_SUCCESS = BASE + 22;
/* Indicates soft ap start failed */
- static final int CMD_START_AP_FAILURE = BASE + 23;
+ static final int CMD_START_AP_FAILURE = BASE + 23;
/* Stop the soft access point */
- static final int CMD_STOP_AP = BASE + 24;
+ static final int CMD_STOP_AP = BASE + 24;
/* Set the soft access point configuration */
- static final int CMD_SET_AP_CONFIG = BASE + 25;
+ static final int CMD_SET_AP_CONFIG = BASE + 25;
/* Soft access point configuration set completed */
- static final int CMD_SET_AP_CONFIG_COMPLETED = BASE + 26;
+ static final int CMD_SET_AP_CONFIG_COMPLETED = BASE + 26;
/* Request the soft access point configuration */
- static final int CMD_REQUEST_AP_CONFIG = BASE + 27;
+ static final int CMD_REQUEST_AP_CONFIG = BASE + 27;
/* Response to access point configuration request */
- static final int CMD_RESPONSE_AP_CONFIG = BASE + 28;
+ static final int CMD_RESPONSE_AP_CONFIG = BASE + 28;
/* Invoked when getting a tether state change notification */
- static final int CMD_TETHER_STATE_CHANGE = BASE + 29;
+ static final int CMD_TETHER_STATE_CHANGE = BASE + 29;
/* A delayed message sent to indicate tether state change failed to arrive */
- static final int CMD_TETHER_NOTIFICATION_TIMED_OUT = BASE + 30;
+ static final int CMD_TETHER_NOTIFICATION_TIMED_OUT = BASE + 30;
- static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE = BASE + 31;
+ static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE = BASE + 31;
/* Supplicant commands */
/* Is supplicant alive ? */
- static final int CMD_PING_SUPPLICANT = BASE + 51;
+ static final int CMD_PING_SUPPLICANT = BASE + 51;
/* Add/update a network configuration */
- static final int CMD_ADD_OR_UPDATE_NETWORK = BASE + 52;
+ static final int CMD_ADD_OR_UPDATE_NETWORK = BASE + 52;
/* Delete a network */
- static final int CMD_REMOVE_NETWORK = BASE + 53;
+ static final int CMD_REMOVE_NETWORK = BASE + 53;
/* Enable a network. The device will attempt a connection to the given network. */
- static final int CMD_ENABLE_NETWORK = BASE + 54;
+ static final int CMD_ENABLE_NETWORK = BASE + 54;
/* Enable all networks */
- static final int CMD_ENABLE_ALL_NETWORKS = BASE + 55;
+ static final int CMD_ENABLE_ALL_NETWORKS = BASE + 55;
/* Blacklist network. De-prioritizes the given BSSID for connection. */
- static final int CMD_BLACKLIST_NETWORK = BASE + 56;
+ static final int CMD_BLACKLIST_NETWORK = BASE + 56;
/* Clear the blacklist network list */
- static final int CMD_CLEAR_BLACKLIST = BASE + 57;
+ static final int CMD_CLEAR_BLACKLIST = BASE + 57;
/* Save configuration */
- static final int CMD_SAVE_CONFIG = BASE + 58;
+ static final int CMD_SAVE_CONFIG = BASE + 58;
/* Get configured networks */
- static final int CMD_GET_CONFIGURED_NETWORKS = BASE + 59;
+ static final int CMD_GET_CONFIGURED_NETWORKS = BASE + 59;
/* Get available frequencies */
- static final int CMD_GET_CAPABILITY_FREQ = BASE + 60;
+ static final int CMD_GET_CAPABILITY_FREQ = BASE + 60;
/* Get adaptors */
- static final int CMD_GET_SUPPORTED_FEATURES = BASE + 61;
+ static final int CMD_GET_SUPPORTED_FEATURES = BASE + 61;
/* Get configured networks with real preSharedKey */
- static final int CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS = BASE + 62;
+ static final int CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS = BASE + 62;
/* Get Link Layer Stats thru HAL */
- static final int CMD_GET_LINK_LAYER_STATS = BASE + 63;
+ static final int CMD_GET_LINK_LAYER_STATS = BASE + 63;
/* Supplicant commands after driver start*/
/* Initiate a scan */
- static final int CMD_START_SCAN = BASE + 71;
+ static final int CMD_START_SCAN = BASE + 71;
/* Set operational mode. CONNECT, SCAN ONLY, SCAN_ONLY with Wi-Fi off mode */
- static final int CMD_SET_OPERATIONAL_MODE = BASE + 72;
+ static final int CMD_SET_OPERATIONAL_MODE = BASE + 72;
/* Disconnect from a network */
- static final int CMD_DISCONNECT = BASE + 73;
+ static final int CMD_DISCONNECT = BASE + 73;
/* Reconnect to a network */
- static final int CMD_RECONNECT = BASE + 74;
+ static final int CMD_RECONNECT = BASE + 74;
/* Reassociate to a network */
- static final int CMD_REASSOCIATE = BASE + 75;
+ static final int CMD_REASSOCIATE = BASE + 75;
/* Get Connection Statistis */
- static final int CMD_GET_CONNECTION_STATISTICS = BASE + 76;
+ static final int CMD_GET_CONNECTION_STATISTICS = BASE + 76;
/* Controls suspend mode optimizations
*
@@ -516,38 +646,66 @@
* - turn off roaming
* - DTIM wake up settings
*/
- static final int CMD_SET_HIGH_PERF_MODE = BASE + 77;
+ static final int CMD_SET_HIGH_PERF_MODE = BASE + 77;
/* Set the country code */
- static final int CMD_SET_COUNTRY_CODE = BASE + 80;
+ static final int CMD_SET_COUNTRY_CODE = BASE + 80;
/* Enables RSSI poll */
- static final int CMD_ENABLE_RSSI_POLL = BASE + 82;
+ static final int CMD_ENABLE_RSSI_POLL = BASE + 82;
/* RSSI poll */
- static final int CMD_RSSI_POLL = BASE + 83;
+ static final int CMD_RSSI_POLL = BASE + 83;
/* Set up packet filtering */
- static final int CMD_START_PACKET_FILTERING = BASE + 84;
+ static final int CMD_START_PACKET_FILTERING = BASE + 84;
/* Clear packet filter */
- static final int CMD_STOP_PACKET_FILTERING = BASE + 85;
+ static final int CMD_STOP_PACKET_FILTERING = BASE + 85;
/* Enable suspend mode optimizations in the driver */
- static final int CMD_SET_SUSPEND_OPT_ENABLED = BASE + 86;
+ static final int CMD_SET_SUSPEND_OPT_ENABLED = BASE + 86;
/* Delayed NETWORK_DISCONNECT */
- static final int CMD_DELAYED_NETWORK_DISCONNECT = BASE + 87;
+ static final int CMD_DELAYED_NETWORK_DISCONNECT = BASE + 87;
/* When there are no saved networks, we do a periodic scan to notify user of
* an open network */
- static final int CMD_NO_NETWORKS_PERIODIC_SCAN = BASE + 88;
+ static final int CMD_NO_NETWORKS_PERIODIC_SCAN = BASE + 88;
/* Test network Disconnection NETWORK_DISCONNECT */
- static final int CMD_TEST_NETWORK_DISCONNECT = BASE + 89;
+ static final int CMD_TEST_NETWORK_DISCONNECT = BASE + 89;
+
private int testNetworkDisconnectCounter = 0;
/* arg1 values to CMD_STOP_PACKET_FILTERING and CMD_START_PACKET_FILTERING */
- static final int MULTICAST_V6 = 1;
- static final int MULTICAST_V4 = 0;
+ static final int MULTICAST_V6 = 1;
+ static final int MULTICAST_V4 = 0;
- /* Set the frequency band */
- static final int CMD_SET_FREQUENCY_BAND = BASE + 90;
+ /* Set the frequency band */
+ static final int CMD_SET_FREQUENCY_BAND = BASE + 90;
/* Enable TDLS on a specific MAC address */
- static final int CMD_ENABLE_TDLS = BASE + 92;
+ static final int CMD_ENABLE_TDLS = BASE + 92;
/* DHCP/IP configuration watchdog */
- static final int CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER = BASE + 93;
+ static final int CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER = BASE + 93;
+
+ /**
+ * Watchdog for protecting against b/16823537
+ * Leave time for 4-way handshake to succeed
+ */
+ static final int ROAM_GUARD_TIMER_MSEC = 15000;
+
+ int roamWatchdogCount = 0;
+ /* Roam state watchdog */
+ static final int CMD_ROAM_WATCHDOG_TIMER = BASE + 94;
+ /* Screen change intent handling */
+ static final int CMD_SCREEN_STATE_CHANGED = BASE + 95;
+
+ /* Disconnecting state watchdog */
+ static final int CMD_DISCONNECTING_WATCHDOG_TIMER = BASE + 96;
+
+ /* Remove a packages associated configrations */
+ static final int CMD_REMOVE_APP_CONFIGURATIONS = BASE + 97;
+
+ /* Disable an ephemeral network */
+ static final int CMD_DISABLE_EPHEMERAL_NETWORK = BASE + 98;
+
+ /* Get matching network */
+ static final int CMD_GET_MATCHING_CONFIG = BASE + 99;
+
+ /* alert from firmware */
+ static final int CMD_FIRMWARE_ALERT = BASE + 100;
/**
* Make this timer 40 seconds, which is about the normal DHCP timeout.
@@ -560,84 +718,82 @@
/* Commands from/to the SupplicantStateTracker */
/* Reset the supplicant state tracker */
- static final int CMD_RESET_SUPPLICANT_STATE = BASE + 111;
-
-
- /**
- * Watchdog for protecting against b/16823537
- * Leave time for 4-ways handshake to succeed
- */
- static final int ROAM_GUARD_TIMER_MSEC = 15000;
-
- int roamWatchdogCount = 0;
- /* Roam state watchdog */
- static final int CMD_ROAM_WATCHDOG_TIMER = BASE + 94;
- /* Screen change intent handling */
- static final int CMD_SCREEN_STATE_CHANGED = BASE + 95;
+ static final int CMD_RESET_SUPPLICANT_STATE = BASE + 111;
int disconnectingWatchdogCount = 0;
static final int DISCONNECTING_GUARD_TIMER_MSEC = 5000;
- /* Disconnecting state watchdog */
- static final int CMD_DISCONNECTING_WATCHDOG_TIMER = BASE + 96;
-
- /* Disable an ephemeral network */
- static final int CMD_DISABLE_EPHEMERAL_NETWORK = BASE + 98;
-
/* P2p commands */
/* We are ok with no response here since we wont do much with it anyway */
- public static final int CMD_ENABLE_P2P = BASE + 131;
+ public static final int CMD_ENABLE_P2P = BASE + 131;
/* In order to shut down supplicant cleanly, we wait till p2p has
* been disabled */
- public static final int CMD_DISABLE_P2P_REQ = BASE + 132;
- public static final int CMD_DISABLE_P2P_RSP = BASE + 133;
+ public static final int CMD_DISABLE_P2P_REQ = BASE + 132;
+ public static final int CMD_DISABLE_P2P_RSP = BASE + 133;
- public static final int CMD_BOOT_COMPLETED = BASE + 134;
-
- /* change the batch scan settings.
- * arg1 = responsible UID
- * arg2 = csph (channel scans per hour)
- * obj = bundle with the new settings and the optional worksource
- */
- public static final int CMD_SET_BATCHED_SCAN = BASE + 135;
- public static final int CMD_START_NEXT_BATCHED_SCAN = BASE + 136;
- public static final int CMD_POLL_BATCHED_SCAN = BASE + 137;
+ public static final int CMD_BOOT_COMPLETED = BASE + 134;
/* We now have a valid IP configuration. */
- static final int CMD_IP_CONFIGURATION_SUCCESSFUL = BASE + 138;
+ static final int CMD_IP_CONFIGURATION_SUCCESSFUL = BASE + 138;
/* We no longer have a valid IP configuration. */
- static final int CMD_IP_CONFIGURATION_LOST = BASE + 139;
+ static final int CMD_IP_CONFIGURATION_LOST = BASE + 139;
/* Link configuration (IP address, DNS, ...) changes notified via netlink */
- static final int CMD_UPDATE_LINKPROPERTIES = BASE + 140;
+ static final int CMD_UPDATE_LINKPROPERTIES = BASE + 140;
/* Supplicant is trying to associate to a given BSSID */
- static final int CMD_TARGET_BSSID = BASE + 141;
+ static final int CMD_TARGET_BSSID = BASE + 141;
/* Reload all networks and reconnect */
- static final int CMD_RELOAD_TLS_AND_RECONNECT = BASE + 142;
+ static final int CMD_RELOAD_TLS_AND_RECONNECT = BASE + 142;
- static final int CMD_AUTO_CONNECT = BASE + 143;
+ static final int CMD_AUTO_CONNECT = BASE + 143;
- static final int network_status_unwanted_disconnect = 0;
- static final int network_status_unwanted_disable_autojoin = 1;
+ private static final int NETWORK_STATUS_UNWANTED_DISCONNECT = 0;
+ private static final int NETWORK_STATUS_UNWANTED_VALIDATION_FAILED = 1;
+ private static final int NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN = 2;
- static final int CMD_UNWANTED_NETWORK = BASE + 144;
+ static final int CMD_UNWANTED_NETWORK = BASE + 144;
- static final int CMD_AUTO_ROAM = BASE + 145;
+ static final int CMD_AUTO_ROAM = BASE + 145;
- static final int CMD_AUTO_SAVE_NETWORK = BASE + 146;
+ static final int CMD_AUTO_SAVE_NETWORK = BASE + 146;
- static final int CMD_ASSOCIATED_BSSID = BASE + 147;
+ static final int CMD_ASSOCIATED_BSSID = BASE + 147;
- static final int CMD_NETWORK_STATUS = BASE + 148;
+ static final int CMD_NETWORK_STATUS = BASE + 148;
+
+ /* A layer 3 neighbor on the Wi-Fi link became unreachable. */
+ static final int CMD_IP_REACHABILITY_LOST = BASE + 149;
+
+ /* Remove a packages associated configrations */
+ static final int CMD_REMOVE_USER_CONFIGURATIONS = BASE + 152;
+
+ static final int CMD_ACCEPT_UNVALIDATED = BASE + 153;
+
+ /* used to restart PNO when it was stopped due to association attempt */
+ static final int CMD_RESTART_AUTOJOIN_OFFLOAD = BASE + 154;
+
+ static int mRestartAutoJoinOffloadCounter = 0;
+
+ /* used to log if PNO was started */
+ static final int CMD_STARTED_PNO_DBG = BASE + 155;
+
+ static final int CMD_PNO_NETWORK_FOUND = BASE + 156;
+
+ /* used to log if PNO was started */
+ static final int CMD_UPDATE_ASSOCIATED_SCAN_PERMISSION = BASE + 158;
+
+ /* used to log if GSCAN was started */
+ static final int CMD_STARTED_GSCAN_DBG = BASE + 159;
+
/* Wifi state machine modes of operation */
/* CONNECT_MODE - connect to any 'known' AP when it becomes available */
- public static final int CONNECT_MODE = 1;
+ public static final int CONNECT_MODE = 1;
/* SCAN_ONLY_MODE - don't connect to any APs; scan, but only while apps hold lock */
- public static final int SCAN_ONLY_MODE = 2;
+ public static final int SCAN_ONLY_MODE = 2;
/* SCAN_ONLY_WITH_WIFI_OFF - scan, but don't connect to any APs */
- public static final int SCAN_ONLY_WITH_WIFI_OFF_MODE = 3;
+ public static final int SCAN_ONLY_WITH_WIFI_OFF_MODE = 3;
private static final int SUCCESS = 1;
private static final int FAILURE = -1;
@@ -649,9 +805,9 @@
*/
private int mSuspendOptNeedsDisabled = 0;
- private static final int SUSPEND_DUE_TO_DHCP = 1;
- private static final int SUSPEND_DUE_TO_HIGH_PERF = 1<<1;
- private static final int SUSPEND_DUE_TO_SCREEN = 1<<2;
+ private static final int SUSPEND_DUE_TO_DHCP = 1;
+ private static final int SUSPEND_DUE_TO_HIGH_PERF = 1 << 1;
+ private static final int SUSPEND_DUE_TO_SCREEN = 1 << 2;
/* Tracks if user has enabled suspend optimizations through settings */
private AtomicBoolean mUserWantsSuspendOpt = new AtomicBoolean(true);
@@ -665,7 +821,11 @@
*/
private final int mDefaultFrameworkScanIntervalMs;
- private int mDisconnectedScanPeriodMs = 10000;
+
+ /**
+ * Scan period for the NO_NETWORKS_PERIIDOC_SCAN_FEATURE
+ */
+ private final int mNoNetworksPeriodicScan;
/**
* Supplicant scan interval in milliseconds.
@@ -690,7 +850,7 @@
* autojoin while connected with screen lit
* Max time is 5 minutes
*/
- private static final long maxFullBandConnectedTimeIntervalMilli = 1000 * 60 * 5;
+ private static final long maxFullBandConnectedTimeIntervalMilli = 1000 * 60 * 5;
/**
* Minimum time interval between enabling all networks.
@@ -710,13 +870,14 @@
private int mDelayedStopCounter;
private boolean mInDelayedStop = false;
- // sometimes telephony gives us this data before boot is complete and we can't store it
- // until after, so the write is deferred
- private volatile String mPersistedCountryCode;
+ // there is a delay between StateMachine change country code and Supplicant change country code
+ // here save the current WifiStateMachine set country code
+ private volatile String mSetCountryCode = null;
// Supplicant doesn't like setting the same country code multiple times (it may drop
- // currently connected network), so we save the country code here to avoid redundency
- private String mLastSetCountryCode;
+ // currently connected network), so we save the current device set country code here to avoid
+ // redundency
+ private String mDriverSetCountryCode = null;
/* Default parent state */
private State mDefaultState = new DefaultState();
@@ -773,9 +934,40 @@
/* Waiting for untether confirmation before stopping soft Ap */
private State mUntetheringState = new UntetheringState();
+
+
+ private class WifiScanListener implements WifiScanner.ScanListener {
+ @Override
+ public void onSuccess() {
+ Log.e(TAG, "WifiScanListener onSuccess");
+ };
+ @Override
+ public void onFailure(int reason, String description) {
+ Log.e(TAG, "WifiScanListener onFailure");
+ };
+ @Override
+ public void onPeriodChanged(int periodInMs) {
+ Log.e(TAG, "WifiScanListener onPeriodChanged period=" + periodInMs);
+ }
+ @Override
+ public void onResults(WifiScanner.ScanData[] results) {
+ Log.e(TAG, "WifiScanListener onResults2 " + results.length);
+ }
+ @Override
+ public void onFullResult(ScanResult fullScanResult) {
+ Log.e(TAG, "WifiScanListener onFullResult " + fullScanResult.toString());
+ }
+
+ WifiScanListener() {}
+ }
+
+ WifiScanListener mWifiScanListener = new WifiScanListener();
+
+
private class TetherStateChange {
ArrayList<String> available;
ArrayList<String> active;
+
TetherStateChange(ArrayList<String> av, ArrayList<String> ac) {
available = av;
active = ac;
@@ -786,50 +978,42 @@
int networkId;
int protocol;
String ssid;
- String[] challenges;
- }
-
- public static class SimAuthResponseData {
- int id;
- String Kc1;
- String SRES1;
- String Kc2;
- String SRES2;
- String Kc3;
- String SRES3;
+ // EAP-SIM: data[] contains the 3 rand, one for each of the 3 challenges
+ // EAP-AKA/AKA': data[] contains rand & authn couple for the single challenge
+ String[] data;
}
/**
* One of {@link WifiManager#WIFI_STATE_DISABLED},
- * {@link WifiManager#WIFI_STATE_DISABLING},
- * {@link WifiManager#WIFI_STATE_ENABLED},
- * {@link WifiManager#WIFI_STATE_ENABLING},
- * {@link WifiManager#WIFI_STATE_UNKNOWN}
- *
+ * {@link WifiManager#WIFI_STATE_DISABLING},
+ * {@link WifiManager#WIFI_STATE_ENABLED},
+ * {@link WifiManager#WIFI_STATE_ENABLING},
+ * {@link WifiManager#WIFI_STATE_UNKNOWN}
*/
private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
/**
* One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
- * {@link WifiManager#WIFI_AP_STATE_DISABLING},
- * {@link WifiManager#WIFI_AP_STATE_ENABLED},
- * {@link WifiManager#WIFI_AP_STATE_ENABLING},
- * {@link WifiManager#WIFI_AP_STATE_FAILED}
- *
+ * {@link WifiManager#WIFI_AP_STATE_DISABLING},
+ * {@link WifiManager#WIFI_AP_STATE_ENABLED},
+ * {@link WifiManager#WIFI_AP_STATE_ENABLING},
+ * {@link WifiManager#WIFI_AP_STATE_FAILED}
*/
private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
private static final int SCAN_REQUEST = 0;
private static final String ACTION_START_SCAN =
- "com.android.server.WifiManager.action.START_SCAN";
+ "com.android.server.WifiManager.action.START_SCAN";
+
+ private static final int PNO_START_REQUEST = 0;
+ private static final String ACTION_START_PNO =
+ "com.android.server.WifiManager.action.START_PNO";
private static final String DELAYED_STOP_COUNTER = "DelayedStopCounter";
private static final int DRIVER_STOP_REQUEST = 0;
private static final String ACTION_DELAYED_DRIVER_STOP =
- "com.android.server.WifiManager.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.
*/
@@ -852,17 +1036,6 @@
private final IBatteryStats mBatteryStats;
- private BatchedScanSettings mBatchedScanSettings = null;
-
- /**
- * Track the worksource/cost of the current settings and track what's been noted
- * to the battery stats, so we can mark the end of the previous when changing.
- */
- private WorkSource mBatchedScanWorkSource = null;
- private int mBatchedScanCsph = 0;
- private WorkSource mNotedBatchedScanWorkSource = null;
- private int mNotedBatchedScanCsph = 0;
-
private String mTcpBufferSizes = null;
// Used for debug and stats gathering
@@ -870,10 +1043,17 @@
final static int frameworkMinScanIntervalSaneValue = 10000;
+ boolean mPnoEnabled;
+ boolean mLazyRoamEnabled;
+ long mGScanStartTimeMilli;
+ long mGScanPeriodMilli;
+
public WifiStateMachine(Context context, String wlanInterface,
- WifiTrafficPoller trafficPoller){
+ WifiTrafficPoller trafficPoller) {
super("WifiStateMachine");
mContext = context;
+ mSetCountryCode = Settings.Global.getString(
+ mContext.getContentResolver(), Settings.Global.WIFI_COUNTRY_CODE);
mInterfaceName = wlanInterface;
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
@@ -886,17 +1066,21 @@
PackageManager.FEATURE_WIFI_DIRECT);
mWifiNative = new WifiNative(mInterfaceName);
- mWifiConfigStore = new WifiConfigStore(context, mWifiNative);
+ mWifiConfigStore = new WifiConfigStore(context,this, mWifiNative);
mWifiAutoJoinController = new WifiAutoJoinController(context, this,
mWifiConfigStore, mWifiConnectionStatistics, mWifiNative);
mWifiMonitor = new WifiMonitor(this, mWifiNative);
+ mWifiLogger = new WifiLogger(this);
+
mWifiInfo = new WifiInfo();
mSupplicantStateTracker = new SupplicantStateTracker(context, this, mWifiConfigStore,
getHandler());
mLinkProperties = new LinkProperties();
IBinder s1 = ServiceManager.getService(Context.WIFI_P2P_SERVICE);
- mWifiP2pServiceImpl = (WifiP2pServiceImpl)IWifiP2pManager.Stub.asInterface(s1);
+ mWifiP2pServiceImpl = (WifiP2pServiceImpl) IWifiP2pManager.Stub.asInterface(s1);
+
+ IBinder s2 = ServiceManager.getService(Context.WIFI_PASSPOINT_SERVICE);
mNetworkInfo.setIsAvailable(false);
mLastBssid = null;
@@ -914,9 +1098,9 @@
loge("Couldn't register netlink tracker: " + e.toString());
}
- mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+ mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
mScanIntent = getPrivateBroadcast(ACTION_START_SCAN, SCAN_REQUEST);
- mBatchedScanIntervalIntent = getPrivateBroadcast(ACTION_REFRESH_BATCHED_SCAN, 0);
+ mPnoIntent = getPrivateBroadcast(ACTION_START_PNO, PNO_START_REQUEST);
// Make sure the interval is not configured less than 10 seconds
int period = mContext.getResources().getInteger(
@@ -925,6 +1109,10 @@
period = frameworkMinScanIntervalSaneValue;
}
mDefaultFrameworkScanIntervalMs = period;
+
+ mNoNetworksPeriodicScan = mContext.getResources().getInteger(
+ R.integer.config_wifi_no_network_periodic_scan_interval);
+
mDriverStopDelayMs = mContext.getResources().getInteger(
R.integer.config_wifi_driver_stop_delay);
@@ -935,7 +1123,7 @@
R.string.config_wifi_p2p_device_type);
mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
+ Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
mNetworkCapabilitiesFilter.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
@@ -946,16 +1134,16 @@
mNetworkCapabilities = new NetworkCapabilities(mNetworkCapabilitiesFilter);
mContext.registerReceiver(
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- ArrayList<String> available = intent.getStringArrayListExtra(
- ConnectivityManager.EXTRA_AVAILABLE_TETHER);
- ArrayList<String> active = intent.getStringArrayListExtra(
- ConnectivityManager.EXTRA_ACTIVE_TETHER);
- sendMessage(CMD_TETHER_STATE_CHANGE, new TetherStateChange(available, active));
- }
- },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ ArrayList<String> available = intent.getStringArrayListExtra(
+ ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+ ArrayList<String> active = intent.getStringArrayListExtra(
+ ConnectivityManager.EXTRA_ACTIVE_TETHER);
+ sendMessage(CMD_TETHER_STATE_CHANGE, new TetherStateChange(available, active));
+ }
+ }, new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
mContext.registerReceiver(
new BroadcastReceiver() {
@@ -964,15 +1152,27 @@
sScanAlarmIntentCount++; // Used for debug only
startScan(SCAN_ALARM_SOURCE, mDelayedScanCounter.incrementAndGet(), null, null);
if (VDBG)
- loge("WiFiStateMachine SCAN ALARM -> " + mDelayedScanCounter.get());
+ logd("SCAN ALARM -> " + mDelayedScanCounter.get());
}
},
new IntentFilter(ACTION_START_SCAN));
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ sendMessage(CMD_RESTART_AUTOJOIN_OFFLOAD, 0,
+ mRestartAutoJoinOffloadCounter, "pno alarm");
+ if (DBG)
+ logd("PNO START ALARM sent");
+ }
+ },
+ new IntentFilter(ACTION_START_PNO));
+
+
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
@@ -983,8 +1183,6 @@
sendMessage(CMD_SCREEN_STATE_CHANGED, 1);
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
sendMessage(CMD_SCREEN_STATE_CHANGED, 0);
- } else if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) {
- startNextBatchedScanAsync();
}
}
}, filter);
@@ -993,14 +1191,14 @@
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- int counter = intent.getIntExtra(DELAYED_STOP_COUNTER, 0);
- sendMessage(CMD_DELAYED_STOP_DRIVER, counter, 0);
+ int counter = intent.getIntExtra(DELAYED_STOP_COUNTER, 0);
+ sendMessage(CMD_DELAYED_STOP_DRIVER, counter, 0);
}
},
new IntentFilter(ACTION_DELAYED_DRIVER_STOP));
mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED), false,
+ Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED), false,
new ContentObserver(getHandler()) {
@Override
public void onChange(boolean selfChange) {
@@ -1018,9 +1216,9 @@
},
new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
- mScanResultCache = new LruCache<String, ScanResult>(SCAN_RESULT_CACHE_SIZE);
+ mScanResultCache = new LruCache<>(SCAN_RESULT_CACHE_SIZE);
- PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+ PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getName());
mSuspendWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WifiSuspend");
@@ -1074,7 +1272,8 @@
PendingIntent getPrivateBroadcast(String action, int requestCode) {
Intent intent = new Intent(action, null);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.setPackage(this.getClass().getPackage().getName());
+ //intent.setPackage(this.getClass().getPackage().getName());
+ intent.setPackage("android");
return PendingIntent.getBroadcast(mContext, requestCode, intent, 0);
}
@@ -1099,6 +1298,7 @@
mLogMessages = false;
mWifiNative.setSupplicantLogLevel("INFO");
}
+ mWifiLogger.startLogging(mVerboseLoggingLevel > 0);
mWifiAutoJoinController.enableVerboseLogging(verbose);
mWifiMonitor.enableVerboseLogging(verbose);
mWifiNative.enableVerboseLogging(verbose);
@@ -1106,6 +1306,99 @@
mSupplicantStateTracker.enableVerboseLogging(verbose);
}
+ public void setHalBasedAutojoinOffload(int enabled) {
+ // Shoult be used for debug only, triggered form developper settings
+ // enabling HAl based PNO dynamically is not safe and not a normal operation
+ mHalBasedPnoEnableInDevSettings = enabled > 0;
+ mWifiConfigStore.enableHalBasedPno.set(mHalBasedPnoEnableInDevSettings);
+ mWifiConfigStore.enableSsidWhitelist.set(mHalBasedPnoEnableInDevSettings);
+ sendMessage(CMD_DISCONNECT);
+ }
+
+ int getHalBasedAutojoinOffload() {
+ return mHalBasedPnoEnableInDevSettings ? 1 : 0;
+ }
+
+ boolean useHalBasedAutoJoinOffload() {
+ // all three settings need to be true:
+ // - developper settings switch
+ // - driver support
+ // - config option
+ return mHalBasedPnoEnableInDevSettings
+ && mHalBasedPnoDriverSupported
+ && mWifiConfigStore.enableHalBasedPno.get();
+ }
+
+ boolean allowFullBandScanAndAssociated() {
+
+ if (!getEnableAutoJoinWhenAssociated()) {
+ if (DBG) {
+ Log.e(TAG, "allowFullBandScanAndAssociated: "
+ + " enableAutoJoinWhenAssociated : disallow");
+ }
+ return false;
+ }
+
+ if (mWifiInfo.txSuccessRate >
+ mWifiConfigStore.maxTxPacketForFullScans
+ || mWifiInfo.rxSuccessRate >
+ mWifiConfigStore.maxRxPacketForFullScans) {
+ if (DBG) {
+ Log.e(TAG, "allowFullBandScanAndAssociated: packet rate tx"
+ + mWifiInfo.txSuccessRate + " rx "
+ + mWifiInfo.rxSuccessRate
+ + " allow scan with traffic " + getAllowScansWithTraffic());
+ }
+ // Too much traffic at the interface, hence no full band scan
+ if (getAllowScansWithTraffic() == 0) {
+ return false;
+ }
+ }
+
+ if (getCurrentState() != mConnectedState) {
+ if (DBG) {
+ Log.e(TAG, "allowFullBandScanAndAssociated: getCurrentState() : disallow");
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ long mLastScanPermissionUpdate = 0;
+ boolean mConnectedModeGScanOffloadStarted = false;
+ // Don't do a G-scan enable/re-enable cycle more than once within 20seconds
+ // The function updateAssociatedScanPermission() can be called quite frequently, hence
+ // we want to throttle the GScan Stop->Start transition
+ static final long SCAN_PERMISSION_UPDATE_THROTTLE_MILLI = 20000;
+ void updateAssociatedScanPermission() {
+
+ if (useHalBasedAutoJoinOffload()) {
+ boolean allowed = allowFullBandScanAndAssociated();
+
+ long now = System.currentTimeMillis();
+ if (mConnectedModeGScanOffloadStarted && !allowed) {
+ if (DBG) {
+ Log.e(TAG, " useHalBasedAutoJoinOffload stop offload");
+ }
+ stopPnoOffload();
+ stopGScan(" useHalBasedAutoJoinOffload");
+ }
+ if (!mConnectedModeGScanOffloadStarted && allowed) {
+ if ((now - mLastScanPermissionUpdate) > SCAN_PERMISSION_UPDATE_THROTTLE_MILLI) {
+ // Re-enable Gscan offload, this will trigger periodic scans and allow firmware
+ // to look for 5GHz BSSIDs and better networks
+ if (DBG) {
+ Log.e(TAG, " useHalBasedAutoJoinOffload restart offload");
+ }
+ startGScanConnectedModeOffload("updatePermission "
+ + (now - mLastScanPermissionUpdate) + "ms");
+ mLastScanPermissionUpdate = now;
+ }
+ }
+ }
+ }
+
private int mAggressiveHandover = 0;
int getAggressiveHandover() {
@@ -1116,31 +1409,46 @@
mAggressiveHandover = enabled;
}
+ public void clearANQPCache() {
+ mWifiConfigStore.trimANQPCache(true);
+ }
+
public void setAllowScansWithTraffic(int enabled) {
- mWifiConfigStore.alwaysEnableScansWhileAssociated = enabled;
+ mWifiConfigStore.alwaysEnableScansWhileAssociated.set(enabled);
}
public int getAllowScansWithTraffic() {
- return mWifiConfigStore.alwaysEnableScansWhileAssociated;
+ return mWifiConfigStore.alwaysEnableScansWhileAssociated.get();
}
+ public boolean enableAutoJoinWhenAssociated(boolean enabled) {
+ boolean old_state = getEnableAutoJoinWhenAssociated();
+ mWifiConfigStore.enableAutoJoinWhenAssociated.set(enabled);
+ if (!old_state && enabled && mScreenOn && getCurrentState() == mConnectedState) {
+ startDelayedScan(mWifiConfigStore.wifiAssociatedShortScanIntervalMilli.get(), null,
+ null);
+ }
+ return true;
+ }
+
+ public boolean getEnableAutoJoinWhenAssociated() {
+ return mWifiConfigStore.enableAutoJoinWhenAssociated.get();
+ }
/*
*
* Framework scan control
*/
private boolean mAlarmEnabled = false;
- /* This is set from the overlay config file or from a secure setting.
- * A value of 0 disables scanning in the framework.
- */
- private long mFrameworkScanIntervalMs = 10000;
private AtomicInteger mDelayedScanCounter = new AtomicInteger();
private void setScanAlarm(boolean enabled) {
if (PDBG) {
- loge("setScanAlarm " + enabled
- + " period " + mDefaultFrameworkScanIntervalMs
+ String state;
+ if (enabled) state = "enabled"; else state = "disabled";
+ logd("setScanAlarm " + state
+ + " defaultperiod " + mDefaultFrameworkScanIntervalMs
+ " mBackgroundScanSupported " + mBackgroundScanSupported);
}
if (mBackgroundScanSupported == false) {
@@ -1166,11 +1474,11 @@
private void cancelDelayedScan() {
mDelayedScanCounter.incrementAndGet();
- loge("cancelDelayedScan -> " + mDelayedScanCounter);
}
private boolean checkAndRestartDelayedScan(int counter, boolean restart, int milli,
- ScanSettings settings, WorkSource workSource) {
+ ScanSettings settings, WorkSource workSource) {
+
if (counter != mDelayedScanCounter.get()) {
return false;
}
@@ -1192,21 +1500,21 @@
mDelayedScanCounter.incrementAndGet();
if (mScreenOn &&
(getCurrentState() == mDisconnectedState
- || getCurrentState() == mConnectedState)) {
+ || getCurrentState() == mConnectedState)) {
Bundle bundle = new Bundle();
bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, settings);
bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
bundle.putLong(SCAN_REQUEST_TIME, System.currentTimeMillis());
sendMessageDelayed(CMD_START_SCAN, SCAN_ALARM_SOURCE,
mDelayedScanCounter.get(), bundle, milli);
- if (DBG) loge("startDelayedScan send -> " + mDelayedScanCounter + " milli " + milli);
+ if (DBG) logd("startDelayedScan send -> " + mDelayedScanCounter + " milli " + milli);
} else if (mBackgroundScanSupported == false
&& !mScreenOn && getCurrentState() == mDisconnectedState) {
setScanAlarm(true);
- if (DBG) loge("startDelayedScan start scan alarm -> "
+ if (DBG) logd("startDelayedScan start scan alarm -> "
+ mDelayedScanCounter + " milli " + milli);
} else {
- if (DBG) loge("startDelayedScan unhandled -> "
+ if (DBG) logd("startDelayedScan unhandled -> "
+ mDelayedScanCounter + " milli " + milli);
}
}
@@ -1224,9 +1532,11 @@
return mWifiNative.setScanningMacOui(ouiBytes);
}
- /*********************************************************
+ /**
+ * ******************************************************
* Methods exposed for public use
- ********************************************************/
+ * ******************************************************
+ */
public Messenger getMessenger() {
return new Messenger(getHandler());
@@ -1262,7 +1572,8 @@
try {
c.channelNum = Integer.parseInt(prop[1]);
c.freqMHz = Integer.parseInt(prop[3]);
- } catch (NumberFormatException e) { }
+ } catch (NumberFormatException e) {
+ }
c.isDFS = line.contains("(DFS)");
list.add(c);
} else if (line.contains("Mode[B] Channels:")) {
@@ -1289,7 +1600,7 @@
* @param callingUid The uid initiating the wifi scan. Blame will be given here unless
* workSource is specified.
* @param workSource If not null, blame is given to workSource.
- * @param settings Scan settings, see {@link ScanSettings}.
+ * @param settings Scan settings, see {@link ScanSettings}.
*/
public void startScan(int callingUid, int scanCounter,
ScanSettings settings, WorkSource workSource) {
@@ -1300,322 +1611,36 @@
sendMessage(CMD_START_SCAN, callingUid, scanCounter, bundle);
}
- /**
- * start or stop batched scanning using the given settings
- */
- public void setBatchedScanSettings(BatchedScanSettings settings, int callingUid, int csph,
- WorkSource workSource) {
- Bundle bundle = new Bundle();
- bundle.putParcelable(BATCHED_SETTING, settings);
- bundle.putParcelable(BATCHED_WORKSOURCE, workSource);
- sendMessage(CMD_SET_BATCHED_SCAN, callingUid, csph, bundle);
- }
-
- 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;
- }
- }
-
- public void requestBatchedScanPoll() {
- sendMessage(CMD_POLL_BATCHED_SCAN);
- }
-
- private void startBatchedScan() {
- if (mBatchedScanSettings == null) return;
-
- if (mDhcpActive) {
- if (DBG) log("not starting Batched Scans due to DHCP");
- return;
- }
-
- // first grab any existing data
- retrieveBatchedScanData();
-
- if (PDBG) loge("try starting Batched Scans due to DHCP");
-
-
- mAlarmManager.cancel(mBatchedScanIntervalIntent);
-
- String scansExpected = mWifiNative.setBatchedScanSettings(mBatchedScanSettings);
- try {
- mExpectedBatchedScans = Integer.parseInt(scansExpected);
- setNextBatchedAlarm(mExpectedBatchedScans);
- if (mExpectedBatchedScans > 0) noteBatchedScanStart();
- } catch (NumberFormatException e) {
- stopBatchedScan();
- 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
- retrieveBatchedScanData();
-
- setNextBatchedAlarm(mExpectedBatchedScans);
- }
-
- private void handleBatchedScanPollRequest() {
- if (DBG) {
- log("handleBatchedScanPoll Request - mBatchedScanMinPollTime=" +
- mBatchedScanMinPollTime + " , mBatchedScanSettings=" +
- mBatchedScanSettings);
- }
- // if there is no appropriate PollTime that's because we either aren't
- // batching or we've already set a time for a poll request
- if (mBatchedScanMinPollTime == 0) return;
- if (mBatchedScanSettings == null) return;
-
- long now = System.currentTimeMillis();
-
- if (now > mBatchedScanMinPollTime) {
- // do the poll and reset our timers
- startNextBatchedScan();
- } else {
- mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, mBatchedScanMinPollTime,
- mBatchedScanIntervalIntent);
- mBatchedScanMinPollTime = 0;
- }
- }
-
- // return true if new/different
- private boolean recordBatchedScanSettings(int responsibleUid, int csph, Bundle bundle) {
- BatchedScanSettings settings = bundle.getParcelable(BATCHED_SETTING);
- WorkSource responsibleWorkSource = bundle.getParcelable(BATCHED_WORKSOURCE);
-
- if (DBG) {
- log("set batched scan to " + settings + " for uid=" + responsibleUid +
- ", worksource=" + responsibleWorkSource);
- }
- if (settings != null) {
- if (settings.equals(mBatchedScanSettings)) return false;
- } else {
- if (mBatchedScanSettings == null) return false;
- }
- mBatchedScanSettings = settings;
- if (responsibleWorkSource == null) responsibleWorkSource = new WorkSource(responsibleUid);
- mBatchedScanWorkSource = responsibleWorkSource;
- mBatchedScanCsph = csph;
- return true;
- }
-
- private void stopBatchedScan() {
- mAlarmManager.cancel(mBatchedScanIntervalIntent);
- retrieveBatchedScanData();
- mWifiNative.setBatchedScanSettings(null);
- noteBatchedScanStop();
- }
-
- private void setNextBatchedAlarm(int scansExpected) {
-
- if (mBatchedScanSettings == null || scansExpected < 1) return;
-
- mBatchedScanMinPollTime = System.currentTimeMillis() +
- mBatchedScanSettings.scanIntervalSec * 1000;
-
- 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.setExact(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
- * "----"
+ * "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 final static boolean DEBUG_PARSE = false;
- private void retrieveBatchedScanData() {
- String rawData = mWifiNative.getBatchedScanResults();
- if (DEBUG_PARSE) log("rawData = " + rawData);
- mBatchedScanMinPollTime = 0;
- if (rawData == null || rawData.equalsIgnoreCase("OK")) {
- loge("Unexpected BatchedScanResults :" + rawData);
- return;
- }
-
- int scanCount = 0;
- final String END_OF_BATCHES = "----";
- final String SCANCOUNT = "scancount=";
- final String TRUNCATED = "trunc";
- 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) {
- loge("scancount parseInt Exception from " + splitData[n]);
- }
- } else log("scancount not found");
- if (scanCount == 0) {
- loge("scanCount==0 - aborting");
- return;
- }
-
- 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;
- final long now = SystemClock.elapsedRealtime();
- final int bssidStrLen = BSSID_STR.length();
-
- while (true) {
- while (n < splitData.length) {
- if (DEBUG_PARSE) logd("parsing " + splitData[n]);
- if (splitData[n].equals(END_OF_BATCHES)) {
- if (n+1 != splitData.length) {
- loge("didn't consume " + (splitData.length-n));
- }
- if (mBatchedScanResults.size() > 0) {
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- }
- logd("retrieveBatchedScanResults X");
- return;
- }
- if ((splitData[n].equals(END_STR)) || splitData[n].equals(DELIMITER_STR)) {
- 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_STR)) {
- if (batchedScanResult.scanResults.size() != 0) {
- mBatchedScanResults.add(batchedScanResult);
- batchedScanResult = new BatchedScanResult();
- } else {
- logd("Found empty batch");
- }
- }
- } else if (splitData[n].equals(TRUNCATED)) {
- batchedScanResult.truncated = true;
- } else if (splitData[n].startsWith(BSSID_STR)) {
- bssid = new String(splitData[n].getBytes(), bssidStrLen,
- splitData[n].length() - bssidStrLen);
- } else if (splitData[n].startsWith(FREQ_STR)) {
- try {
- freq = Integer.parseInt(splitData[n].substring(FREQ_STR.length()));
- } catch (NumberFormatException e) {
- loge("Invalid freqency: " + splitData[n]);
- freq = 0;
- }
- } else if (splitData[n].startsWith(AGE)) {
- try {
- tsf = now - Long.parseLong(splitData[n].substring(AGE.length()));
- tsf *= 1000; // convert mS -> uS
- } catch (NumberFormatException e) {
- loge("Invalid timestamp: " + splitData[n]);
- tsf = 0;
- }
- } else if (splitData[n].startsWith(SSID_STR)) {
- wifiSsid = WifiSsid.createFromAsciiEncoded(
- splitData[n].substring(SSID_STR.length()));
- } else if (splitData[n].startsWith(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]);
- level = 0;
- }
- } else if (splitData[n].startsWith(DIST)) {
- try {
- dist = Integer.parseInt(splitData[n].substring(DIST.length()));
- } catch (NumberFormatException e) {
- loge("Invalid distance: " + splitData[n]);
- dist = ScanResult.UNSPECIFIED;
- }
- } else if (splitData[n].startsWith(DISTSD)) {
- try {
- distSd = Integer.parseInt(splitData[n].substring(DISTSD.length()));
- } catch (NumberFormatException e) {
- loge("Invalid distanceSd: " + splitData[n]);
- distSd = ScanResult.UNSPECIFIED;
- }
- } else {
- loge("Unable to parse batched scan result line: " + splitData[n]);
- }
- n++;
- }
- rawData = mWifiNative.getBatchedScanResults();
- if (DEBUG_PARSE) log("reading more data:\n" + rawData);
- if (rawData == null) {
- loge("Unexpected null BatchedScanResults");
- return;
- }
- 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;
- }
- n = 0;
- }
- }
- }
private long mDisconnectedTimeStamp = 0;
@@ -1633,7 +1658,7 @@
private long lastScanDuration = 0;
// Last connect attempt is used to prevent scan requests:
// - for a period of 10 seconds after attempting to connect
- private long lastConnectAttempt = 0;
+ private long lastConnectAttemptTimestamp = 0;
private String lastScanFreqs = null;
// For debugging, keep track of last message status handling
@@ -1655,9 +1680,9 @@
//TODO: second logic should be folded into that
private boolean checkOrDeferScanAllowed(Message msg) {
long now = System.currentTimeMillis();
- if (lastConnectAttempt != 0 && (now - lastConnectAttempt) < 10000) {
+ if (lastConnectAttemptTimestamp != 0 && (now - lastConnectAttemptTimestamp) < 10000) {
Message dmsg = Message.obtain(msg);
- sendMessageDelayed(dmsg, 11000 - (now - lastConnectAttempt));
+ sendMessageDelayed(dmsg, 11000 - (now - lastConnectAttemptTimestamp));
return false;
}
return true;
@@ -1696,12 +1721,12 @@
mTxTimeLastReport = mTxTime;
int rx = mRxTime - mRxTimeLastReport;
mRxTimeLastReport = mRxTime;
- int period = (int)(now - lastOntimeReportTimeStamp);
+ int period = (int) (now - lastOntimeReportTimeStamp);
lastOntimeReportTimeStamp = now;
sb.append(String.format("[on:%d tx:%d rx:%d period:%d]", on, tx, rx, period));
// Report stats since Screen State Changed
on = mOnTime - mOnTimeScreenStateChange;
- period = (int)(now - lastScreenStateChangeTimeStamp);
+ period = (int) (now - lastScreenStateChangeTimeStamp);
sb.append(String.format(" from screen [on:%d period:%d]", on, period));
return sb.toString();
}
@@ -1719,9 +1744,6 @@
mTxTime = stats.tx_time;
mRxTime = stats.rx_time;
mRunningBeaconCount = stats.beacon_rx;
- if (dbg) {
- loge(stats.toString());
- }
}
}
if (stats == null || mWifiLinkLayerStatsSupported <= 0) {
@@ -1731,6 +1753,9 @@
} else {
mWifiInfo.updatePacketRates(stats);
}
+ if (useHalBasedAutoJoinOffload()) {
+ sendMessage(CMD_UPDATE_ASSOCIATED_SCAN_PERMISSION);
+ }
return stats;
}
@@ -1766,10 +1791,10 @@
if (DBG) {
String ts = String.format("[%,d ms]", now);
if (workSource != null) {
- loge(ts + " noteScanStart" + workSource.toString()
+ if (DBG) logd(ts + " noteScanStart" + workSource.toString()
+ " uid " + Integer.toString(callingUid));
} else {
- loge(ts + " noteScanstart no scan source"
+ if (DBG) logd(ts + " noteScanstart no scan source"
+ " uid " + Integer.toString(callingUid));
}
}
@@ -1778,8 +1803,15 @@
&& callingUid != SCAN_ALARM_SOURCE)
|| workSource != null)) {
mScanWorkSource = workSource != null ? workSource : new WorkSource(callingUid);
+
+ WorkSource batteryWorkSource = mScanWorkSource;
+ if (mScanWorkSource.size() == 1 && mScanWorkSource.get(0) < 0) {
+ // WiFi uses negative UIDs to mean special things. BatteryStats don't care!
+ batteryWorkSource = new WorkSource(Process.WIFI_UID);
+ }
+
try {
- mBatteryStats.noteWifiScanStartedFromSource(mScanWorkSource);
+ mBatteryStats.noteWifiScanStartedFromSource(batteryWorkSource);
} catch (RemoteException e) {
log(e.toString());
}
@@ -1795,10 +1827,10 @@
if (DBG) {
String ts = String.format("[%,d ms]", now);
if (mScanWorkSource != null)
- loge(ts + " noteScanEnd " + mScanWorkSource.toString()
+ logd(ts + " noteScanEnd " + mScanWorkSource.toString()
+ " onTime=" + mOnTimeThisScan);
else
- loge(ts + " noteScanEnd no scan source"
+ logd(ts + " noteScanEnd no scan source"
+ " onTime=" + mOnTimeThisScan);
}
if (mScanWorkSource != null) {
@@ -1812,47 +1844,6 @@
}
}
- private void noteBatchedScanStart() {
- if (PDBG) loge("noteBatchedScanstart()");
- // note the end of a previous scan set
- if (mNotedBatchedScanWorkSource != null &&
- (mNotedBatchedScanWorkSource.equals(mBatchedScanWorkSource) == false ||
- mNotedBatchedScanCsph != mBatchedScanCsph)) {
- try {
- mBatteryStats.noteWifiBatchedScanStoppedFromSource(mNotedBatchedScanWorkSource);
- } catch (RemoteException e) {
- log(e.toString());
- } finally {
- mNotedBatchedScanWorkSource = null;
- mNotedBatchedScanCsph = 0;
- }
- }
- // note the start of the new
- try {
- mBatteryStats.noteWifiBatchedScanStartedFromSource(mBatchedScanWorkSource,
- mBatchedScanCsph);
- mNotedBatchedScanWorkSource = mBatchedScanWorkSource;
- mNotedBatchedScanCsph = mBatchedScanCsph;
- } catch (RemoteException e) {
- log(e.toString());
- }
- }
-
- private void noteBatchedScanStop() {
- if (PDBG) loge("noteBatchedScanstop()");
-
- if (mNotedBatchedScanWorkSource != null) {
- try {
- mBatteryStats.noteWifiBatchedScanStoppedFromSource(mNotedBatchedScanWorkSource);
- } catch (RemoteException e) {
- log(e.toString());
- } finally {
- mNotedBatchedScanWorkSource = null;
- mNotedBatchedScanCsph = 0;
- }
- }
- }
-
private void handleScanRequest(int type, Message message) {
ScanSettings settings = null;
WorkSource workSource = null;
@@ -1871,7 +1862,8 @@
StringBuilder sb = new StringBuilder();
boolean first = true;
for (WifiChannel channel : settings.channelSet) {
- if (!first) sb.append(','); else first = false;
+ if (!first) sb.append(',');
+ else first = false;
sb.append(channel.freqMHz);
}
freqs = sb.toString();
@@ -1929,7 +1921,9 @@
}
- /** return true iff scan request is accepted */
+ /**
+ * return true iff scan request is accepted
+ */
private boolean startScanNative(int type, String freqs) {
if (mWifiNative.scan(type, freqs)) {
mIsScanOngoing = true;
@@ -2029,11 +2023,11 @@
/**
* Get status information for the current connection, if any.
- * @return a {@link WifiInfo} object containing information about the current connection
*
+ * @return a {@link WifiInfo} object containing information about the current connection
*/
public WifiInfo syncRequestConnectionInfo() {
- return mWifiInfo;
+ return getWiFiInfoForUid(Binder.getCallingUid());
}
public DhcpResults syncGetDhcpResults() {
@@ -2067,8 +2061,8 @@
public List<ScanResult> syncGetScanResultsList() {
synchronized (mScanResultCache) {
List<ScanResult> scanList = new ArrayList<ScanResult>();
- for(ScanResult result: mScanResults) {
- scanList.add(new ScanResult(result));
+ for (ScanDetail result : mScanResults) {
+ scanList.add(new ScanResult(result.getScanResult()));
}
return scanList;
}
@@ -2084,7 +2078,7 @@
* Get unsynchronized pointer to scan result list
* Can be called only from AutoJoinController which runs in the WifiStateMachine context
*/
- public List<ScanResult> getScanResultsListNoCopyUnsync() {
+ public List<ScanDetail> getScanResultsListNoCopyUnsync() {
return mScanResults;
}
@@ -2135,6 +2129,7 @@
/**
* Get configured networks synchronously
+ *
* @param channel
* @return
*/
@@ -2154,9 +2149,14 @@
return result;
}
+ public WifiConfiguration syncGetMatchingWifiConfig(ScanResult scanResult, AsyncChannel channel) {
+ Message resultMsg = channel.sendMessageSynchronously(CMD_GET_MATCHING_CONFIG, scanResult);
+ return (WifiConfiguration) resultMsg.obj;
+ }
/**
* Get connection statistics synchronously
+ *
* @param channel
* @return
*/
@@ -2204,7 +2204,7 @@
/**
* Enable a network
*
- * @param netId network id of the network
+ * @param netId network id of the network
* @param disableOthers true, if all other networks have to be disabled
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
@@ -2231,6 +2231,7 @@
/**
* Retrieves a WPS-NFC configuration token for the specified network
+ *
* @return a hex string representation of the WPS-NFC configuration token
*/
public String syncGetWpsNfcConfigurationToken(int netId) {
@@ -2241,7 +2242,12 @@
if (enable) {
mWifiConfigStore.enableAllNetworks();
}
- mWifiNative.enableBackgroundScan(enable);
+ boolean ret = mWifiNative.enableBackgroundScan(enable);
+ if (ret) {
+ mLegacyPnoEnabled = enable;
+ } else {
+ Log.e(TAG, " Fail to set up pno, want " + enable + " now " + mLegacyPnoEnabled);
+ }
}
/**
@@ -2256,14 +2262,13 @@
/**
* Clear the blacklist list
- *
*/
public void clearBlacklist() {
sendMessage(CMD_CLEAR_BLACKLIST);
}
public void enableRssiPolling(boolean enabled) {
- sendMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0);
+ sendMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0);
}
public void enableAllNetworks() {
@@ -2304,6 +2309,7 @@
* Set high performance mode of operation.
* Enabling would set active power mode and disable suspend optimizations;
* disabling would set auto power mode and enable suspend optimizations
+ *
* @param enable true if enable, false otherwise
*/
public void setHighPerfModeEnabled(boolean enable) {
@@ -2312,24 +2318,63 @@
/**
* Set the country code
+ *
* @param countryCode following ISO 3166 format
- * @param persist {@code true} if the setting should be remembered.
+ * @param persist {@code true} if the setting should be remembered.
*/
- public void setCountryCode(String countryCode, boolean persist) {
+ public synchronized void setCountryCode(String countryCode, boolean persist) {
// If it's a good country code, apply after the current
// wifi connection is terminated; ignore resetting of code
// for now (it is unclear what the chipset should do when
// country code is reset)
- int countryCodeSequence = mCountryCodeSequence.incrementAndGet();
+
if (TextUtils.isEmpty(countryCode)) {
log("Ignoring resetting of country code");
} else {
- sendMessage(CMD_SET_COUNTRY_CODE, countryCodeSequence, persist ? 1 : 0, countryCode);
+ // if mCountryCodeSequence == 0, it is the first time to set country code, always set
+ // else only when the new country code is different from the current one to set
+ int countryCodeSequence = mCountryCodeSequence.get();
+ if (countryCodeSequence == 0 || countryCode.equals(mSetCountryCode) == false) {
+
+ countryCodeSequence = mCountryCodeSequence.incrementAndGet();
+ mSetCountryCode = countryCode;
+ sendMessage(CMD_SET_COUNTRY_CODE, countryCodeSequence, persist ? 1 : 0,
+ countryCode);
+ }
+
+ if (persist) {
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.WIFI_COUNTRY_CODE,
+ countryCode);
+ }
}
}
/**
+ * Get Network object of current wifi network
+ * @return Network object of current wifi network
+ */
+ public Network getCurrentNetwork() {
+ if (mNetworkAgent != null) {
+ return new Network(mNetworkAgent.netId);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the country code
+ *
+ * @param countryCode following ISO 3166 format
+ */
+ public String getCountryCode() {
+ return mSetCountryCode;
+ }
+
+
+ /**
* Set the operational frequency band
+ *
* @param band
* @param persist {@code true} if the setting should be remembered.
*/
@@ -2372,10 +2417,28 @@
}
/**
+ * Send a message indicating a package has been uninstalled.
+ */
+ public void removeAppConfigs(String packageName, int uid) {
+ // Build partial AppInfo manually - package may not exist in database any more
+ ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = packageName;
+ ai.uid = uid;
+ sendMessage(CMD_REMOVE_APP_CONFIGURATIONS, ai);
+ }
+
+ /**
+ * Send a message indicating a user has been removed.
+ */
+ public void removeUserConfigs(int userId) {
+ sendMessage(CMD_REMOVE_USER_CONFIGURATIONS, userId);
+ }
+
+ /**
* Save configuration on supplicant
*
* @return {@code true} if the operation succeeds, {@code false} otherwise
- *
+ * <p/>
* TODO: deprecate this
*/
public boolean syncSaveConfig(AsyncChannel channel) {
@@ -2435,18 +2498,31 @@
pw.println("mUserWantsSuspendOpt " + mUserWantsSuspendOpt);
pw.println("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
pw.println("Supplicant status " + mWifiNative.status(true));
- pw.println("mEnableBackgroundScan " + mEnableBackgroundScan);
- pw.println("mLastSetCountryCode " + mLastSetCountryCode);
- pw.println("mPersistedCountryCode " + mPersistedCountryCode);
+ pw.println("mLegacyPnoEnabled " + mLegacyPnoEnabled);
+ pw.println("mSetCountryCode " + mSetCountryCode);
+ pw.println("mDriverSetCountryCode " + mDriverSetCountryCode);
+ pw.println("mConnectedModeGScanOffloadStarted " + mConnectedModeGScanOffloadStarted);
+ pw.println("mGScanPeriodMilli " + mGScanPeriodMilli);
+ if (mWhiteListedSsids != null && mWhiteListedSsids.length > 0) {
+ pw.println("SSID whitelist :" );
+ for (int i=0; i < mWhiteListedSsids.length; i++) {
+ pw.println(" " + mWhiteListedSsids[i]);
+ }
+ }
mNetworkFactory.dump(fd, pw, args);
mUntrustedNetworkFactory.dump(fd, pw, args);
pw.println();
mWifiConfigStore.dump(fd, pw, args);
+ pw.println();
+ mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_USER_ACTION);
+ mWifiLogger.dump(fd, pw, args);
}
- /*********************************************************
+ /**
+ * ******************************************************
* Internal private functions
- ********************************************************/
+ * ******************************************************
+ */
private void logStateAndMessage(Message message, String state) {
messageHandlingStatus = 0;
@@ -2454,7 +2530,7 @@
//long now = SystemClock.elapsedRealtimeNanos();
//String ts = String.format("[%,d us]", now/1000);
- loge( " " + state + " " + getLogRecString(message));
+ logd(" " + state + " " + getLogRecString(message));
}
}
@@ -2490,7 +2566,58 @@
if (msg.sendingUid > 0 && msg.sendingUid != Process.WIFI_UID) {
sb.append(" uid=" + msg.sendingUid);
}
+ sb.append(" ").append(printTime());
switch (msg.what) {
+ case CMD_STARTED_GSCAN_DBG:
+ case CMD_STARTED_PNO_DBG:
+ sb.append(" ");
+ sb.append(Integer.toString(msg.arg1));
+ sb.append(" ");
+ sb.append(Integer.toString(msg.arg2));
+ if (msg.obj != null) {
+ sb.append(" " + (String)msg.obj);
+ }
+ break;
+ case CMD_RESTART_AUTOJOIN_OFFLOAD:
+ sb.append(" ");
+ sb.append(Integer.toString(msg.arg1));
+ sb.append(" ");
+ sb.append(Integer.toString(msg.arg2));
+ sb.append("/").append(Integer.toString(mRestartAutoJoinOffloadCounter));
+ if (msg.obj != null) {
+ sb.append(" " + (String)msg.obj);
+ }
+ break;
+ case CMD_UPDATE_ASSOCIATED_SCAN_PERMISSION:
+ sb.append(" ");
+ sb.append(Integer.toString(msg.arg1));
+ sb.append(" ");
+ sb.append(Integer.toString(msg.arg2));
+ sb.append(" halAllowed=").append(useHalBasedAutoJoinOffload());
+ sb.append(" scanAllowed=").append(allowFullBandScanAndAssociated());
+ sb.append(" autojoinAllowed=");
+ sb.append(mWifiConfigStore.enableAutoJoinWhenAssociated.get());
+ sb.append(" withTraffic=").append(getAllowScansWithTraffic());
+ sb.append(" tx=").append(mWifiInfo.txSuccessRate);
+ sb.append("/").append(mWifiConfigStore.maxTxPacketForFullScans);
+ sb.append(" rx=").append(mWifiInfo.rxSuccessRate);
+ sb.append("/").append(mWifiConfigStore.maxRxPacketForFullScans);
+ sb.append(" -> ").append(mConnectedModeGScanOffloadStarted);
+ break;
+ case CMD_PNO_NETWORK_FOUND:
+ sb.append(" ");
+ sb.append(Integer.toString(msg.arg1));
+ sb.append(" ");
+ sb.append(Integer.toString(msg.arg2));
+ if (msg.obj != null) {
+ ScanResult[] results = (ScanResult[])msg.obj;
+ for (int i = 0; i < results.length; i++) {
+ sb.append(" ").append(results[i].SSID).append(" ");
+ sb.append(results[i].frequency);
+ sb.append(" ").append(results[i].level);
+ }
+ }
+ break;
case CMD_START_SCAN:
now = System.currentTimeMillis();
sb.append(" ");
@@ -2500,7 +2627,7 @@
sb.append(" ic=");
sb.append(Integer.toString(sScanAlarmIntentCount));
if (msg.obj != null) {
- Bundle bundle = (Bundle)msg.obj;
+ Bundle bundle = (Bundle) msg.obj;
Long request = bundle.getLong(SCAN_REQUEST_TIME, 0);
if (request != 0) {
sb.append(" proc(ms):").append(now - request);
@@ -2539,7 +2666,6 @@
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
- sb.append(printTime());
StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
if (stateChangeResult != null) {
sb.append(stateChangeResult.toString());
@@ -2601,13 +2727,12 @@
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
- String bssid = (String)msg.obj;
- if (bssid != null && bssid.length()>0) {
+ String bssid = (String) msg.obj;
+ if (bssid != null && bssid.length() > 0) {
sb.append(" ");
sb.append(bssid);
}
sb.append(" blacklist=" + Boolean.toString(didBlackListBSSID));
- sb.append(printTime());
break;
case WifiMonitor.SCAN_RESULTS_EVENT:
sb.append(" ");
@@ -2642,6 +2767,8 @@
sb.append(" last=").append(key);
}
break;
+ case WifiMonitor.SCAN_FAILED_EVENT:
+ break;
case WifiMonitor.NETWORK_CONNECTION_EVENT:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
@@ -2653,7 +2780,6 @@
if (config != null) {
sb.append(" ").append(config.configKey());
}
- sb.append(printTime());
key = mWifiConfigStore.getLastSelectedConfiguration();
if (key != null) {
sb.append(" last=").append(key);
@@ -2666,17 +2792,16 @@
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
if (msg.obj != null) {
- sb.append(" BSSID=").append((String)msg.obj);
+ sb.append(" BSSID=").append((String) msg.obj);
}
if (mTargetRoamBSSID != null) {
sb.append(" Target=").append(mTargetRoamBSSID);
}
sb.append(" roam=").append(Integer.toString(mAutoRoaming));
- sb.append(printTime());
break;
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
if (msg.obj != null) {
- sb.append(" ").append((String)msg.obj);
+ sb.append(" ").append((String) msg.obj);
}
sb.append(" nid=").append(msg.arg1);
sb.append(" reason=").append(msg.arg2);
@@ -2690,13 +2815,12 @@
if (linkDebouncing) {
sb.append(" debounce");
}
- sb.append(printTime());
break;
case WifiMonitor.SSID_TEMP_DISABLED:
case WifiMonitor.SSID_REENABLED:
sb.append(" nid=").append(msg.arg1);
if (msg.obj != null) {
- sb.append(" ").append((String)msg.obj);
+ sb.append(" ").append((String) msg.obj);
}
config = getCurrentWifiConfiguration();
if (config != null) {
@@ -2722,7 +2846,6 @@
sb.append(" bssid=").append(mWifiInfo.getBSSID());
}
}
- sb.append(printTime());
break;
case CMD_RSSI_POLL:
case CMD_UNWANTED_NETWORK:
@@ -2732,8 +2855,8 @@
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
if (mWifiInfo.getSSID() != null)
- if (mWifiInfo.getSSID() != null)
- sb.append(" ").append(mWifiInfo.getSSID());
+ if (mWifiInfo.getSSID() != null)
+ sb.append(" ").append(mWifiInfo.getSSID());
if (mWifiInfo.getBSSID() != null)
sb.append(" ").append(mWifiInfo.getBSSID());
sb.append(" rssi=").append(mWifiInfo.getRssi());
@@ -2752,6 +2875,11 @@
if (wifiScoringReport != null) {
sb.append(wifiScoringReport);
}
+ if (mConnectedModeGScanOffloadStarted) {
+ sb.append(" offload-started periodMilli " + mGScanPeriodMilli);
+ } else {
+ sb.append(" offload-stopped");
+ }
break;
case CMD_AUTO_CONNECT:
case WifiManager.CONNECT_NETWORK:
@@ -2770,7 +2898,6 @@
sb.append(" ").append(mTargetRoamBSSID);
}
sb.append(" roam=").append(Integer.toString(mAutoRoaming));
- sb.append(printTime());
config = getCurrentWifiConfiguration();
if (config != null) {
sb.append(config.configKey());
@@ -2784,7 +2911,7 @@
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
- ScanResult result = (ScanResult)msg.obj;
+ ScanResult result = (ScanResult) msg.obj;
if (result != null) {
now = System.currentTimeMillis();
sb.append(" bssid=").append(result.BSSID);
@@ -2802,7 +2929,6 @@
}
sb.append(" roam=").append(Integer.toString(mAutoRoaming));
sb.append(" fail count=").append(Integer.toString(mRoamFailCount));
- sb.append(printTime());
break;
case CMD_ADD_OR_UPDATE_NETWORK:
sb.append(" ");
@@ -2810,7 +2936,7 @@
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
if (msg.obj != null) {
- config = (WifiConfiguration)msg.obj;
+ config = (WifiConfiguration) msg.obj;
sb.append(" ").append(config.configKey());
sb.append(" prio=").append(config.priority);
sb.append(" status=").append(config.status);
@@ -2897,7 +3023,7 @@
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
if (msg.obj != null) {
- NetworkInfo info = (NetworkInfo)msg.obj;
+ NetworkInfo info = (NetworkInfo) msg.obj;
NetworkInfo.State state = info.getState();
NetworkInfo.DetailedState detailedState = info.getDetailedState();
if (state != null) {
@@ -2924,8 +3050,11 @@
sb.append(" ").append(mWifiInfo.getBSSID());
}
if (c != null) {
- if (c.scanResultCache != null) {
- for (ScanResult r : c.scanResultCache.values()) {
+ ScanDetailCache scanDetailCache =
+ mWifiConfigStore.getScanDetailCache(c);
+ if (scanDetailCache != null) {
+ for (ScanDetail sd : scanDetailCache.values()) {
+ ScanResult r = sd.getScanResult();
if (r.BSSID.equals(mWifiInfo.getBSSID())) {
sb.append(" ipfail=").append(r.numIpConfigFailures);
sb.append(",st=").append(r.autoJoinStatus);
@@ -2938,7 +3067,6 @@
sb.append(",").append(mWifiInfo.txBad);
sb.append(",").append(mWifiInfo.txRetries);
}
- sb.append(printTime());
sb.append(String.format(" bcn=%d", mRunningBeaconCount));
break;
case CMD_UPDATE_LINKPROPERTIES:
@@ -2967,13 +3095,18 @@
}
}
break;
+ case CMD_IP_REACHABILITY_LOST:
+ if (msg.obj != null) {
+ sb.append(" ").append((String) msg.obj);
+ }
+ break;
case CMD_SET_COUNTRY_CODE:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
if (msg.obj != null) {
- sb.append(" ").append((String)msg.obj);
+ sb.append(" ").append((String) msg.obj);
}
break;
case CMD_ROAM_WATCHDOG_TIMER:
@@ -3001,10 +3134,293 @@
return sb.toString();
}
- private void handleScreenStateChanged(boolean screenOn, boolean startBackgroundScanIfNeeded) {
+ private void stopPnoOffload() {
+
+ // clear the PNO list
+ if (!WifiNative.setPnoList(null, WifiStateMachine.this)) {
+ Log.e(TAG, "Failed to stop pno");
+ }
+
+ }
+
+
+ private boolean configureSsidWhiteList() {
+
+ mWhiteListedSsids = mWifiConfigStore.getWhiteListedSsids(getCurrentWifiConfiguration());
+ if (mWhiteListedSsids == null || mWhiteListedSsids.length == 0) {
+ return true;
+ }
+
+ if (!WifiNative.setSsidWhitelist(mWhiteListedSsids)) {
+ loge("configureSsidWhiteList couldnt program SSID list, size "
+ + mWhiteListedSsids.length);
+ return false;
+ }
+
+ logd("configureSsidWhiteList success");
+ return true;
+ }
+
+ // In associated more, lazy roam will be looking for 5GHz roam candidate
+ private boolean configureLazyRoam() {
+ boolean status;
+ if (!useHalBasedAutoJoinOffload()) return false;
+
+ WifiNative.WifiLazyRoamParams params = mWifiNative.new WifiLazyRoamParams();
+ params.A_band_boost_threshold = mWifiConfigStore.bandPreferenceBoostThreshold5.get();
+ params.A_band_penalty_threshold = mWifiConfigStore.bandPreferencePenaltyThreshold5.get();
+ params.A_band_boost_factor = mWifiConfigStore.bandPreferenceBoostFactor5;
+ params.A_band_penalty_factor = mWifiConfigStore.bandPreferencePenaltyFactor5;
+ params.A_band_max_boost = 65;
+ params.lazy_roam_hysteresis = 25;
+ params.alert_roam_rssi_trigger = -75;
+
+ if (DBG) {
+ Log.e(TAG, "configureLazyRoam " + params.toString());
+ }
+
+ if (!WifiNative.setLazyRoam(true, params)) {
+
+ Log.e(TAG, "configureLazyRoam couldnt program params");
+
+ return false;
+ }
+ if (DBG) {
+ Log.e(TAG, "configureLazyRoam success");
+ }
+ return true;
+ }
+
+ // In associated more, lazy roam will be looking for 5GHz roam candidate
+ private boolean stopLazyRoam() {
+ boolean status;
+ if (!useHalBasedAutoJoinOffload()) return false;
+ if (DBG) {
+ Log.e(TAG, "stopLazyRoam");
+ }
+ return WifiNative.setLazyRoam(false, null);
+ }
+
+ private boolean startGScanConnectedModeOffload(String reason) {
+ if (DBG) {
+ if (reason == null) {
+ reason = "";
+ }
+ logd("startGScanConnectedModeOffload " + reason);
+ }
+ stopGScan("startGScanConnectedModeOffload " + reason);
+ if (!mScreenOn) return false;
+
+ if (USE_PAUSE_SCANS) {
+ mWifiNative.pauseScan();
+ }
+ mPnoEnabled = configurePno();
+ if (mPnoEnabled == false) {
+ if (USE_PAUSE_SCANS) {
+ mWifiNative.restartScan();
+ }
+ return false;
+ }
+ mLazyRoamEnabled = configureLazyRoam();
+ if (mLazyRoamEnabled == false) {
+ if (USE_PAUSE_SCANS) {
+ mWifiNative.restartScan();
+ }
+ return false;
+ }
+ if (mWifiConfigStore.getLastSelectedConfiguration() == null) {
+ configureSsidWhiteList();
+ }
+ if (!startConnectedGScan(reason)) {
+ if (USE_PAUSE_SCANS) {
+ mWifiNative.restartScan();
+ }
+ return false;
+ }
+ if (USE_PAUSE_SCANS) {
+ mWifiNative.restartScan();
+ }
+ mConnectedModeGScanOffloadStarted = true;
+ if (DBG) {
+ logd("startGScanConnectedModeOffload success");
+ }
+ return true;
+ }
+
+ private boolean startGScanDisconnectedModeOffload(String reason) {
+ if (DBG) {
+ logd("startGScanDisconnectedModeOffload " + reason);
+ }
+ stopGScan("startGScanDisconnectedModeOffload " + reason);
+ if (USE_PAUSE_SCANS) {
+ mWifiNative.pauseScan();
+ }
+ mPnoEnabled = configurePno();
+ if (mPnoEnabled == false) {
+ if (USE_PAUSE_SCANS) {
+ mWifiNative.restartScan();
+ }
+ return false;
+ }
+ if (!startDisconnectedGScan(reason)) {
+ if (USE_PAUSE_SCANS) {
+ mWifiNative.restartScan();
+ }
+ return false;
+ }
+ if (USE_PAUSE_SCANS) {
+ mWifiNative.restartScan();
+ }
+ return true;
+ }
+
+ private boolean configurePno() {
+ if (!useHalBasedAutoJoinOffload()) return false;
+
+ if (mWifiScanner == null) {
+ log("configurePno: mWifiScanner is null ");
+ return true;
+ }
+
+ List<WifiNative.WifiPnoNetwork> llist
+ = mWifiAutoJoinController.getPnoList(getCurrentWifiConfiguration());
+ if (llist == null || llist.size() == 0) {
+ stopPnoOffload();
+ log("configurePno: empty PNO list ");
+ return true;
+ }
+ if (DBG) {
+ log("configurePno: got llist size " + llist.size());
+ }
+
+ // first program the network we want to look for thru the pno API
+ WifiNative.WifiPnoNetwork list[]
+ = (WifiNative.WifiPnoNetwork[]) llist.toArray(new WifiNative.WifiPnoNetwork[0]);
+
+ if (!WifiNative.setPnoList(list, WifiStateMachine.this)) {
+ Log.e(TAG, "Failed to set pno, length = " + list.length);
+ return false;
+ }
+
+ if (true) {
+ StringBuilder sb = new StringBuilder();
+ for (WifiNative.WifiPnoNetwork network : list) {
+ sb.append("[").append(network.SSID).append(" auth=").append(network.auth);
+ sb.append(" flags=");
+ sb.append(network.flags).append(" rssi").append(network.rssi_threshold);
+ sb.append("] ");
+
+ }
+ sendMessage(CMD_STARTED_PNO_DBG, 1, (int)mGScanPeriodMilli, sb.toString());
+ }
+ return true;
+ }
+
+ final static int DISCONNECTED_SHORT_SCANS_DURATION_MILLI = 2 * 60 * 1000;
+ final static int CONNECTED_SHORT_SCANS_DURATION_MILLI = 2 * 60 * 1000;
+
+ private boolean startConnectedGScan(String reason) {
+ // send a scan background request so as to kick firmware
+ // 5GHz roaming and autojoin
+ // We do this only if screen is on
+ WifiScanner.ScanSettings settings;
+
+ if (mPnoEnabled || mLazyRoamEnabled) {
+ settings = new WifiScanner.ScanSettings();
+ settings.band = WifiScanner.WIFI_BAND_BOTH;
+ long now = System.currentTimeMillis();
+
+ if (!mScreenOn || (mGScanStartTimeMilli!= 0 && now > mGScanStartTimeMilli
+ && ((now - mGScanStartTimeMilli) > CONNECTED_SHORT_SCANS_DURATION_MILLI))) {
+ settings.periodInMs = mWifiConfigStore.wifiAssociatedLongScanIntervalMilli.get();
+ } else {
+ mGScanStartTimeMilli = now;
+ settings.periodInMs = mWifiConfigStore.wifiAssociatedShortScanIntervalMilli.get();
+ // if we start offload with short interval, then reconfigure it after a given
+ // duration of time so as to reduce the scan frequency
+ int delay = 30 * 1000 + CONNECTED_SHORT_SCANS_DURATION_MILLI;
+ sendMessageDelayed(CMD_RESTART_AUTOJOIN_OFFLOAD, delay,
+ mRestartAutoJoinOffloadCounter, " startConnectedGScan " + reason,
+ (long)delay);
+ mRestartAutoJoinOffloadCounter++;
+ }
+ mGScanPeriodMilli = settings.periodInMs;
+ settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL;
+ if (DBG) {
+ log("startConnectedScan: settings band="+ settings.band
+ + " period=" + settings.periodInMs);
+ }
+
+ mWifiScanner.startBackgroundScan(settings, mWifiScanListener);
+ if (true) {
+ sendMessage(CMD_STARTED_GSCAN_DBG, 1, (int)mGScanPeriodMilli, reason);
+ }
+ }
+ return true;
+ }
+
+ private boolean startDisconnectedGScan(String reason) {
+ // send a scan background request so as to kick firmware
+ // PNO
+ // This is done in both screen On and screen Off modes
+ WifiScanner.ScanSettings settings;
+
+ if (mWifiScanner == null) {
+ log("startDisconnectedGScan: no wifi scanner");
+ return false;
+ }
+
+ if (mPnoEnabled || mLazyRoamEnabled) {
+ settings = new WifiScanner.ScanSettings();
+ settings.band = WifiScanner.WIFI_BAND_BOTH;
+ long now = System.currentTimeMillis();
+
+
+ if (!mScreenOn || (mGScanStartTimeMilli != 0 && now > mGScanStartTimeMilli
+ && ((now - mGScanStartTimeMilli) > DISCONNECTED_SHORT_SCANS_DURATION_MILLI))) {
+ settings.periodInMs = mWifiConfigStore.wifiDisconnectedLongScanIntervalMilli.get();
+ } else {
+ settings.periodInMs = mWifiConfigStore.wifiDisconnectedShortScanIntervalMilli.get();
+ mGScanStartTimeMilli = now;
+ // if we start offload with short interval, then reconfigure it after a given
+ // duration of time so as to reduce the scan frequency
+ int delay = 30 * 1000 + DISCONNECTED_SHORT_SCANS_DURATION_MILLI;
+ sendMessageDelayed(CMD_RESTART_AUTOJOIN_OFFLOAD, delay,
+ mRestartAutoJoinOffloadCounter, " startDisconnectedGScan " + reason,
+ (long)delay);
+ mRestartAutoJoinOffloadCounter++;
+ }
+ mGScanPeriodMilli = settings.periodInMs;
+ settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL;
+ if (DBG) {
+ log("startDisconnectedScan: settings band="+ settings.band
+ + " period=" + settings.periodInMs);
+ }
+ mWifiScanner.startBackgroundScan(settings, mWifiScanListener);
+ if (true) {
+ sendMessage(CMD_STARTED_GSCAN_DBG, 1, (int)mGScanPeriodMilli, reason);
+ }
+ }
+ return true;
+ }
+
+ private boolean stopGScan(String reason) {
+ mGScanStartTimeMilli = 0;
+ mGScanPeriodMilli = 0;
+ if (mWifiScanner != null) {
+ mWifiScanner.stopBackgroundScan(mWifiScanListener);
+ }
+ mConnectedModeGScanOffloadStarted = false;
+ if (true) {
+ sendMessage(CMD_STARTED_GSCAN_DBG, 0, 0, reason);
+ }
+ return true;
+ }
+
+ private void handleScreenStateChanged(boolean screenOn) {
mScreenOn = screenOn;
if (PDBG) {
- loge(" handleScreenStateChanged Enter: screenOn=" + screenOn
+ logd(" handleScreenStateChanged Enter: screenOn=" + screenOn
+ " mUserWantsSuspendOpt=" + mUserWantsSuspendOpt
+ " state " + getCurrentState().getName()
+ " suppState:" + mSupplicantStateTracker.getSupplicantStateName());
@@ -3025,41 +3441,61 @@
getWifiLinkLayerStats(false);
mOnTimeScreenStateChange = mOnTime;
lastScreenStateChangeTimeStamp = lastLinkLayerStatsUpdate;
- mEnableBackgroundScan = false;
+
cancelDelayedScan();
if (screenOn) {
+ enableBackgroundScan(false);
setScanAlarm(false);
clearBlacklist();
- fullBandConnectedTimeIntervalMilli = mWifiConfigStore.associatedPartialScanPeriodMilli;
+ fullBandConnectedTimeIntervalMilli
+ = mWifiConfigStore.wifiAssociatedShortScanIntervalMilli.get();
// In either Disconnectedstate or ConnectedState,
// start the scan alarm so as to enable autojoin
if (getCurrentState() == mConnectedState
- && mWifiConfigStore.enableAutoJoinScanWhenAssociated) {
- // Scan after 500ms
- startDelayedScan(500, null, null);
+ && allowFullBandScanAndAssociated()) {
+ if (useHalBasedAutoJoinOffload()) {
+ startGScanConnectedModeOffload("screenOnConnected");
+ } else {
+ // Scan after 500ms
+ startDelayedScan(500, null, null);
+ }
} else if (getCurrentState() == mDisconnectedState) {
- // Scan after 200ms
- startDelayedScan(200, null, null);
+ if (useHalBasedAutoJoinOffload()) {
+ startGScanDisconnectedModeOffload("screenOnDisconnected");
+ } else {
+ // Scan after 500ms
+ startDelayedScan(500, null, null);
+ }
}
- } else if (startBackgroundScanIfNeeded) {
- // Screen Off and Disconnected and chipset doesn't support scan offload
- // => start scan alarm
- // Screen Off and Disconnected and chipset does support scan offload
- // => will use scan offload (i.e. background scan)
- if (!mBackgroundScanSupported) {
- setScanAlarm(true);
+ } else {
+ if (getCurrentState() == mDisconnectedState) {
+ // Screen Off and Disconnected and chipset doesn't support scan offload
+ // => start scan alarm
+ // Screen Off and Disconnected and chipset does support scan offload
+ // => will use scan offload (i.e. background scan)
+ if (useHalBasedAutoJoinOffload()) {
+ startGScanDisconnectedModeOffload("screenOffDisconnected");
+ } else {
+ if (!mBackgroundScanSupported) {
+ setScanAlarm(true);
+ } else {
+ if (!mIsScanOngoing) {
+ enableBackgroundScan(true);
+ }
+ }
+ }
} else {
- mEnableBackgroundScan = true;
+ enableBackgroundScan(false);
+ if (useHalBasedAutoJoinOffload()) {
+ // don't try stop Gscan if it is not enabled
+ stopGScan("ScreenOffStop(enableBackground=" + mLegacyPnoEnabled + ") ");
+ }
}
}
- if (DBG) logd("backgroundScan enabled=" + mEnableBackgroundScan
- + " startBackgroundScanIfNeeded:" + startBackgroundScanIfNeeded);
- if (startBackgroundScanIfNeeded) {
- // to scan for them in background, we need all networks enabled
- enableBackgroundScan(mEnableBackgroundScan);
- }
+ if (DBG) logd("backgroundScan enabled=" + mLegacyPnoEnabled);
+
if (DBG) log("handleScreenStateChanged Exit: " + screenOn);
}
@@ -3097,7 +3533,7 @@
return false;
}
- if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ if (mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
loge("Error tethering on " + intf);
return false;
}
@@ -3168,17 +3604,27 @@
private void setFrequencyBand() {
int band = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.WIFI_FREQUENCY_BAND, WifiManager.WIFI_FREQUENCY_BAND_AUTO);
- setFrequencyBand(band, false);
+
+ if (mWifiNative.setBand(band)) {
+ mFrequencyBand.set(band);
+ if (PDBG) {
+ logd("done set frequency band " + band);
+ }
+ } else {
+ loge("Failed to set frequency band " + band);
+ }
}
+
+
private void setSuspendOptimizationsNative(int reason, boolean enabled) {
if (DBG) {
log("setSuspendOptimizationsNative: " + reason + " " + enabled
+ " -want " + mUserWantsSuspendOpt.get()
+ " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
- +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName()
- +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName()
- +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName());
+ + " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
+ + " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
+ + " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
}
//mWifiNative.setSuspendOptimizations(enabled);
@@ -3189,9 +3635,9 @@
if (DBG) {
log("setSuspendOptimizationsNative do it " + reason + " " + enabled
+ " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
- +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName()
- +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName()
- +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName());
+ + " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
+ + " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
+ + " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
}
mWifiNative.setSuspendOptimizations(true);
}
@@ -3235,7 +3681,7 @@
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
- private void setWifiApState(int wifiApState) {
+ private void setWifiApState(int wifiApState, int reason) {
final int previousWifiApState = mWifiApState.get();
try {
@@ -3257,6 +3703,11 @@
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
+ if (wifiApState == WifiManager.WIFI_AP_STATE_FAILED) {
+ //only set reason number when softAP start failed
+ intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, reason);
+ }
+
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
@@ -3275,6 +3726,7 @@
}
}*/
+ private static final String IE_STR = "ie=";
private static final String ID_STR = "id=";
private static final String BSSID_STR = "bssid=";
private static final String FREQ_STR = "freq=";
@@ -3292,7 +3744,7 @@
/**
* Format:
- *
+ * <p/>
* id=1
* bssid=68:7f:76:d7:1a:6e
* freq=2412
@@ -3331,7 +3783,7 @@
scanResultsBuf.append("\n");
String[] lines = tmpResults.split("\n");
sid = -1;
- for (int i=lines.length - 1; i >= 0; i--) {
+ for (int i = lines.length - 1; i >= 0; i--) {
if (lines[i].startsWith(END_STR)) {
break;
} else if (lines[i].startsWith(ID_STR)) {
@@ -3356,21 +3808,25 @@
if (emptyScanResultCount > 10) {
// If we got too many empty scan results, the current scan cache is stale,
// hence clear it.
- mScanResults = new ArrayList<ScanResult>();
+ mScanResults = new ArrayList<>();
}
- return;
+ return;
}
emptyScanResultCount = 0;
+ mWifiConfigStore.trimANQPCache(false);
+
// note that all these splits and substrings keep references to the original
// huge string buffer while the amount we really want is generally pretty small
// so make copies instead (one example b/11087956 wasted 400k of heap here).
- synchronized(mScanResultCache) {
- mScanResults = new ArrayList<ScanResult>();
+ synchronized (mScanResultCache) {
+ mScanResults = new ArrayList<>();
String[] lines = scanResults.split("\n");
final int bssidStrLen = BSSID_STR.length();
final int flagLen = FLAGS_STR.length();
+ String infoElements = null;
+ List<String> anqpLines = null;
for (String line : lines) {
if (line.startsWith(BSSID_STR)) {
@@ -3388,7 +3844,7 @@
* so we need to adjust for that here.
*/
if (level > 0) level -= 256;
- } catch(NumberFormatException e) {
+ } catch (NumberFormatException e) {
level = 0;
}
} else if (line.startsWith(TSF_STR)) {
@@ -3402,40 +3858,45 @@
} else if (line.startsWith(SSID_STR)) {
wifiSsid = WifiSsid.createFromAsciiEncoded(
line.substring(SSID_STR.length()));
- } else if (line.startsWith(DELIMITER_STR) || line.startsWith(END_STR)) {
- Matcher match = null;
- if (bssid!= null) {
- match = mNotZero.matcher(bssid);
+ } else if (line.startsWith(IE_STR)) {
+ infoElements = line;
+ } else if (SupplicantBridge.isAnqpAttribute(line)) {
+ if (anqpLines == null) {
+ anqpLines = new ArrayList<>();
}
- if (match != null && !bssid.isEmpty() && match.find()) {
- String ssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
- String key = bssid + ssid;
- ScanResult scanResult = mScanResultCache.get(key);
- if (scanResult != null) {
- // TODO: average the RSSI, instead of overwriting it
- scanResult.level = level;
- scanResult.wifiSsid = wifiSsid;
- // Keep existing API
- scanResult.SSID = (wifiSsid != null) ? wifiSsid.toString() :
- WifiSsid.NONE;
- scanResult.capabilities = flags;
- scanResult.frequency = freq;
- scanResult.timestamp = tsf;
- scanResult.seen = System.currentTimeMillis();
- } else {
- scanResult =
- new ScanResult(
- wifiSsid, bssid, flags, level, freq, tsf);
- scanResult.seen = System.currentTimeMillis();
- mScanResultCache.put(key, scanResult);
- }
- mNumScanResultsReturned ++; // Keep track of how many scan results we got
- // as part of this scan's processing
- mScanResults.add(scanResult);
- } else {
- if (bssid != null) {
- loge("setScanResults obtaining null BSSID results <"
- + bssid + ">, discard it");
+ anqpLines.add(line);
+ } else if (line.startsWith(DELIMITER_STR) || line.startsWith(END_STR)) {
+ if (bssid != null) {
+ try {
+ NetworkDetail networkDetail =
+ new NetworkDetail(bssid, infoElements, anqpLines, freq);
+
+ String xssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
+ if (!xssid.equals(networkDetail.getTrimmedSSID())) {
+ logd(String.format(
+ "Inconsistent SSID on BSSID '%s': '%s' vs '%s': %s",
+ bssid, xssid, networkDetail.getSSID(), infoElements));
+ }
+
+ if (networkDetail.hasInterworking()) {
+ Log.d(Utils.hs2LogTag(getClass()), "HSNwk: '" + networkDetail);
+ }
+
+ ScanDetail scanDetail = mScanResultCache.get(networkDetail);
+ if (scanDetail != null) {
+ scanDetail.updateResults(networkDetail, level, wifiSsid, xssid,
+ flags, freq, tsf);
+ } else {
+ scanDetail = new ScanDetail(networkDetail, wifiSsid, bssid,
+ flags, level, freq, tsf);
+ mScanResultCache.put(networkDetail, scanDetail);
+ }
+
+ mNumScanResultsReturned++; // Keep track of how many scan results we got
+ // as part of this scan's processing
+ mScanResults.add(scanDetail);
+ } catch (IllegalArgumentException iae) {
+ Log.d(TAG, "Failed to parse information elements: " + iae);
}
}
bssid = null;
@@ -3444,10 +3905,15 @@
tsf = 0;
flags = "";
wifiSsid = null;
+ infoElements = null;
+ anqpLines = null;
}
}
}
- boolean attemptAutoJoin = true;
+
+ /* don't attempt autojoin if last connect attempt was just scheduled */
+ boolean attemptAutoJoin =
+ (System.currentTimeMillis() - lastConnectAttemptTimestamp) > CONNECT_TIMEOUT_MSEC;
SupplicantState state = mWifiInfo.getSupplicantState();
String selection = mWifiConfigStore.getLastSelectedConfiguration();
if (getCurrentState() == mRoamingState
@@ -3455,7 +3921,7 @@
|| getCurrentState() == mScanModeState
|| getCurrentState() == mDisconnectingState
|| (getCurrentState() == mConnectedState
- && !mWifiConfigStore.enableAutoJoinWhenAssociated)
+ && !getEnableAutoJoinWhenAssociated())
|| linkDebouncing
|| state == SupplicantState.ASSOCIATING
|| state == SupplicantState.AUTHENTICATING
@@ -3463,7 +3929,7 @@
|| state == SupplicantState.GROUP_HANDSHAKE
|| (/* keep autojoin enabled if user has manually selected a wifi network,
so as to make sure we reliably remain connected to this network */
- mConnectionRequests == 0 && selection == null)) {
+ mConnectionRequests == 0 && selection == null)) {
// Dont attempt auto-joining again while we are already attempting to join
// and/or obtaining Ip address
attemptAutoJoin = false;
@@ -3472,11 +3938,13 @@
if (selection == null) {
selection = "<none>";
}
- loge("wifi setScanResults state" + getCurrentState()
+ logd("wifi setScanResults state" + getCurrentState()
+ " sup_state=" + state
+ " debouncing=" + linkDebouncing
+ " mConnectionRequests=" + mConnectionRequests
- + " selection=" + selection);
+ + " selection=" + selection
+ + " mNumScanResultsReturned " + mNumScanResultsReturned
+ + " mScanResults " + mScanResults.size());
}
if (attemptAutoJoin) {
messageHandlingStatus = MESSAGE_HANDLING_STATUS_PROCESSED;
@@ -3486,8 +3954,8 @@
mWifiConfigStore.setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
}
- if (mWifiConfigStore.enableAutoJoinWhenAssociated) {
- synchronized(mScanResultCache) {
+ if (attemptAutoJoin) {
+ synchronized (mScanResultCache) {
// AutoJoincontroller will directly acces the scan result list and update it with
// ScanResult status
mNumScanResultsKnown = mWifiAutoJoinController.newSupplicantResults(attemptAutoJoin);
@@ -3532,13 +4000,13 @@
}
if (PDBG) {
- loge("fetchRssiLinkSpeedAndFrequencyNative rssi="
+ logd("fetchRssiLinkSpeedAndFrequencyNative rssi="
+ Integer.toString(newRssi) + " linkspeed="
+ Integer.toString(newLinkSpeed));
}
if (newRssi > WifiInfo.INVALID_RSSI && newRssi < WifiInfo.MAX_RSSI) {
- // screen out invalid values
+ // screen out invalid values
/* some implementations avoid negative values by adding 256
* so we need to adjust for that here.
*/
@@ -3579,7 +4047,7 @@
}
/**
- * Determine if we need to switch network:
+ * Determine if we need to switch network:
* - the delta determine the urgency to switch and/or or the expected evilness of the disruption
* - match the uregncy of the switch versus the packet usage at the interface
*/
@@ -3590,7 +4058,7 @@
}
delta = networkDelta;
if (mWifiInfo != null) {
- if (!mWifiConfigStore.enableAutoJoinWhenAssociated
+ if (!getEnableAutoJoinWhenAssociated()
&& mWifiInfo.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
// If AutoJoin while associated is not enabled,
// we should never switch network when already associated
@@ -3605,13 +4073,13 @@
} else if ((mWifiInfo.txSuccessRate > 5) || (mWifiInfo.rxSuccessRate > 30)) {
delta -= 6;
}
- loge("WifiStateMachine shouldSwitchNetwork "
+ logd("shouldSwitchNetwork "
+ " txSuccessRate=" + String.format("%.2f", mWifiInfo.txSuccessRate)
+ " rxSuccessRate=" + String.format("%.2f", mWifiInfo.rxSuccessRate)
+ " delta " + networkDelta + " -> " + delta);
}
} else {
- loge("WifiStateMachine shouldSwitchNetwork "
+ logd("shouldSwitchNetwork "
+ " delta " + networkDelta + " -> " + delta);
}
if (delta > 0) {
@@ -3632,6 +4100,7 @@
// For debug, provide information about the last scoring operation
String wifiScoringReport = null;
+
private void calculateWifiScore(WifiLinkLayerStats stats) {
StringBuilder sb = new StringBuilder();
@@ -3667,17 +4136,18 @@
boolean use24Thresholds = false;
boolean homeNetworkBoost = false;
WifiConfiguration currentConfiguration = getCurrentWifiConfiguration();
- if (currentConfiguration != null
- && currentConfiguration.scanResultCache != null) {
- currentConfiguration.setVisibility(12000);
+ ScanDetailCache scanDetailCache =
+ mWifiConfigStore.getScanDetailCache(currentConfiguration);
+ if (currentConfiguration != null && scanDetailCache != null) {
+ currentConfiguration.setVisibility(scanDetailCache.getVisibility(12000));
if (currentConfiguration.visibility != null) {
if (currentConfiguration.visibility.rssi24 != WifiConfiguration.INVALID_RSSI
&& currentConfiguration.visibility.rssi24
- >= (currentConfiguration.visibility.rssi5-2)) {
+ >= (currentConfiguration.visibility.rssi5 - 2)) {
use24Thresholds = true;
}
}
- if (currentConfiguration.scanResultCache.size() <= 6
+ if (scanDetailCache.size() <= 6
&& currentConfiguration.allowedKeyManagement.cardinality() == 1
&& currentConfiguration.allowedKeyManagement.
get(WifiConfiguration.KeyMgmt.WPA_PSK) == true) {
@@ -3695,19 +4165,19 @@
boolean is24GHz = use24Thresholds || mWifiInfo.is24GHz();
- boolean isBadRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdBadRssi24)
- || (!is24GHz && rssi < mWifiConfigStore.thresholdBadRssi5);
- boolean isLowRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdLowRssi24)
- || (!is24GHz && mWifiInfo.getRssi() < mWifiConfigStore.thresholdLowRssi5);
- boolean isHighRSSI = (is24GHz && rssi >= mWifiConfigStore.thresholdGoodRssi24)
- || (!is24GHz && mWifiInfo.getRssi() >= mWifiConfigStore.thresholdGoodRssi5);
+ boolean isBadRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdBadRssi24.get())
+ || (!is24GHz && rssi < mWifiConfigStore.thresholdBadRssi5.get());
+ boolean isLowRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdLowRssi24.get())
+ || (!is24GHz && mWifiInfo.getRssi() < mWifiConfigStore.thresholdLowRssi5.get());
+ boolean isHighRSSI = (is24GHz && rssi >= mWifiConfigStore.thresholdGoodRssi24.get())
+ || (!is24GHz && mWifiInfo.getRssi() >= mWifiConfigStore.thresholdGoodRssi5.get());
if (isBadRSSI) sb.append(" br");
if (isLowRSSI) sb.append(" lr");
if (isHighRSSI) sb.append(" hr");
int penalizedDueToUserTriggeredDisconnect = 0; // For debug information
- if (currentConfiguration!= null &&
+ if (currentConfiguration != null &&
(mWifiInfo.txSuccessRate > 5 || mWifiInfo.rxSuccessRate > 5)) {
if (isBadRSSI) {
currentConfiguration.numTicksAtBadRSSI++;
@@ -3727,9 +4197,9 @@
}
if (mWifiConfigStore.enableWifiCellularHandoverUserTriggeredAdjustment &&
(currentConfiguration.numUserTriggeredWifiDisableBadRSSI > 0
- || currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0
- || currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0)) {
- score = score -5;
+ || currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0
+ || currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0)) {
+ score = score - 5;
penalizedDueToUserTriggeredDisconnect = 1;
sb.append(" p1");
}
@@ -3748,8 +4218,8 @@
}
if (mWifiConfigStore.enableWifiCellularHandoverUserTriggeredAdjustment &&
(currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0
- || currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0)) {
- score = score -5;
+ || currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0)) {
+ score = score - 5;
penalizedDueToUserTriggeredDisconnect = 2;
sb.append(" p2");
}
@@ -3765,7 +4235,7 @@
}
if (mWifiConfigStore.enableWifiCellularHandoverUserTriggeredAdjustment &&
currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
- score = score -5;
+ score = score - 5;
penalizedDueToUserTriggeredDisconnect = 3;
sb.append(" p3");
}
@@ -3781,15 +4251,15 @@
else if (isHighRSSI) rssiStatus += " highRSSI ";
else if (isLowRSSI) rssiStatus += " lowRSSI ";
if (isBadLinkspeed) rssiStatus += " lowSpeed ";
- loge("calculateWifiScore freq=" + Integer.toString(mWifiInfo.getFrequency())
- + " speed=" + Integer.toString(mWifiInfo.getLinkSpeed())
- + " score=" + Integer.toString(mWifiInfo.score)
- + rssiStatus
- + " -> txbadrate=" + String.format( "%.2f", mWifiInfo.txBadRate )
- + " txgoodrate=" + String.format("%.2f", mWifiInfo.txSuccessRate)
- + " txretriesrate=" + String.format("%.2f", mWifiInfo.txRetriesRate)
- + " rxrate=" + String.format("%.2f", mWifiInfo.rxSuccessRate)
- + " userTriggerdPenalty" + penalizedDueToUserTriggeredDisconnect);
+ logd("calculateWifiScore freq=" + Integer.toString(mWifiInfo.getFrequency())
+ + " speed=" + Integer.toString(mWifiInfo.getLinkSpeed())
+ + " score=" + Integer.toString(mWifiInfo.score)
+ + rssiStatus
+ + " -> txbadrate=" + String.format("%.2f", mWifiInfo.txBadRate)
+ + " txgoodrate=" + String.format("%.2f", mWifiInfo.txSuccessRate)
+ + " txretriesrate=" + String.format("%.2f", mWifiInfo.txRetriesRate)
+ + " rxrate=" + String.format("%.2f", mWifiInfo.rxSuccessRate)
+ + " userTriggerdPenalty" + penalizedDueToUserTriggeredDisconnect);
}
if ((mWifiInfo.txBadRate >= 1) && (mWifiInfo.txSuccessRate < 3)
@@ -3798,13 +4268,13 @@
if (mWifiInfo.linkStuckCount < 5)
mWifiInfo.linkStuckCount += 1;
sb.append(String.format(" ls+=%d", mWifiInfo.linkStuckCount));
- if (PDBG) loge(" bad link -> stuck count ="
+ if (PDBG) logd(" bad link -> stuck count ="
+ Integer.toString(mWifiInfo.linkStuckCount));
- } else if (mWifiInfo.txSuccessRate > 2 || mWifiInfo.txBadRate < 0.1) {
+ } else if (mWifiInfo.txBadRate < 0.3) {
if (mWifiInfo.linkStuckCount > 0)
mWifiInfo.linkStuckCount -= 1;
sb.append(String.format(" ls-=%d", mWifiInfo.linkStuckCount));
- if (PDBG) loge(" good link -> stuck count ="
+ if (PDBG) logd(" good link -> stuck count ="
+ Integer.toString(mWifiInfo.linkStuckCount));
}
@@ -3817,9 +4287,9 @@
sb.append(String.format(",%d", score));
if (isBadLinkspeed) {
- score -= 4 ;
+ score -= 4;
if (PDBG) {
- loge(" isBadLinkspeed ---> count=" + mBadLinkspeedcount
+ logd(" isBadLinkspeed ---> count=" + mBadLinkspeedcount
+ " score=" + Integer.toString(score));
}
} else if ((isGoodLinkspeed) && (mWifiInfo.txSuccessRate > 5)) {
@@ -3841,17 +4311,17 @@
mWifiInfo.lowRssiCount = 0;
}
- score -= mWifiInfo.badRssiCount * 2 + mWifiInfo.lowRssiCount ;
+ score -= mWifiInfo.badRssiCount * 2 + mWifiInfo.lowRssiCount;
sb.append(String.format(",%d", score));
- if (PDBG) loge(" badRSSI count" + Integer.toString(mWifiInfo.badRssiCount)
- + " lowRSSI count" + Integer.toString(mWifiInfo.lowRssiCount)
- + " --> score " + Integer.toString(score));
+ if (PDBG) logd(" badRSSI count" + Integer.toString(mWifiInfo.badRssiCount)
+ + " lowRSSI count" + Integer.toString(mWifiInfo.lowRssiCount)
+ + " --> score " + Integer.toString(score));
if (isHighRSSI) {
score += 5;
- if (PDBG) loge(" isHighRSSI ---> score=" + Integer.toString(score));
+ if (PDBG) logd(" isHighRSSI ---> score=" + Integer.toString(score));
}
sb.append(String.format(",%d]", score));
@@ -3866,7 +4336,7 @@
//report score
if (score != mWifiInfo.score) {
if (DBG) {
- loge("calculateWifiScore() report new score " + Integer.toString(score));
+ logd("calculateWifiScore() report new score " + Integer.toString(score));
}
mWifiInfo.score = score;
if (mNetworkAgent != null) {
@@ -3927,32 +4397,36 @@
private boolean isProvisioned(LinkProperties lp) {
return lp.isProvisioned() ||
- (mWifiConfigStore.isUsingStaticIp(mLastNetworkId) && lp.hasIPv4Address());
+ (mWifiConfigStore.isUsingStaticIp(mLastNetworkId) && lp.hasIPv4Address());
}
/**
- * Updates mLinkProperties by merging information from various sources.
- *
+ * Creates a new LinkProperties object by merging information from various sources.
+ * <p/>
* This is needed because the information in mLinkProperties comes from multiple sources (DHCP,
* netlink, static configuration, ...). When one of these sources of information has updated
* link properties, we can't just assign them to mLinkProperties or we'd lose track of the
* information that came from other sources. Instead, when one of those sources has new
* information, we update the object that tracks the information from that source and then
- * call this method to apply the change to mLinkProperties.
- *
- * The information in mLinkProperties is currently obtained as follows:
- * - Interface name: set in the constructor.
- * - IPv4 and IPv6 addresses: netlink, passed in by mNetlinkTracker.
- * - IPv4 routes, DNS servers, and domains: DHCP.
- * - IPv6 routes and DNS servers: netlink, passed in by mNetlinkTracker.
- * - HTTP proxy: the wifi config store.
+ * call this method to integrate the change into a new LinkProperties object for subsequent
+ * comparison with mLinkProperties.
+ * <p/>
+ * The information used to build LinkProperties is currently obtained as follows:
+ * - Interface name: set in the constructor.
+ * - IPv4 and IPv6 addresses: netlink, passed in by mNetlinkTracker.
+ * - IPv4 routes, DNS servers, and domains: DHCP.
+ * - IPv6 routes and DNS servers: netlink, passed in by mNetlinkTracker.
+ * - HTTP proxy: the wifi config store.
*/
- private void updateLinkProperties(int reason) {
+ private LinkProperties makeLinkProperties() {
LinkProperties newLp = new LinkProperties();
- // Interface name and proxy are locally configured.
+ // Interface name, proxy, and TCP buffer sizes are locally configured.
newLp.setInterfaceName(mInterfaceName);
newLp.setHttpProxy(mWifiConfigStore.getProxyProperties(mLastNetworkId));
+ if (!TextUtils.isEmpty(mTcpBufferSizes)) {
+ newLp.setTcpBufferSizes(mTcpBufferSizes);
+ }
// IPv4/v6 addresses, IPv6 routes and IPv6 DNS servers come from netlink.
LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties();
@@ -3961,7 +4435,11 @@
newLp.addRoute(route);
}
for (InetAddress dns : netlinkLinkProperties.getDnsServers()) {
- newLp.addDnsServer(dns);
+ // Only add likely reachable DNS servers.
+ // TODO: investigate deleting this.
+ if (newLp.isReachable(dns)) {
+ newLp.addDnsServer(dns);
+ }
}
// IPv4 routes, DNS servers and domains come from mDhcpResults.
@@ -3973,17 +4451,32 @@
newLp.addRoute(route);
}
for (InetAddress dns : mDhcpResults.dnsServers) {
- newLp.addDnsServer(dns);
+ // Only add likely reachable DNS servers.
+ // TODO: investigate deleting this.
+ if (newLp.isReachable(dns)) {
+ newLp.addDnsServer(dns);
+ }
}
newLp.setDomains(mDhcpResults.domains);
}
}
+ return newLp;
+ }
+
+ private void updateLinkProperties(int reason) {
+ LinkProperties newLp = makeLinkProperties();
+
final boolean linkChanged = !newLp.equals(mLinkProperties);
final boolean wasProvisioned = isProvisioned(mLinkProperties);
final boolean isProvisioned = isProvisioned(newLp);
- final boolean lostIPv4Provisioning =
- mLinkProperties.hasIPv4Address() && !newLp.hasIPv4Address();
+ // TODO: Teach LinkProperties how to understand static assignment
+ // and simplify all this provisioning change detection logic by
+ // unifying it under LinkProperties.compareProvisioning().
+ final boolean lostProvisioning =
+ (wasProvisioned && !isProvisioned) ||
+ (mLinkProperties.hasIPv4Address() && !newLp.hasIPv4Address()) ||
+ (mLinkProperties.isIPv6Provisioned() && !newLp.isIPv6Provisioned());
final DetailedState detailedState = getNetworkDetailedState();
if (linkChanged) {
@@ -3992,12 +4485,18 @@
+ " old: " + mLinkProperties + " new: " + newLp);
}
mLinkProperties = newLp;
- if (TextUtils.isEmpty(mTcpBufferSizes) == false) {
- mLinkProperties.setTcpBufferSizes(mTcpBufferSizes);
+ if (mIpReachabilityMonitor != null) {
+ mIpReachabilityMonitor.updateLinkProperties(mLinkProperties);
}
if (mNetworkAgent != null) mNetworkAgent.sendLinkProperties(mLinkProperties);
}
+ if (lostProvisioning) {
+ log("Lost IP layer provisioning!" +
+ " was: " + mLinkProperties +
+ " now: " + newLp);
+ }
+
if (DBG) {
StringBuilder sb = new StringBuilder();
sb.append("updateLinkProperties nid: " + mLastNetworkId);
@@ -4027,7 +4526,7 @@
sb.append(" isprov");
}
}
- loge(sb.toString());
+ logd(sb.toString());
}
// If we just configured or lost IP configuration, do the needful.
@@ -4046,7 +4545,7 @@
// TODO: disconnect here instead. If our configuration is not usable, there's no
// point in staying connected, and if mLinkProperties is out of sync with
// reality, that will cause problems in the future.
- loge("IPv4 config succeeded, but not provisioned");
+ logd("IPv4 config succeeded, but not provisioned");
}
break;
@@ -4054,7 +4553,7 @@
// DHCP failed. If we're not already provisioned, or we had IPv4 and now lost it,
// give up and disconnect.
// If we're already provisioned (e.g., IPv6-only network), stay connected.
- if (!isProvisioned || lostIPv4Provisioning) {
+ if (!isProvisioned || lostProvisioning) {
sendMessage(CMD_IP_CONFIGURATION_LOST);
} else {
// DHCP failed, but we're provisioned (e.g., if we're on an IPv6-only network).
@@ -4085,7 +4584,7 @@
case CMD_UPDATE_LINKPROPERTIES:
// IP addresses, DNS servers, etc. changed. Act accordingly.
- if (wasProvisioned && !isProvisioned) {
+ if (lostProvisioning) {
// We no longer have a usable network configuration. Disconnect.
sendMessage(CMD_IP_CONFIGURATION_LOST);
} else if (!wasProvisioned && isProvisioned) {
@@ -4103,66 +4602,70 @@
/**
* Clears all our link properties.
*/
- private void clearLinkProperties() {
- // Clear the link properties obtained from DHCP and netlink.
- synchronized (mDhcpResultsLock) {
- if (mDhcpResults != null) {
- mDhcpResults.clear();
- }
- }
- mNetlinkTracker.clearLinkProperties();
+ private void clearLinkProperties() {
+ // Clear the link properties obtained from DHCP and netlink.
+ synchronized (mDhcpResultsLock) {
+ if (mDhcpResults != null) {
+ mDhcpResults.clear();
+ }
+ }
+ mNetlinkTracker.clearLinkProperties();
+ if (mIpReachabilityMonitor != null) {
+ mIpReachabilityMonitor.clearLinkProperties();
+ }
- // Now clear the merged link properties.
- mLinkProperties.clear();
- if (mNetworkAgent != null) mNetworkAgent.sendLinkProperties(mLinkProperties);
- }
+ // Now clear the merged link properties.
+ mLinkProperties.clear();
+ if (mNetworkAgent != null) mNetworkAgent.sendLinkProperties(mLinkProperties);
+ }
- /**
- * try to update default route MAC address.
- */
- private String updateDefaultRouteMacAddress(int timeout) {
- String address = null;
- for (RouteInfo route : mLinkProperties.getRoutes()) {
- if (route.isDefaultRoute() && route.hasGateway()) {
- InetAddress gateway = route.getGateway();
- if (gateway instanceof Inet4Address) {
- if (PDBG) {
- loge("updateDefaultRouteMacAddress found Ipv4 default :"
- + gateway.getHostAddress());
- }
- address = macAddressFromRoute(gateway.getHostAddress());
- /* The gateway's MAC address is known */
- if ((address == null) && (timeout > 0)) {
- boolean reachable = false;
- try {
- reachable = gateway.isReachable(timeout);
- } catch (Exception e) {
- loge("updateDefaultRouteMacAddress exception reaching :"
- + gateway.getHostAddress());
+ /**
+ * try to update default route MAC address.
+ */
+ private String updateDefaultRouteMacAddress(int timeout) {
+ String address = null;
+ for (RouteInfo route : mLinkProperties.getRoutes()) {
+ if (route.isDefaultRoute() && route.hasGateway()) {
+ InetAddress gateway = route.getGateway();
+ if (gateway instanceof Inet4Address) {
+ if (PDBG) {
+ logd("updateDefaultRouteMacAddress found Ipv4 default :"
+ + gateway.getHostAddress());
+ }
+ address = macAddressFromRoute(gateway.getHostAddress());
+ /* The gateway's MAC address is known */
+ if ((address == null) && (timeout > 0)) {
+ boolean reachable = false;
+ try {
+ reachable = gateway.isReachable(timeout);
+ } catch (Exception e) {
+ loge("updateDefaultRouteMacAddress exception reaching :"
+ + gateway.getHostAddress());
- } finally {
- if (reachable == true) {
+ } finally {
+ if (reachable == true) {
- address = macAddressFromRoute(gateway.getHostAddress());
- if (PDBG) {
- loge("updateDefaultRouteMacAddress reachable (tried again) :"
- + gateway.getHostAddress() + " found " + address);
- }
- }
- }
- }
- if (address != null) {
- mWifiConfigStore.setDefaultGwMacAddress(mLastNetworkId, address);
- }
- }
- }
- }
- return address;
- }
+ address = macAddressFromRoute(gateway.getHostAddress());
+ if (PDBG) {
+ logd("updateDefaultRouteMacAddress reachable (tried again) :"
+ + gateway.getHostAddress() + " found " + address);
+ }
+ }
+ }
+ }
+ if (address != null) {
+ mWifiConfigStore.setDefaultGwMacAddress(mLastNetworkId, address);
+ }
+ }
+ }
+ }
+ return address;
+ }
- private void sendScanResultsAvailableBroadcast() {
+ void sendScanResultsAvailableBroadcast(boolean scanSucceeded) {
Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, scanSucceeded);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
@@ -4182,16 +4685,43 @@
Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
- intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties (mLinkProperties));
+ intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties(mLinkProperties));
if (bssid != null)
intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
if (mNetworkInfo.getDetailedState() == DetailedState.VERIFYING_POOR_LINK ||
mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) {
- intent.putExtra(WifiManager.EXTRA_WIFI_INFO, new WifiInfo(mWifiInfo));
+ // We no longer report MAC address to third-parties and our code does
+ // not rely on this broadcast, so just send the default MAC address.
+ WifiInfo sentWifiInfo = new WifiInfo(mWifiInfo);
+ sentWifiInfo.setMacAddress(WifiInfo.DEFAULT_MAC_ADDRESS);
+ intent.putExtra(WifiManager.EXTRA_WIFI_INFO, sentWifiInfo);
}
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
+ private WifiInfo getWiFiInfoForUid(int uid) {
+ if (Binder.getCallingUid() == Process.myUid()) {
+ return mWifiInfo;
+ }
+
+ WifiInfo result = new WifiInfo(mWifiInfo);
+ result.setMacAddress(WifiInfo.DEFAULT_MAC_ADDRESS);
+
+ IBinder binder = ServiceManager.getService("package");
+ IPackageManager packageManager = IPackageManager.Stub.asInterface(binder);
+
+ try {
+ if (packageManager.checkUidPermission(Manifest.permission.LOCAL_MAC_ADDRESS,
+ uid) == PackageManager.PERMISSION_GRANTED) {
+ result.setMacAddress(mWifiInfo.getMacAddress());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error checking receiver permission", e);
+ }
+
+ return result;
+ }
+
private void sendLinkConfigurationChangedBroadcast() {
Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
@@ -4208,6 +4738,7 @@
/**
* Record the detailed state of a network.
+ *
* @param state the new {@code DetailedState}
*/
private boolean setNetworkDetailedState(NetworkInfo.DetailedState state) {
@@ -4237,7 +4768,7 @@
// Always indicate that SSID has changed
if (!mNetworkInfo.getExtraInfo().equals(mWifiInfo.getSSID())) {
if (DBG) {
- log("setDetailed state send new extra info" + mWifiInfo.getSSID());
+ log("setDetailed state send new extra info" + mWifiInfo.getSSID());
}
mNetworkInfo.setExtraInfo(mWifiInfo.getSSID());
sendNetworkStateChangeBroadcast(null);
@@ -4262,7 +4793,6 @@
return mNetworkInfo.getDetailedState();
}
-
private SupplicantState handleSupplicantStateChange(Message message) {
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
SupplicantState state = stateChangeResult.state;
@@ -4279,7 +4809,36 @@
}
mWifiInfo.setBSSID(stateChangeResult.BSSID);
+
+ if (mWhiteListedSsids != null
+ && mWhiteListedSsids.length > 0
+ && stateChangeResult.wifiSsid != null) {
+ String SSID = stateChangeResult.wifiSsid.toString();
+ String currentSSID = mWifiInfo.getSSID();
+ if (SSID != null
+ && currentSSID != null
+ && !SSID.equals(WifiSsid.NONE)) {
+ // Remove quote before comparing
+ if (SSID.length() >= 2 && SSID.charAt(0) == '"'
+ && SSID.charAt(SSID.length() - 1) == '"')
+ {
+ SSID = SSID.substring(1, SSID.length() - 1);
+ }
+ if (currentSSID.length() >= 2 && currentSSID.charAt(0) == '"'
+ && currentSSID.charAt(currentSSID.length() - 1) == '"') {
+ currentSSID = currentSSID.substring(1, currentSSID.length() - 1);
+ }
+ if ((!SSID.equals(currentSSID)) && (getCurrentState() == mConnectedState)) {
+ lastConnectAttemptTimestamp = System.currentTimeMillis();
+ targetWificonfiguration
+ = mWifiConfigStore.getWifiConfiguration(mWifiInfo.getNetworkId());
+ transitionTo(mRoamingState);
+ }
+ }
+ }
+
mWifiInfo.setSSID(stateChangeResult.wifiSsid);
+ mWifiInfo.setEphemeral(mWifiConfigStore.isEphemeral(mWifiInfo.getNetworkId()));
mSupplicantStateTracker.sendMessage(Message.obtain(message));
@@ -4293,9 +4852,9 @@
private void handleNetworkDisconnect() {
if (DBG) log("handleNetworkDisconnect: Stopping DHCP and clearing IP"
+ " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
- +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName()
- +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName()
- +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName());
+ + " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
+ + " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
+ + " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
clearCurrentConfigBSSID("handleNetworkDisconnect");
@@ -4318,7 +4877,7 @@
/**
* fullBandConnectedTimeIntervalMilli:
- * - start scans at mWifiConfigStore.associatedPartialScanPeriodMilli seconds interval
+ * - start scans at mWifiConfigStore.wifiAssociatedShortScanIntervalMilli seconds interval
* - exponentially increase to mWifiConfigStore.associatedFullScanMaxIntervalMilli
* Initialize to sane value = 20 seconds
*/
@@ -4340,16 +4899,18 @@
/* Cancel auto roam requests */
autoRoamSetBSSID(mLastNetworkId, "any");
- mLastBssid= null;
+ mLastBssid = null;
registerDisconnected();
mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
}
- private void handleSupplicantConnectionLoss() {
+ private void handleSupplicantConnectionLoss(boolean killSupplicant) {
/* Socket connection can be lost when we do a graceful shutdown
* or when the driver is hung. Ensure supplicant is stopped here.
*/
- mWifiMonitor.killSupplicant(mP2pSupported);
+ if (killSupplicant) {
+ mWifiMonitor.killSupplicant(mP2pSupported);
+ }
mWifiNative.closeSupplicantConnection();
sendSupplicantConnectionChangedBroadcast(false);
setWifiState(WIFI_STATE_DISABLED);
@@ -4386,9 +4947,6 @@
setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, false);
mWifiNative.setPowerSave(false);
- stopBatchedScan();
- WifiNative.pauseScan();
-
// Update link layer stats
getWifiLinkLayerStats(false);
@@ -4402,22 +4960,32 @@
}
- void startDhcp() {
- if (mDhcpStateMachine == null) {
- mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
- mContext, WifiStateMachine.this, mInterfaceName);
+ private boolean useLegacyDhcpClient() {
+ return Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.LEGACY_DHCP_CLIENT, 0) == 1;
+ }
+ private void maybeInitDhcpStateMachine() {
+ if (mDhcpStateMachine == null) {
+ if (useLegacyDhcpClient()) {
+ mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
+ mContext, WifiStateMachine.this, mInterfaceName);
+ } else {
+ mDhcpStateMachine = DhcpClient.makeDhcpStateMachine(
+ mContext, WifiStateMachine.this, mInterfaceName);
+ }
}
+ }
+
+ void startDhcp() {
+ maybeInitDhcpStateMachine();
mDhcpStateMachine.registerForPreDhcpNotification();
mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
}
void renewDhcp() {
- if (mDhcpStateMachine == null) {
- mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
- mContext, WifiStateMachine.this, mInterfaceName);
-
- }
+ maybeInitDhcpStateMachine();
mDhcpStateMachine.registerForPreDhcpNotification();
mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_RENEW_DHCP);
}
@@ -4442,36 +5010,34 @@
mWifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
mDhcpActive = false;
+ }
- startBatchedScan();
- WifiNative.restartScan();
+ void connectScanningService() {
+
+ if (mWifiScanner == null) {
+ mWifiScanner = (WifiScanner) mContext.getSystemService(Context.WIFI_SCANNING_SERVICE);
+ }
}
private void handleIPv4Success(DhcpResults dhcpResults, int reason) {
if (PDBG) {
- loge("wifistatemachine handleIPv4Success <" + dhcpResults.toString() + ">");
- loge("link address " + dhcpResults.ipAddress);
+ logd("handleIPv4Success <" + dhcpResults.toString() + ">");
+ logd("link address " + dhcpResults.ipAddress);
}
+ Inet4Address addr;
synchronized (mDhcpResultsLock) {
mDhcpResults = dhcpResults;
+ addr = (Inet4Address) dhcpResults.ipAddress.getAddress();
}
- Inet4Address addr = (Inet4Address) dhcpResults.ipAddress.getAddress();
if (isRoaming()) {
- if (addr instanceof Inet4Address) {
- int previousAddress = mWifiInfo.getIpAddress();
- int newAddress = NetworkUtils.inetAddressToInt(addr);
- if (previousAddress != newAddress) {
- loge("handleIPv4Success, roaming and address changed" +
- mWifiInfo + " got: " + addr);
- } else {
-
- }
- } else {
- loge("handleIPv4Success, roaming and didnt get an IPv4 address" +
- addr.toString());
+ int previousAddress = mWifiInfo.getIpAddress();
+ int newAddress = NetworkUtils.inetAddressToInt(addr);
+ if (previousAddress != newAddress) {
+ logd("handleIPv4Success, roaming and address changed" +
+ mWifiInfo + " got: " + addr);
}
}
mWifiInfo.setInetAddress(addr);
@@ -4492,7 +5058,7 @@
if (c != null) {
ScanResult result = getCurrentScanResult();
if (result == null) {
- loge("WifiStateMachine: handleSuccessfulIpConfiguration and no scan results" +
+ logd("WifiStateMachine: handleSuccessfulIpConfiguration and no scan results" +
c.configKey());
} else {
// Clear the per BSSID failure count
@@ -4502,7 +5068,7 @@
// this will typically happen if the user walks away and come back to his arrea
// TODO: implement blacklisting based on a timer, i.e. keep BSSID blacklisted
// in supplicant for a couple of hours or a day
- mWifiNative.clearBlacklist();
+ mWifiConfigStore.clearBssidBlacklist();
}
}
}
@@ -4514,7 +5080,7 @@
}
}
if (PDBG) {
- loge("wifistatemachine handleIPv4Failure");
+ logd("handleIPv4Failure");
}
updateLinkProperties(reason);
}
@@ -4532,13 +5098,150 @@
mWifiNative.disconnect();
}
+ // TODO: De-duplicated this and handleIpConfigurationLost().
+ private void handleIpReachabilityLost() {
+ // No need to be told about any additional neighbors that might also
+ // become unreachable--quiet them now while we start disconnecting.
+ if (mIpReachabilityMonitor != null) {
+ mIpReachabilityMonitor.clearLinkProperties();
+ }
+
+ mWifiInfo.setInetAddress(null);
+ mWifiInfo.setMeteredHint(false);
+
+ // TODO: Determine whether to call some form of mWifiConfigStore.handleSSIDStateChange().
+
+ // Disconnect via supplicant, and let autojoin retry connecting to the network.
+ mWifiNative.disconnect();
+ }
+
+ private int convertFrequencyToChannelNumber(int frequency) {
+ if (frequency >= 2412 && frequency <= 2484) {
+ return (frequency -2412) / 5 + 1;
+ } else if (frequency >= 5170 && frequency <=5825) {
+ //DFS is included
+ return (frequency -5170) / 5 + 34;
+ } else {
+ return 0;
+ }
+ }
+
+ private int chooseApChannel(int apBand) {
+ int apChannel;
+ int[] channel;
+
+ if (apBand == 0) {
+ if (mWifiApConfigStore.allowed2GChannel == null ||
+ mWifiApConfigStore.allowed2GChannel.size() == 0) {
+ //most safe channel to use
+ if(DBG) {
+ Log.d(TAG, "No specified 2G allowed channel list");
+ }
+ apChannel = 6;
+ } else {
+ int index = mRandom.nextInt(mWifiApConfigStore.allowed2GChannel.size());
+ apChannel = mWifiApConfigStore.allowed2GChannel.get(index).intValue();
+ }
+ } else {
+ //5G without DFS
+ channel = mWifiNative.getChannelsForBand(2);
+ if (channel != null && channel.length > 0) {
+ apChannel = channel[mRandom.nextInt(channel.length)];
+ apChannel = convertFrequencyToChannelNumber(apChannel);
+ } else {
+ Log.e(TAG, "SoftAp do not get available channel list");
+ apChannel = 0;
+ }
+ }
+
+ if(DBG) {
+ Log.d(TAG, "SoftAp set on channel " + apChannel);
+ }
+
+ return apChannel;
+ }
+
+ /* SoftAP configuration */
+ private boolean enableSoftAp() {
+ if (WifiNative.getInterfaces() != 0) {
+ if (!mWifiNative.toggleInterface(0)) {
+ if (DBG) Log.e(TAG, "toggleInterface failed");
+ return false;
+ }
+ } else {
+ if (DBG) Log.d(TAG, "No interfaces to toggle");
+ }
+
+ try {
+ mNwService.wifiFirmwareReload(mInterfaceName, "AP");
+ if (DBG) Log.d(TAG, "Firmware reloaded in AP mode");
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to reload AP firmware " + e);
+ }
+
+ if (WifiNative.startHal() == false) {
+ /* starting HAL is optional */
+ Log.e(TAG, "Failed to start HAL");
+ }
+ return true;
+ }
+
/* Current design is to not set the config on a running hostapd but instead
* stop and start tethering when user changes config on a running access point
*
* TODO: Add control channel setup through hostapd that allows changing config
* on a running daemon
*/
- private void startSoftApWithConfig(final WifiConfiguration config) {
+ private void startSoftApWithConfig(final WifiConfiguration configuration) {
+ // set channel
+ final WifiConfiguration config = new WifiConfiguration(configuration);
+
+ if (DBG) {
+ Log.d(TAG, "SoftAp config channel is: " + config.apChannel);
+ }
+
+ //We need HAL support to set country code and get available channel list, if HAL is
+ //not available, like razor, we regress to original implementaion (2GHz, channel 6)
+ if (mWifiNative.isHalStarted()) {
+ //set country code through HAL Here
+ if (mSetCountryCode != null) {
+ if (!mWifiNative.setCountryCodeHal(mSetCountryCode.toUpperCase(Locale.ROOT))) {
+ if (config.apBand != 0) {
+ Log.e(TAG, "Fail to set country code. Can not setup Softap on 5GHz");
+ //countrycode is mandatory for 5GHz
+ sendMessage(CMD_START_AP_FAILURE, WifiManager.SAP_START_FAILURE_GENERAL);
+ return;
+ }
+ }
+ } else {
+ if (config.apBand != 0) {
+ //countrycode is mandatory for 5GHz
+ Log.e(TAG, "Can not setup softAp on 5GHz without country code!");
+ sendMessage(CMD_START_AP_FAILURE, WifiManager.SAP_START_FAILURE_GENERAL);
+ return;
+ }
+ }
+
+ if (config.apChannel == 0) {
+ config.apChannel = chooseApChannel(config.apBand);
+ if (config.apChannel == 0) {
+ if(mWifiNative.isGetChannelsForBandSupported()) {
+ //fail to get available channel
+ sendMessage(CMD_START_AP_FAILURE, WifiManager.SAP_START_FAILURE_NO_CHANNEL);
+ return;
+ } else {
+ //for some old device, wifiHal may not be supportedget valid channels are not
+ //supported
+ config.apBand = 0;
+ config.apChannel = 6;
+ }
+ }
+ }
+ } else {
+ //for some old device, wifiHal may not be supported
+ config.apBand = 0;
+ config.apChannel = 6;
+ }
// Start hostapd on a separate thread
new Thread(new Runnable() {
public void run() {
@@ -4551,7 +5254,7 @@
mNwService.startAccessPoint(config, mInterfaceName);
} catch (Exception e1) {
loge("Exception in softap re-start " + e1);
- sendMessage(CMD_START_AP_FAILURE);
+ sendMessage(CMD_START_AP_FAILURE, WifiManager.SAP_START_FAILURE_GENERAL);
return;
}
}
@@ -4750,33 +5453,10 @@
}
break;
case CMD_BOOT_COMPLETED:
- String countryCode = mPersistedCountryCode;
- if (TextUtils.isEmpty(countryCode) == false) {
- Settings.Global.putString(mContext.getContentResolver(),
- Settings.Global.WIFI_COUNTRY_CODE,
- countryCode);
- // It may be that the state transition that should send this info
- // to the driver happened between mPersistedCountryCode getting set
- // and now, so simply persisting it here would mean we have sent
- // nothing to the driver. Send the cmd so it might be set now.
- int sequenceNum = mCountryCodeSequence.incrementAndGet();
- sendMessageAtFrontOfQueue(CMD_SET_COUNTRY_CODE,
- sequenceNum, 0, countryCode);
- }
maybeRegisterNetworkFactory();
break;
- case CMD_SET_BATCHED_SCAN:
- recordBatchedScanSettings(message.arg1, message.arg2, (Bundle)message.obj);
- break;
- case CMD_POLL_BATCHED_SCAN:
- handleBatchedScanPollRequest();
- break;
- case CMD_START_NEXT_BATCHED_SCAN:
- startNextBatchedScan();
- break;
case CMD_SCREEN_STATE_CHANGED:
- handleScreenStateChanged(message.arg1 != 0,
- /* startBackgroundScanIfNeeded = */ false);
+ handleScreenStateChanged(message.arg1 != 0);
break;
/* Discard */
case CMD_START_SCAN:
@@ -4804,6 +5484,7 @@
case WifiMonitor.NETWORK_CONNECTION_EVENT:
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
case WifiMonitor.SCAN_RESULTS_EVENT:
+ case WifiMonitor.SCAN_FAILED_EVENT:
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
@@ -4839,6 +5520,10 @@
case CMD_DISCONNECTING_WATCHDOG_TIMER:
case CMD_ROAM_WATCHDOG_TIMER:
case CMD_DISABLE_EPHEMERAL_NETWORK:
+ case CMD_RESTART_AUTOJOIN_OFFLOAD:
+ case CMD_STARTED_PNO_DBG:
+ case CMD_STARTED_GSCAN_DBG:
+ case CMD_UPDATE_ASSOCIATED_SCAN_PERMISSION:
messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
break;
case DhcpStateMachine.CMD_ON_QUIT:
@@ -4886,11 +5571,13 @@
WifiManager.BUSY);
break;
case CMD_GET_SUPPORTED_FEATURES:
- if (WifiNative.startHal()) {
- int featureSet = WifiNative.getSupportedFeatureSet();
- replyToMessage(message, message.what, featureSet);
- } else {
- replyToMessage(message, message.what, 0);
+ int featureSet = WifiNative.getSupportedFeatureSet();
+ replyToMessage(message, message.what, featureSet);
+ break;
+ case CMD_FIRMWARE_ALERT:
+ if (mWifiLogger != null) {
+ byte[] buffer = (byte[])message.obj;
+ mWifiLogger.captureAlertData(message.arg1, buffer);
}
break;
case CMD_GET_LINK_LAYER_STATS:
@@ -4909,13 +5596,23 @@
case CMD_UPDATE_LINKPROPERTIES:
updateLinkProperties(CMD_UPDATE_LINKPROPERTIES);
break;
+ case CMD_GET_MATCHING_CONFIG:
+ replyToMessage(message, message.what);
+ break;
case CMD_IP_CONFIGURATION_SUCCESSFUL:
case CMD_IP_CONFIGURATION_LOST:
+ case CMD_IP_REACHABILITY_LOST:
messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
break;
case CMD_GET_CONNECTION_STATISTICS:
replyToMessage(message, message.what, mWifiConnectionStatistics);
break;
+ case CMD_REMOVE_APP_CONFIGURATIONS:
+ deferMessage(message);
+ break;
+ case CMD_REMOVE_USER_CONFIGURATIONS:
+ deferMessage(message);
+ break;
default:
loge("Error! unhandled message" + message);
break;
@@ -4927,8 +5624,8 @@
class InitialState extends State {
@Override
public void enter() {
+ WifiNative.stopHal();
mWifiNative.unloadDriver();
-
if (mWifiP2pChannel == null) {
mWifiP2pChannel = new AsyncChannel();
mWifiP2pChannel.connect(mContext, getHandler(),
@@ -4937,11 +5634,16 @@
if (mWifiApConfigChannel == null) {
mWifiApConfigChannel = new AsyncChannel();
- WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
+ mWifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
mContext, getHandler());
- wifiApConfigStore.loadApConfiguration();
+ mWifiApConfigStore.loadApConfiguration();
mWifiApConfigChannel.connectSync(mContext, getHandler(),
- wifiApConfigStore.getMessenger());
+ mWifiApConfigStore.getMessenger());
+ }
+
+ if (mWifiConfigStore.enableHalBasedPno.get()) {
+ // make sure developer Settings are in sync with the config option
+ mHalBasedPnoEnableInDevSettings = true;
}
}
@Override
@@ -4969,10 +5671,10 @@
// Set privacy extensions
mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
- // IPv6 is enabled only as long as access point is connected since:
- // - IPv6 addresses and routes stick around after disconnection
- // - kernel is unaware when connected and fails to start IPv6 negotiation
- // - kernel can start autoconfiguration when 802.1x is not complete
+ // IPv6 is enabled only as long as access point is connected since:
+ // - IPv6 addresses and routes stick around after disconnection
+ // - kernel is unaware when connected and fails to start IPv6 negotiation
+ // - kernel can start autoconfiguration when 802.1x is not complete
mNwService.disableIpv6(mInterfaceName);
} catch (RemoteException re) {
loge("Unable to change interface settings: " + re);
@@ -4985,7 +5687,13 @@
* on a running supplicant properly.
*/
mWifiMonitor.killSupplicant(mP2pSupported);
- if(mWifiNative.startSupplicant(mP2pSupported)) {
+
+ if (WifiNative.startHal() == false) {
+ /* starting HAL is optional */
+ loge("Failed to start HAL");
+ }
+
+ if (mWifiNative.startSupplicant(mP2pSupported)) {
setWifiState(WIFI_STATE_ENABLING);
if (DBG) log("Supplicant start successful");
mWifiMonitor.startMonitoring();
@@ -4998,12 +5706,19 @@
}
break;
case CMD_START_AP:
- if (mWifiNative.loadDriver()) {
- setWifiApState(WIFI_AP_STATE_ENABLING);
- transitionTo(mSoftApStartingState);
- } else {
+ if (mWifiNative.loadDriver() == false) {
loge("Failed to load driver for softap");
+ } else {
+ if (enableSoftAp() == true) {
+ setWifiApState(WIFI_AP_STATE_ENABLING, 0);
+ transitionTo(mSoftApStartingState);
+ } else {
+ setWifiApState(WIFI_AP_STATE_FAILED,
+ WifiManager.SAP_START_FAILURE_GENERAL);
+ transitionTo(mInitialState);
+ }
}
+ break;
default:
return NOT_HANDLED;
}
@@ -5060,13 +5775,12 @@
mLastSignalLevel = -1;
mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
+ /* set frequency band of operation */
+ setFrequencyBand();
mWifiNative.enableSaveConfig();
mWifiConfigStore.loadAndEnableAllNetworks();
- if (mWifiConfigStore.enableVerboseLogging > 0) {
- enableVerboseLogging(mWifiConfigStore.enableVerboseLogging);
- }
- if (mWifiConfigStore.associatedPartialScanPeriodMilli < 0) {
- mWifiConfigStore.associatedPartialScanPeriodMilli = 0;
+ if (mWifiConfigStore.enableVerboseLogging.get() > 0) {
+ enableVerboseLogging(mWifiConfigStore.enableVerboseLogging.get());
}
initializeWpsDetails();
@@ -5125,6 +5839,12 @@
mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);
mWifiNative.setExternalSim(true);
+ /* turn on use of DFS channels */
+ WifiNative.setDfsFlag(true);
+
+ /* set country code */
+ setCountryCode();
+
setRandomMacOui();
mWifiNative.enableAutoConnect(false);
}
@@ -5143,7 +5863,7 @@
break;
case WifiMonitor.SUP_DISCONNECTION_EVENT: /* Supplicant connection lost */
loge("Connection lost, restart supplicant");
- handleSupplicantConnectionLoss();
+ handleSupplicantConnectionLoss(true);
handleNetworkDisconnect();
mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
if (mP2pSupported) {
@@ -5154,13 +5874,15 @@
sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
break;
case WifiMonitor.SCAN_RESULTS_EVENT:
+ case WifiMonitor.SCAN_FAILED_EVENT:
maybeRegisterNetworkFactory(); // Make sure our NetworkFactory is registered
closeRadioScanStats();
noteScanEnd();
setScanResults();
if (mIsFullScanOngoing || mSendScanResultsBroadcast) {
/* Just updated results from full scan, let apps know about this */
- sendScanResultsAvailableBroadcast();
+ boolean scanSucceeded = message.what == WifiMonitor.SCAN_RESULTS_EVENT;
+ sendScanResultsAvailableBroadcast(scanSucceeded);
}
mSendScanResultsBroadcast = false;
mIsScanOngoing = false;
@@ -5179,7 +5901,7 @@
case CMD_START_AP:
/* Cannot start soft AP while in client mode */
loge("Failed to start soft AP with a running supplicant");
- setWifiApState(WIFI_AP_STATE_FAILED);
+ setWifiApState(WIFI_AP_STATE_FAILED, WifiManager.SAP_START_FAILURE_GENERAL);
break;
case CMD_SET_OPERATIONAL_MODE:
mOperationalMode = message.arg1;
@@ -5200,6 +5922,29 @@
}
replyToMessage(message, message.what, stats);
break;
+ case CMD_SET_COUNTRY_CODE:
+ String country = (String) message.obj;
+
+ final boolean persist = (message.arg2 == 1);
+ final int sequence = message.arg1;
+
+ if (sequence != mCountryCodeSequence.get()) {
+ if (DBG) log("set country code ignored due to sequnce num");
+ break;
+ }
+ if (DBG) log("set country code " + country);
+ country = country.toUpperCase(Locale.ROOT);
+
+ if (mDriverSetCountryCode == null || !mDriverSetCountryCode.equals(country)) {
+ if (mWifiNative.setCountryCode(country)) {
+ mDriverSetCountryCode = country;
+ } else {
+ loge("Failed to set country code " + country);
+ }
+ }
+
+ mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.SET_COUNTRY_CODE, country);
+ break;
default:
return NOT_HANDLED;
}
@@ -5227,7 +5972,7 @@
String p2pSuppState = System.getProperty("init.svc.p2p_supplicant");
if (p2pSuppState == null) p2pSuppState = "unknown";
- loge("SupplicantStoppingState: stopSupplicant "
+ logd("SupplicantStoppingState: stopSupplicant "
+ " init.svc.wpa_supplicant=" + suppState
+ " init.svc.p2p_supplicant=" + p2pSuppState);
mWifiMonitor.stopSupplicant();
@@ -5248,13 +5993,13 @@
break;
case WifiMonitor.SUP_DISCONNECTION_EVENT:
if (DBG) log("Supplicant connection lost");
- handleSupplicantConnectionLoss();
+ handleSupplicantConnectionLoss(false);
transitionTo(mInitialState);
break;
case CMD_STOP_SUPPLICANT_FAILED:
if (message.arg1 == mSupplicantStopFailureToken) {
loge("Timed out on a supplicant stop, kill and proceed");
- handleSupplicantConnectionLoss();
+ handleSupplicantConnectionLoss(true);
transitionTo(mInitialState);
}
break;
@@ -5340,6 +6085,7 @@
deferMessage(message);
break;
case WifiMonitor.SCAN_RESULTS_EVENT:
+ case WifiMonitor.SCAN_FAILED_EVENT:
// Loose scan results obtained in Driver Starting state, they can only confuse
// the state machine
break;
@@ -5355,8 +6101,10 @@
public void enter() {
if (PDBG) {
- loge("DriverStartedState enter");
+ logd("DriverStartedState enter");
}
+
+ mWifiLogger.startLogging(mVerboseLoggingLevel > 0);
mIsRunning = true;
mInDelayedStop = false;
mDelayedStopCounter++;
@@ -5367,10 +6115,6 @@
* driver are changed to reduce interference with bluetooth
*/
mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
- /* set country code */
- setCountryCode();
- /* set frequency band of operation */
- setFrequencyBand();
/* initialize network state */
setNetworkDetailedState(DetailedState.DISCONNECTED);
@@ -5386,8 +6130,6 @@
mDhcpActive = false;
- startBatchedScan();
-
if (mOperationalMode != CONNECT_MODE) {
mWifiNative.disconnect();
mWifiConfigStore.disableAllNetworks();
@@ -5405,14 +6147,14 @@
mWifiNative.status();
// Transitioning to Disconnected state will trigger a scan and subsequently AutoJoin
transitionTo(mDisconnectedState);
+ transitionTo(mDisconnectedState);
}
// We may have missed screen update at boot
if (mScreenBroadcastReceived.get() == false) {
PowerManager powerManager = (PowerManager)mContext.getSystemService(
Context.POWER_SERVICE);
- handleScreenStateChanged(powerManager.isScreenOn(),
- /* startBackgroundScanIfNeeded = */ false);
+ handleScreenStateChanged(powerManager.isScreenOn());
} else {
// Set the right suspend mode settings
mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0
@@ -5435,8 +6177,18 @@
intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ mHalFeatureSet = WifiNative.getSupportedFeatureSet();
+ if ((mHalFeatureSet & WifiManager.WIFI_FEATURE_HAL_EPNO)
+ == WifiManager.WIFI_FEATURE_HAL_EPNO) {
+ mHalBasedPnoDriverSupported = true;
+ }
+
+ // Enable link layer stats gathering
+ mWifiNative.setWifiLinkLayerStats("wlan0", 1);
+
if (PDBG) {
- loge("Driverstarted State enter done");
+ logd("Driverstarted State enter done, epno=" + mHalBasedPnoDriverSupported
+ + " feature=" + mHalFeatureSet);
}
}
@@ -5448,56 +6200,20 @@
case CMD_START_SCAN:
handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
break;
- case CMD_SET_BATCHED_SCAN:
- if (recordBatchedScanSettings(message.arg1, message.arg2,
- (Bundle)message.obj)) {
- if (mBatchedScanSettings != null) {
- startBatchedScan();
- } else {
- stopBatchedScan();
- }
- }
- break;
- case CMD_SET_COUNTRY_CODE:
- String country = (String) message.obj;
- final boolean persist = (message.arg2 == 1);
- final int sequence = message.arg1;
- if (sequence != mCountryCodeSequence.get()) {
- if (DBG) log("set country code ignored due to sequnce num");
- break;
- }
- if (DBG) log("set country code " + country);
- if (persist) {
- mPersistedCountryCode = country;
- Settings.Global.putString(mContext.getContentResolver(),
- Settings.Global.WIFI_COUNTRY_CODE,
- country);
- }
- country = country.toUpperCase(Locale.ROOT);
- if (mLastSetCountryCode == null
- || country.equals(mLastSetCountryCode) == false) {
- if (mWifiNative.setCountryCode(country)) {
- mLastSetCountryCode = country;
- } else {
- loge("Failed to set country code " + country);
- }
- }
- mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.SET_COUNTRY_CODE, country);
- break;
case CMD_SET_FREQUENCY_BAND:
int band = message.arg1;
if (DBG) log("set frequency band " + band);
if (mWifiNative.setBand(band)) {
- if (PDBG) loge("did set frequency band " + band);
+ if (PDBG) logd("did set frequency band " + band);
mFrequencyBand.set(band);
// Flush old data - like scan results
mWifiNative.bssFlush();
// Fetch the latest scan results when frequency band is set
- startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null);
+// startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null);
- if (PDBG) loge("done set frequency band " + band);
+ if (PDBG) logd("done set frequency band " + band);
} else {
loge("Failed to set frequency band " + band);
@@ -5601,6 +6317,9 @@
mWifiNative.startTdls(remoteAddress, enable);
}
break;
+ case WifiMonitor.ANQP_DONE_EVENT:
+ mWifiConfigStore.notifyANQPDone((Long) message.obj, message.arg1 != 0);
+ break;
default:
return NOT_HANDLED;
}
@@ -5608,11 +6327,12 @@
}
@Override
public void exit() {
+
+ mWifiLogger.stopLogging();
+
mIsRunning = false;
updateBatteryWorkSource(null);
- mScanResults = new ArrayList<ScanResult>();
-
- stopBatchedScan();
+ mScanResults = new ArrayList<>();
final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
@@ -5620,8 +6340,6 @@
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
noteScanEnd(); // wrap up any pending request.
mBufferedScanMsg.clear();
-
- mLastSetCountryCode = null;
}
}
@@ -5920,7 +6638,7 @@
s = "CMD_GET_CONFIGURED_NETWORKS";
break;
case CMD_GET_SUPPORTED_FEATURES:
- s = "CMD_GET_ADAPTORS";
+ s = "CMD_GET_SUPPORTED_FEATURES";
break;
case CMD_UNWANTED_NETWORK:
s = "CMD_UNWANTED_NETWORK";
@@ -5931,6 +6649,9 @@
case CMD_GET_LINK_LAYER_STATS:
s = "CMD_GET_LINK_LAYER_STATS";
break;
+ case CMD_GET_MATCHING_CONFIG:
+ s = "CMD_GET_MATCHING_CONFIG";
+ break;
case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
s = "CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS";
break;
@@ -5970,15 +6691,6 @@
case CMD_NO_NETWORKS_PERIODIC_SCAN:
s = "CMD_NO_NETWORKS_PERIODIC_SCAN";
break;
- case CMD_SET_BATCHED_SCAN:
- s = "CMD_SET_BATCHED_SCAN";
- break;
- case CMD_START_NEXT_BATCHED_SCAN:
- s = "CMD_START_NEXT_BATCHED_SCAN";
- break;
- case CMD_POLL_BATCHED_SCAN:
- s = "CMD_POLL_BATCHED_SCAN";
- break;
case CMD_UPDATE_LINKPROPERTIES:
s = "CMD_UPDATE_LINKPROPERTIES";
break;
@@ -6003,6 +6715,9 @@
case WifiMonitor.SCAN_RESULTS_EVENT:
s = "SCAN_RESULTS_EVENT";
break;
+ case WifiMonitor.SCAN_FAILED_EVENT:
+ s = "SCAN_FAILED_EVENT";
+ break;
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
s = "SUPPLICANT_STATE_CHANGE_EVENT";
break;
@@ -6033,6 +6748,18 @@
case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
s = "ASSOCIATION_REJECTION_EVENT";
break;
+ case WifiMonitor.ANQP_DONE_EVENT:
+ s = "WifiMonitor.ANQP_DONE_EVENT";
+ break;
+ case WifiMonitor.GAS_QUERY_DONE_EVENT:
+ s = "WifiMonitor.GAS_QUERY_DONE_EVENT";
+ break;
+ case WifiMonitor.HS20_DEAUTH_EVENT:
+ s = "WifiMonitor.HS20_DEAUTH_EVENT";
+ break;
+ case WifiMonitor.GAS_QUERY_START_EVENT:
+ s = "WifiMonitor.GAS_QUERY_START_EVENT";
+ break;
case CMD_SET_OPERATIONAL_MODE:
s = "CMD_SET_OPERATIONAL_MODE";
break;
@@ -6099,6 +6826,9 @@
case CMD_IP_CONFIGURATION_SUCCESSFUL:
s = "CMD_IP_CONFIGURATION_SUCCESSFUL";
break;
+ case CMD_IP_REACHABILITY_LOST:
+ s = "CMD_IP_REACHABILITY_LOST";
+ break;
case CMD_STATIC_IP_SUCCESS:
s = "CMD_STATIC_IP_SUCCESSFUL";
break;
@@ -6117,6 +6847,12 @@
case CMD_ASSOCIATED_BSSID:
s = "CMD_ASSOCIATED_BSSID";
break;
+ case CMD_REMOVE_APP_CONFIGURATIONS:
+ s = "CMD_REMOVE_APP_CONFIGURATIONS";
+ break;
+ case CMD_REMOVE_USER_CONFIGURATIONS:
+ s = "CMD_REMOVE_USER_CONFIGURATIONS";
+ break;
case CMD_ROAM_WATCHDOG_TIMER:
s = "CMD_ROAM_WATCHDOG_TIMER";
break;
@@ -6126,6 +6862,21 @@
case CMD_DISCONNECTING_WATCHDOG_TIMER:
s = "CMD_DISCONNECTING_WATCHDOG_TIMER";
break;
+ case CMD_RESTART_AUTOJOIN_OFFLOAD:
+ s = "CMD_RESTART_AUTOJOIN_OFFLOAD";
+ break;
+ case CMD_STARTED_PNO_DBG:
+ s = "CMD_STARTED_PNO_DBG";
+ break;
+ case CMD_STARTED_GSCAN_DBG:
+ s = "CMD_STARTED_GSCAN_DBG";
+ break;
+ case CMD_PNO_NETWORK_FOUND:
+ s = "CMD_PNO_NETWORK_FOUND";
+ break;
+ case CMD_UPDATE_ASSOCIATED_SCAN_PERMISSION:
+ s = "CMD_UPDATE_ASSOCIATED_SCAN_PERMISSION";
+ break;
default:
s = "what:" + Integer.toString(what);
break;
@@ -6176,12 +6927,12 @@
&& rssi != WifiInfo.INVALID_RSSI
&& config != null) {
boolean is24GHz = mWifiInfo.is24GHz();
- boolean isBadRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdBadRssi24)
- || (!is24GHz && rssi < mWifiConfigStore.thresholdBadRssi5);
- boolean isLowRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdLowRssi24)
- || (!is24GHz && mWifiInfo.getRssi() < mWifiConfigStore.thresholdLowRssi5);
- boolean isHighRSSI = (is24GHz && rssi >= mWifiConfigStore.thresholdGoodRssi24)
- || (!is24GHz && mWifiInfo.getRssi() >= mWifiConfigStore.thresholdGoodRssi5);
+ boolean isBadRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdBadRssi24.get())
+ || (!is24GHz && rssi < mWifiConfigStore.thresholdBadRssi5.get());
+ boolean isLowRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdLowRssi24.get())
+ || (!is24GHz && mWifiInfo.getRssi() < mWifiConfigStore.thresholdLowRssi5.get());
+ boolean isHighRSSI = (is24GHz && rssi >= mWifiConfigStore.thresholdGoodRssi24.get())
+ || (!is24GHz && mWifiInfo.getRssi() >= mWifiConfigStore.thresholdGoodRssi5.get());
if (isBadRSSI) {
// Take note that we got disabled while RSSI was Bad
config.numUserTriggeredWifiDisableLowRSSI++;
@@ -6211,10 +6962,14 @@
if (BSSID == null) {
BSSID = mTargetRoamBSSID;
}
- if (config.scanResultCache == null) {
+ ScanDetailCache scanDetailCache =
+ mWifiConfigStore.getScanDetailCache(config);
+
+ if (scanDetailCache == null) {
return null;
}
- return config.scanResultCache.get(BSSID);
+
+ return scanDetailCache.get(BSSID);
}
String getCurrentBSSID() {
@@ -6225,6 +6980,12 @@
}
class ConnectModeState extends State {
+
+ @Override
+ public void enter() {
+ connectScanningService();
+ }
+
@Override
public boolean processMessage(Message message) {
WifiConfiguration config;
@@ -6238,6 +6999,7 @@
switch (message.what) {
case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
+ mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_ASSOC_FAILURE);
didBlackListBSSID = false;
bssid = (String) message.obj;
if (bssid == null || TextUtils.isEmpty(bssid)) {
@@ -6254,6 +7016,7 @@
mSupplicantStateTracker.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT);
break;
case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
+ mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_AUTH_FAILURE);
mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
break;
case WifiMonitor.SSID_TEMP_DISABLED:
@@ -6261,7 +7024,7 @@
String substr = (String) message.obj;
String en = message.what == WifiMonitor.SSID_TEMP_DISABLED ?
"temp-disabled" : "re-enabled";
- loge("ConnectModeState SSID state=" + en + " nid="
+ logd("ConnectModeState SSID state=" + en + " nid="
+ Integer.toString(message.arg1) + " [" + substr + "]");
synchronized(mScanResultCache) {
mWifiConfigStore.handleSSIDStateChange(message.arg1, message.what ==
@@ -6293,6 +7056,15 @@
handleNetworkDisconnect();
transitionTo(mDisconnectedState);
}
+
+ // If we have COMPLETED a connection to a BSSID, start doing
+ // DNAv4/DNAv6 -style probing for on-link neighbors of
+ // interest (e.g. routers); harmless if none are configured.
+ if (state == SupplicantState.COMPLETED) {
+ if (mIpReachabilityMonitor != null) {
+ mIpReachabilityMonitor.probeAll();
+ }
+ }
break;
case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
if (message.arg1 == 1) {
@@ -6305,6 +7077,17 @@
break;
case CMD_ADD_OR_UPDATE_NETWORK:
config = (WifiConfiguration) message.obj;
+
+ if (!recordUidIfAuthorized(config, message.sendingUid,
+ /* onlyAnnotate */ false)) {
+ logw("Not authorized to update network "
+ + " config=" + config.SSID
+ + " cnid=" + config.networkId
+ + " uid=" + message.sendingUid);
+ replyToMessage(message, message.what, FAILURE);
+ break;
+ }
+
int res = mWifiConfigStore.addOrUpdateNetwork(config, message.sendingUid);
if (res < 0) {
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
@@ -6317,9 +7100,11 @@
// Set the last selected configuration so as to allow the system to
// stick the last user choice without persisting the choice
mWifiConfigStore.setLastSelectedConfiguration(res);
+ mWifiConfigStore.updateLastConnectUid(config, message.sendingUid);
+ mWifiConfigStore.writeKnownNetworkHistory(false);
// Remember time of last connection attempt
- lastConnectAttempt = System.currentTimeMillis();
+ lastConnectAttemptTimestamp = System.currentTimeMillis();
mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
@@ -6331,6 +7116,16 @@
replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK, res);
break;
case CMD_REMOVE_NETWORK:
+ netId = message.arg1;
+ if (!mWifiConfigStore.canModifyNetwork(message.sendingUid, netId,
+ /* onlyAnnotate */ false)) {
+ logw("Not authorized to remove network "
+ + " cnid=" + netId
+ + " uid=" + message.sendingUid);
+ replyToMessage(message, message.what, FAILURE);
+ break;
+ }
+
ok = mWifiConfigStore.removeNetwork(message.arg1);
if (!ok) {
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
@@ -6338,28 +7133,38 @@
replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
break;
case CMD_ENABLE_NETWORK:
- boolean others = message.arg2 == 1;
+ boolean disableOthers = message.arg2 == 1;
+ netId = message.arg1;
+ config = mWifiConfigStore.getWifiConfiguration(netId);
+ if (config == null) {
+ loge("No network with id = " + netId);
+ messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
+ break;
+ }
+
// Tell autojoin the user did try to select to that network
// However, do NOT persist the choice by bumping the priority of the network
- if (others) {
+ if (disableOthers) {
mWifiAutoJoinController.
- updateConfigurationHistory(message.arg1, true, false);
+ updateConfigurationHistory(netId, true, false);
// Set the last selected configuration so as to allow the system to
// stick the last user choice without persisting the choice
- mWifiConfigStore.setLastSelectedConfiguration(message.arg1);
+ mWifiConfigStore.setLastSelectedConfiguration(netId);
// Remember time of last connection attempt
- lastConnectAttempt = System.currentTimeMillis();
+ lastConnectAttemptTimestamp = System.currentTimeMillis();
mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
}
// Cancel auto roam requests
- autoRoamSetBSSID(message.arg1, "any");
+ autoRoamSetBSSID(netId, "any");
- ok = mWifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
+ int uid = message.sendingUid;
+ ok = mWifiConfigStore.enableNetwork(netId, disableOthers, uid);
if (!ok) {
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
}
+
replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
break;
case CMD_ENABLE_ALL_NETWORKS:
@@ -6389,15 +7194,15 @@
}
break;
case CMD_BLACKLIST_NETWORK:
- mWifiNative.addToBlacklist((String) message.obj);
+ mWifiConfigStore.blackListBssid((String) message.obj);
break;
case CMD_CLEAR_BLACKLIST:
- mWifiNative.clearBlacklist();
+ mWifiConfigStore.clearBssidBlacklist();
break;
case CMD_SAVE_CONFIG:
ok = mWifiConfigStore.saveConfig();
- if (DBG) loge("wifistatemachine did save config " + ok);
+ if (DBG) logd("did save config " + ok);
replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
// Inform the backup manager about a data change
@@ -6416,19 +7221,56 @@
mWifiConfigStore.getConfiguredNetworks());
break;
case WifiMonitor.SUP_REQUEST_IDENTITY:
- // Supplicant lacks credentials to connect to that network, hence black list
- ssid = (String) message.obj;
+ int networkId = message.arg2;
+ boolean identitySent = false;
+ int eapMethod = WifiEnterpriseConfig.Eap.NONE;
- if (targetWificonfiguration != null && ssid != null
- && targetWificonfiguration.SSID != null
- && targetWificonfiguration.SSID.equals("\"" + ssid + "\"")) {
- mWifiConfigStore.handleSSIDStateChange(targetWificonfiguration.networkId,
- false, "AUTH_FAILED no identity", null);
+ if (targetWificonfiguration != null
+ && targetWificonfiguration.enterpriseConfig != null) {
+ eapMethod = targetWificonfiguration.enterpriseConfig.getEapMethod();
}
- // Disconnect now, as we don't have any way to fullfill the supplicant request.
- mWifiConfigStore.setLastSelectedConfiguration
- (WifiConfiguration.INVALID_NETWORK_ID);
- mWifiNative.disconnect();
+
+ // For SIM & AKA/AKA' EAP method Only, get identity from ICC
+ if (targetWificonfiguration != null
+ && targetWificonfiguration.networkId == networkId
+ && targetWificonfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.IEEE8021X)
+ && (eapMethod == WifiEnterpriseConfig.Eap.SIM
+ || eapMethod == WifiEnterpriseConfig.Eap.AKA
+ || eapMethod == WifiEnterpriseConfig.Eap.AKA_PRIME)) {
+ TelephonyManager tm = (TelephonyManager)
+ mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ if (tm != null) {
+ String imsi = tm.getSubscriberId();
+ String mccMnc = "";
+
+ if (tm.getSimState() == TelephonyManager.SIM_STATE_READY)
+ mccMnc = tm.getSimOperator();
+
+ String identity = buildIdentity(eapMethod, imsi, mccMnc);
+
+ if (!identity.isEmpty()) {
+ mWifiNative.simIdentityResponse(networkId, identity);
+ identitySent = true;
+ }
+ }
+ }
+ if (!identitySent) {
+ // Supplicant lacks credentials to connect to that network, hence black list
+ ssid = (String) message.obj;
+ if (targetWificonfiguration != null && ssid != null
+ && targetWificonfiguration.SSID != null
+ && targetWificonfiguration.SSID.equals("\"" + ssid + "\"")) {
+ mWifiConfigStore.handleSSIDStateChange(
+ targetWificonfiguration.networkId, false,
+ "AUTH_FAILED no identity", null);
+ }
+ // Disconnect now, as we don't have any way to fullfill
+ // the supplicant request.
+ mWifiConfigStore.setLastSelectedConfiguration(
+ WifiConfiguration.INVALID_NETWORK_ID);
+ mWifiNative.disconnect();
+ }
break;
case WifiMonitor.SUP_REQUEST_SIM_AUTH:
logd("Received SUP_REQUEST_SIM_AUTH");
@@ -6436,7 +7278,8 @@
if (requestData != null) {
if (requestData.protocol == WifiEnterpriseConfig.Eap.SIM) {
handleGsmAuthRequest(requestData);
- } else if (requestData.protocol == WifiEnterpriseConfig.Eap.AKA) {
+ } else if (requestData.protocol == WifiEnterpriseConfig.Eap.AKA
+ || requestData.protocol == WifiEnterpriseConfig.Eap.AKA_PRIME) {
handle3GAuthRequest(requestData);
}
} else {
@@ -6447,7 +7290,11 @@
replyToMessage(message, message.what,
mWifiConfigStore.getPrivilegedConfiguredNetworks());
break;
- /* Do a redundant disconnect without transition */
+ case CMD_GET_MATCHING_CONFIG:
+ replyToMessage(message, message.what,
+ mWifiConfigStore.getMatchingConfig((ScanResult)message.obj));
+ break;
+ /* Do a redundant disconnect without transition */
case CMD_DISCONNECT:
mWifiConfigStore.setLastSelectedConfiguration
(WifiConfiguration.INVALID_NETWORK_ID);
@@ -6457,14 +7304,14 @@
mWifiAutoJoinController.attemptAutoJoin();
break;
case CMD_REASSOCIATE:
- lastConnectAttempt = System.currentTimeMillis();
+ lastConnectAttemptTimestamp = System.currentTimeMillis();
mWifiNative.reassociate();
break;
case CMD_RELOAD_TLS_AND_RECONNECT:
if (mWifiConfigStore.needsUnlockedKeyStore()) {
logd("Reconnecting to give a chance to un-connected TLS networks");
mWifiNative.disconnect();
- lastConnectAttempt = System.currentTimeMillis();
+ lastConnectAttemptTimestamp = System.currentTimeMillis();
mWifiNative.reconnect();
}
break;
@@ -6493,7 +7340,7 @@
config = (WifiConfiguration) message.obj;
netId = message.arg1;
int roam = message.arg2;
- loge("CMD_AUTO_CONNECT sup state "
+ logd("CMD_AUTO_CONNECT sup state "
+ mSupplicantStateTracker.getSupplicantStateName()
+ " my state " + getCurrentState().getName()
+ " nid=" + Integer.toString(netId)
@@ -6507,19 +7354,45 @@
autoRoamSetBSSID(netId, config.BSSID);
/* Save the network config */
- loge("CMD_AUTO_CONNECT will save config -> " + config.SSID
+ logd("CMD_AUTO_CONNECT will save config -> " + config.SSID
+ " nid=" + Integer.toString(netId));
- result = mWifiConfigStore.saveNetwork(config, -1);
+ result = mWifiConfigStore.saveNetwork(config, WifiConfiguration.UNKNOWN_UID);
netId = result.getNetworkId();
- loge("CMD_AUTO_CONNECT did save config -> "
+ logd("CMD_AUTO_CONNECT did save config -> "
+ " nid=" + Integer.toString(netId));
+ // Since we updated the config,read it back from config store:
+ config = mWifiConfigStore.getWifiConfiguration(netId);
+ if (config == null) {
+ loge("CMD_AUTO_CONNECT couldn't update the config, got null config");
+ break;
+ }
+ if (netId != config.networkId) {
+ loge("CMD_AUTO_CONNECT couldn't update the config, want"
+ + " nid=" + Integer.toString(netId) + " but got" + config.networkId);
+ break;
+ }
+
+ if (deferForUserInput(message, netId, false)) {
+ break;
+ } else if (mWifiConfigStore.getWifiConfiguration(netId).userApproved ==
+ WifiConfiguration.USER_BANNED) {
+ replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
+ WifiManager.NOT_AUTHORIZED);
+ break;
+ }
+
// Make sure the network is enabled, since supplicant will not reenable it
mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
- if (mWifiConfigStore.selectNetwork(netId) &&
- mWifiNative.reconnect()) {
- lastConnectAttempt = System.currentTimeMillis();
+ // If we're autojoining a network that the user or an app explicitly selected,
+ // keep track of the UID that selected it.
+ int lastConnectUid = mWifiConfigStore.isLastSelectedConfiguration(config) ?
+ config.lastConnectUid : WifiConfiguration.UNKNOWN_UID;
+
+ if (mWifiConfigStore.selectNetwork(config, /* updatePriorities = */ false,
+ lastConnectUid) && mWifiNative.reconnect()) {
+ lastConnectAttemptTimestamp = System.currentTimeMillis();
targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
config = mWifiConfigStore.getWifiConfiguration(netId);
if (config != null
@@ -6546,6 +7419,16 @@
transitionTo(mDisconnectingState);
} else {
/* Already in disconnected state, nothing to change */
+ if (!mScreenOn && mLegacyPnoEnabled && mBackgroundScanSupported) {
+ int delay = 60 * 1000;
+ if (VDBG) {
+ logd("Starting PNO alarm: " + delay);
+ }
+ mAlarmManager.set(AlarmManager.RTC_WAKEUP,
+ System.currentTimeMillis() + delay,
+ mPnoIntent);
+ }
+ mRestartAutoJoinOffloadCounter++;
}
} else {
loge("Failed to connect config: " + config + " netId: " + netId);
@@ -6554,6 +7437,12 @@
break;
}
break;
+ case CMD_REMOVE_APP_CONFIGURATIONS:
+ mWifiConfigStore.removeNetworksForApp((ApplicationInfo) message.obj);
+ break;
+ case CMD_REMOVE_USER_CONFIGURATIONS:
+ mWifiConfigStore.removeNetworksForUser(message.arg1);
+ break;
case WifiManager.CONNECT_NETWORK:
/**
* The connect message can contain a network id passed as arg1 on message or
@@ -6568,6 +7457,21 @@
/* Save the network config */
if (config != null) {
+ // When connecting to an access point, WifiStateMachine wants to update the
+ // relevant config with administrative data. This update should not be
+ // considered a 'real' update, therefore lockdown by Device Owner must be
+ // disregarded.
+ if (!recordUidIfAuthorized(config, message.sendingUid,
+ /* onlyAnnotate */ true)) {
+ logw("Not authorized to update network "
+ + " config=" + config.SSID
+ + " cnid=" + config.networkId
+ + " uid=" + message.sendingUid);
+ replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
+ WifiManager.NOT_AUTHORIZED);
+ break;
+ }
+
String configKey = config.configKey(true /* allowCached */);
WifiConfiguration savedConfig =
mWifiConfigStore.getWifiConfiguration(configKey);
@@ -6576,7 +7480,7 @@
// (either AUTO_JOIN_DELETED or ephemeral; see WifiConfigStore#
// getConfiguredNetworks). Remove those bits and update the config.
config = savedConfig;
- loge("CONNECT_NETWORK updating existing config with id=" +
+ logd("CONNECT_NETWORK updating existing config with id=" +
config.networkId + " configKey=" + configKey);
config.ephemeral = false;
config.autoJoinStatus = WifiConfiguration.AUTO_JOIN_ENABLED;
@@ -6589,12 +7493,15 @@
config = mWifiConfigStore.getWifiConfiguration(netId);
if (config == null) {
- loge("CONNECT_NETWORK id=" + Integer.toString(netId) + " "
+ logd("CONNECT_NETWORK no config for id=" + Integer.toString(netId) + " "
+ mSupplicantStateTracker.getSupplicantStateName() + " my state "
+ getCurrentState().getName());
+ replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
+ WifiManager.ERROR);
+ break;
} else {
String wasSkipped = config.autoJoinBailedDueToLowRssi ? " skipped" : "";
- loge("CONNECT_NETWORK id=" + Integer.toString(netId)
+ logd("CONNECT_NETWORK id=" + Integer.toString(netId)
+ " config=" + config.SSID
+ " cnid=" + config.networkId
+ " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
@@ -6616,10 +7523,21 @@
clearConfigBSSID(config, "CONNECT_NETWORK");
}
+ if (deferForUserInput(message, netId, true)) {
+ break;
+ } else if (mWifiConfigStore.getWifiConfiguration(netId).userApproved ==
+ WifiConfiguration.USER_BANNED) {
+ replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
+ WifiManager.NOT_AUTHORIZED);
+ break;
+ }
+
mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
- /* Tell autojoin the user did try to connect to that network */
- mWifiAutoJoinController.updateConfigurationHistory(netId, true, true);
+ /* Tell autojoin the user did try to connect to that network if from settings */
+ boolean persist =
+ mWifiConfigStore.checkConfigOverridePermission(message.sendingUid);
+ mWifiAutoJoinController.updateConfigurationHistory(netId, true, persist);
mWifiConfigStore.setLastSelectedConfiguration(netId);
@@ -6636,9 +7554,9 @@
// Make sure the network is enabled, since supplicant will not reenable it
mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
- if (mWifiConfigStore.selectNetwork(netId) &&
- mWifiNative.reconnect()) {
- lastConnectAttempt = System.currentTimeMillis();
+ if (mWifiConfigStore.selectNetwork(config, /* updatePriorities = */ true,
+ message.sendingUid) && mWifiNative.reconnect()) {
+ lastConnectAttemptTimestamp = System.currentTimeMillis();
targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
/* The state tracker handles enabling networks upon completion/failure */
@@ -6683,13 +7601,26 @@
}
lastSavedConfigurationAttempt = new WifiConfiguration(config);
int nid = config.networkId;
- loge("SAVE_NETWORK id=" + Integer.toString(nid)
+ logd("SAVE_NETWORK id=" + Integer.toString(nid)
+ " config=" + config.SSID
+ " nid=" + config.networkId
+ " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
+ " my state " + getCurrentState().getName());
- result = mWifiConfigStore.saveNetwork(config, -1);
+ // Only record the uid if this is user initiated
+ boolean checkUid = (message.what == WifiManager.SAVE_NETWORK);
+ if (checkUid && !recordUidIfAuthorized(config, message.sendingUid,
+ /* onlyAnnotate */ false)) {
+ logw("Not authorized to update network "
+ + " config=" + config.SSID
+ + " cnid=" + config.networkId
+ + " uid=" + message.sendingUid);
+ replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
+ WifiManager.NOT_AUTHORIZED);
+ break;
+ }
+
+ result = mWifiConfigStore.saveNetwork(config, WifiConfiguration.UNKNOWN_UID);
if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
if (result.hasIpChanged()) {
@@ -6707,9 +7638,11 @@
}
}
replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
+ broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
+
if (VDBG) {
- loge("Success save network nid="
- + Integer.toString(result.getNetworkId()));
+ logd("Success save network nid="
+ + Integer.toString(result.getNetworkId()));
}
synchronized(mScanResultCache) {
@@ -6719,8 +7652,18 @@
* and interpret the SAVE_NETWORK as a request to connect
*/
boolean user = message.what == WifiManager.SAVE_NETWORK;
+
+ // Did this connect come from settings
+ boolean persistConnect =
+ mWifiConfigStore.checkConfigOverridePermission(message.sendingUid);
+
+ if (user) {
+ mWifiConfigStore.updateLastConnectUid(config, message.sendingUid);
+ mWifiConfigStore.writeKnownNetworkHistory(false);
+ }
+
mWifiAutoJoinController.updateConfigurationHistory(result.getNetworkId()
- , user, true);
+ , user, persistConnect);
mWifiAutoJoinController.attemptAutoJoin();
}
} else {
@@ -6739,8 +7682,23 @@
} else {
lastForgetConfigurationAttempt = new WifiConfiguration(toRemove);
}
+ // check that the caller owns this network
+ netId = message.arg1;
+
+ if (!mWifiConfigStore.canModifyNetwork(message.sendingUid, netId,
+ /* onlyAnnotate */ false)) {
+ logw("Not authorized to forget network "
+ + " cnid=" + netId
+ + " uid=" + message.sendingUid);
+ replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
+ WifiManager.NOT_AUTHORIZED);
+ break;
+ }
+
if (mWifiConfigStore.forgetNetwork(message.arg1)) {
replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
+ broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_FORGOT,
+ (WifiConfiguration) message.obj);
} else {
loge("Failed to forget network");
replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
@@ -6800,6 +7758,9 @@
handleNetworkDisconnect();
transitionTo(mDisconnectedState);
break;
+ case CMD_PNO_NETWORK_FOUND:
+ processPnoNetworkFound((ScanResult[])message.obj);
+ break;
default:
return NOT_HANDLED;
}
@@ -6828,20 +7789,34 @@
if (this != mNetworkAgent) return;
if (DBG) log("WifiNetworkAgent -> Wifi unwanted score "
+ Integer.toString(mWifiInfo.score));
- unwantedNetwork(network_status_unwanted_disconnect);
+ unwantedNetwork(NETWORK_STATUS_UNWANTED_DISCONNECT);
}
+ @Override
protected void networkStatus(int status) {
+ if (this != mNetworkAgent) return;
if (status == NetworkAgent.INVALID_NETWORK) {
if (DBG) log("WifiNetworkAgent -> Wifi networkStatus invalid, score="
+ Integer.toString(mWifiInfo.score));
- unwantedNetwork(network_status_unwanted_disable_autojoin);
+ unwantedNetwork(NETWORK_STATUS_UNWANTED_VALIDATION_FAILED);
} else if (status == NetworkAgent.VALID_NETWORK) {
if (DBG && mWifiInfo != null) log("WifiNetworkAgent -> Wifi networkStatus valid, score= "
+ Integer.toString(mWifiInfo.score));
doNetworkStatus(status);
}
}
+
+ @Override
+ protected void saveAcceptUnvalidated(boolean accept) {
+ if (this != mNetworkAgent) return;
+ WifiStateMachine.this.sendMessage(CMD_ACCEPT_UNVALIDATED, accept ? 1 : 0);
+ }
+
+ @Override
+ protected void preventAutomaticReconnect() {
+ if (this != mNetworkAgent) return;
+ unwantedNetwork(NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN);
+ }
}
void unwantedNetwork(int reason) {
@@ -6852,6 +7827,43 @@
sendMessage(CMD_NETWORK_STATUS, status);
}
+ // rfc4186 & rfc4187:
+ // create Permanent Identity base on IMSI,
+ // identity = usernam@realm
+ // with username = prefix | IMSI
+ // and realm is derived MMC/MNC tuple according 3GGP spec(TS23.003)
+ private String buildIdentity(int eapMethod, String imsi, String mccMnc) {
+ String mcc;
+ String mnc;
+ String prefix;
+
+ if (imsi == null || imsi.isEmpty())
+ return "";
+
+ if (eapMethod == WifiEnterpriseConfig.Eap.SIM)
+ prefix = "1";
+ else if (eapMethod == WifiEnterpriseConfig.Eap.AKA)
+ prefix = "0";
+ else if (eapMethod == WifiEnterpriseConfig.Eap.AKA_PRIME)
+ prefix = "6";
+ else // not a valide EapMethod
+ return "";
+
+ /* extract mcc & mnc from mccMnc */
+ if (mccMnc != null && !mccMnc.isEmpty()) {
+ mcc = mccMnc.substring(0, 3);
+ mnc = mccMnc.substring(3);
+ if (mnc.length() == 2)
+ mnc = "0" + mnc;
+ } else {
+ // extract mcc & mnc from IMSI, assume mnc size is 3
+ mcc = imsi.substring(0, 3);
+ mnc = imsi.substring(3, 6);
+ }
+
+ return prefix + imsi + "@wlan.mnc" + mnc + ".mcc" + mcc + ".3gppnetwork.org";
+ }
+
boolean startScanForConfiguration(WifiConfiguration config, boolean restrictChannelList) {
if (config == null)
return false;
@@ -6861,14 +7873,15 @@
// primary purpose of the partial scans is roaming.
// Full badn scans with exponential backoff for the purpose or extended roaming and
// network switching are performed unconditionally.
- if (config.scanResultCache == null
+ ScanDetailCache scanDetailCache =
+ mWifiConfigStore.getScanDetailCache(config);
+ if (scanDetailCache == null
|| !config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)
- || config.scanResultCache.size() > 6) {
+ || scanDetailCache.size() > 6) {
//return true but to not trigger the scan
return true;
}
- HashSet<Integer> channels
- = mWifiConfigStore.makeChannelList(config,
+ HashSet<Integer> channels = mWifiConfigStore.makeChannelList(config,
ONE_HOUR_MILLI, restrictChannelList);
if (channels != null && channels.size() != 0) {
StringBuilder freqs = new StringBuilder();
@@ -6880,7 +7893,7 @@
first = false;
}
//if (DBG) {
- loge("WifiStateMachine starting scan for " + config.configKey() + " with " + freqs);
+ logd("starting scan for " + config.configKey() + " with " + freqs);
//}
// Call wifi native to start the scan
if (startScanNative(
@@ -6895,7 +7908,7 @@
}
return true;
} else {
- if (DBG) loge("WifiStateMachine no channels for " + config.configKey());
+ if (DBG) logd("no channels for " + config.configKey());
return false;
}
}
@@ -6911,13 +7924,13 @@
if (config == null)
return;
if (DBG) {
- loge(dbg + " " + mTargetRoamBSSID + " config " + config.configKey()
+ logd(dbg + " " + mTargetRoamBSSID + " config " + config.configKey()
+ " config.bssid " + config.BSSID);
}
config.autoJoinBSSID = "any";
config.BSSID = "any";
if (DBG) {
- loge(dbg + " " + config.SSID
+ logd(dbg + " " + config.SSID
+ " nid=" + Integer.toString(config.networkId));
}
mWifiConfigStore.saveWifiConfigBSSID(config);
@@ -6936,7 +7949,7 @@
}
setNetworkDetailedState(DetailedState.CONNECTING);
- if (TextUtils.isEmpty(mTcpBufferSizes) == false) {
+ if (!TextUtils.isEmpty(mTcpBufferSizes)) {
mLinkProperties.setTcpBufferSizes(mTcpBufferSizes);
}
mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext,
@@ -6947,10 +7960,28 @@
// from this point on and having the BSSID specified in the network block would
// cause the roam to faile and the device to disconnect
clearCurrentConfigBSSID("L2ConnectedState");
+
+ try {
+ mIpReachabilityMonitor = new IpReachabilityMonitor(
+ mInterfaceName,
+ new IpReachabilityMonitor.Callback() {
+ @Override
+ public void notifyLost(InetAddress ip, String logMsg) {
+ sendMessage(CMD_IP_REACHABILITY_LOST, logMsg);
+ }
+ });
+ } catch (IllegalArgumentException e) {
+ Log.wtf("Failed to create IpReachabilityMonitor", e);
+ }
}
@Override
public void exit() {
+ if (mIpReachabilityMonitor != null) {
+ mIpReachabilityMonitor.stop();
+ mIpReachabilityMonitor = null;
+ }
+
// This is handled by receiving a NETWORK_DISCONNECTION_EVENT in ConnectModeState
// Bug: 15347363
// For paranoia's sake, call handleNetworkDisconnect
@@ -6979,18 +8010,19 @@
case DhcpStateMachine.CMD_POST_DHCP_ACTION:
handlePostDhcpSetup();
if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
- if (DBG) log("WifiStateMachine DHCP successful");
+ if (DBG) log("DHCP successful");
handleIPv4Success((DhcpResults) message.obj, DhcpStateMachine.DHCP_SUCCESS);
- // We advance to mVerifyingLinkState because handleIPv4Success will call
+ // We advance to mConnectedState because handleIPv4Success will call
// updateLinkProperties, which then sends CMD_IP_CONFIGURATION_SUCCESSFUL.
} else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
+ mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_DHCP_FAILURE);
if (DBG) {
int count = -1;
WifiConfiguration config = getCurrentWifiConfiguration();
if (config != null) {
count = config.numConnectionFailures;
}
- log("WifiStateMachine DHCP failure count=" + count);
+ log("DHCP failure count=" + count);
}
handleIPv4Failure(DhcpStateMachine.DHCP_FAILURE);
// As above, we transition to mDisconnectingState via updateLinkProperties.
@@ -7002,11 +8034,16 @@
transitionTo(mConnectedState);
break;
case CMD_IP_CONFIGURATION_LOST:
- // Get Link layer stats so as we get fresh tx packet counters
+ // Get Link layer stats so that we get fresh tx packet counters.
getWifiLinkLayerStats(true);
handleIpConfigurationLost();
transitionTo(mDisconnectingState);
break;
+ case CMD_IP_REACHABILITY_LOST:
+ if (DBG && message.obj != null) log((String) message.obj);
+ handleIpReachabilityLost();
+ transitionTo(mDisconnectingState);
+ break;
case CMD_DISCONNECT:
mWifiNative.disconnect();
transitionTo(mDisconnectingState);
@@ -7034,30 +8071,34 @@
deferMessage(message);
break;
case CMD_START_SCAN:
- //if (DBG) {
- loge("WifiStateMachine CMD_START_SCAN source " + message.arg1
+ if (DBG) {
+ logd("CMD_START_SCAN source " + message.arg1
+ " txSuccessRate="+String.format( "%.2f", mWifiInfo.txSuccessRate)
+ " rxSuccessRate="+String.format( "%.2f", mWifiInfo.rxSuccessRate)
+ " targetRoamBSSID=" + mTargetRoamBSSID
+ " RSSI=" + mWifiInfo.getRssi());
- //}
+ }
if (message.arg1 == SCAN_ALARM_SOURCE) {
// Check if the CMD_START_SCAN message is obsolete (and thus if it should
- // not be processed) and restart the scan if needed
- boolean shouldScan =
- mScreenOn && mWifiConfigStore.enableAutoJoinScanWhenAssociated;
+ // not be processed) and restart the scan if neede
+ if (!getEnableAutoJoinWhenAssociated()) {
+ return HANDLED;
+ }
+ boolean shouldScan = mScreenOn;
+
if (!checkAndRestartDelayedScan(message.arg2,
shouldScan,
- mWifiConfigStore.associatedPartialScanPeriodMilli, null, null)) {
+ mWifiConfigStore.wifiAssociatedShortScanIntervalMilli.get(),
+ null, null)) {
messageHandlingStatus = MESSAGE_HANDLING_STATUS_OBSOLETE;
- loge("WifiStateMachine L2Connected CMD_START_SCAN source "
+ logd("L2Connected CMD_START_SCAN source "
+ message.arg1
+ " " + message.arg2 + ", " + mDelayedScanCounter
+ " -> obsolete");
return HANDLED;
}
if (mP2pConnected.get()) {
- loge("WifiStateMachine L2Connected CMD_START_SCAN source "
+ logd("L2Connected CMD_START_SCAN source "
+ message.arg1
+ " " + message.arg2 + ", " + mDelayedScanCounter
+ " ignore because P2P is connected");
@@ -7068,17 +8109,17 @@
boolean restrictChannelList = false;
long now_ms = System.currentTimeMillis();
if (DBG) {
- loge("WifiStateMachine CMD_START_SCAN with age="
+ logd("CMD_START_SCAN with age="
+ Long.toString(now_ms - lastFullBandConnectedTimeMilli)
+ " interval=" + fullBandConnectedTimeIntervalMilli
+ " maxinterval=" + maxFullBandConnectedTimeIntervalMilli);
}
if (mWifiInfo != null) {
- if (mWifiConfigStore.enableFullBandScanWhenAssociated &&
+ if (mWifiConfigStore.enableFullBandScanWhenAssociated.get() &&
(now_ms - lastFullBandConnectedTimeMilli)
> fullBandConnectedTimeIntervalMilli) {
if (DBG) {
- loge("WifiStateMachine CMD_START_SCAN try full band scan age="
+ logd("CMD_START_SCAN try full band scan age="
+ Long.toString(now_ms - lastFullBandConnectedTimeMilli)
+ " interval=" + fullBandConnectedTimeIntervalMilli
+ " maxinterval=" + maxFullBandConnectedTimeIntervalMilli);
@@ -7092,7 +8133,7 @@
mWifiConfigStore.maxRxPacketForFullScans) {
// Too much traffic at the interface, hence no full band scan
if (DBG) {
- loge("WifiStateMachine CMD_START_SCAN " +
+ logd("CMD_START_SCAN " +
"prevent full band scan due to pkt rate");
}
tryFullBandScan = false;
@@ -7104,9 +8145,9 @@
mWifiConfigStore.maxRxPacketForPartialScans) {
// Don't scan if lots of packets are being sent
restrictChannelList = true;
- if (mWifiConfigStore.alwaysEnableScansWhileAssociated == 0) {
+ if (mWifiConfigStore.alwaysEnableScansWhileAssociated.get() == 0) {
if (DBG) {
- loge("WifiStateMachine CMD_START_SCAN source " + message.arg1
+ logd("CMD_START_SCAN source " + message.arg1
+ " ...and ignore scans"
+ " tx=" + String.format("%.2f", mWifiInfo.txSuccessRate)
+ " rx=" + String.format("%.2f", mWifiInfo.rxSuccessRate));
@@ -7119,15 +8160,15 @@
WifiConfiguration currentConfiguration = getCurrentWifiConfiguration();
if (DBG) {
- loge("WifiStateMachine CMD_START_SCAN full=" +
+ logd("CMD_START_SCAN full=" +
tryFullBandScan);
}
if (currentConfiguration != null) {
if (fullBandConnectedTimeIntervalMilli
- < mWifiConfigStore.associatedPartialScanPeriodMilli) {
+ < mWifiConfigStore.wifiAssociatedShortScanIntervalMilli.get()) {
// Sanity
fullBandConnectedTimeIntervalMilli
- = mWifiConfigStore.associatedPartialScanPeriodMilli;
+ = mWifiConfigStore.wifiAssociatedShortScanIntervalMilli.get();
}
if (tryFullBandScan) {
lastFullBandConnectedTimeMilli = now_ms;
@@ -7136,10 +8177,10 @@
// Increase the interval
fullBandConnectedTimeIntervalMilli
= fullBandConnectedTimeIntervalMilli
- * mWifiConfigStore.associatedFullScanBackoff / 8;
+ * mWifiConfigStore.associatedFullScanBackoff.get() / 8;
if (DBG) {
- loge("WifiStateMachine CMD_START_SCAN bump interval ="
+ logd("CMD_START_SCAN bump interval ="
+ fullBandConnectedTimeIntervalMilli);
}
}
@@ -7149,7 +8190,7 @@
if (!startScanForConfiguration(
currentConfiguration, restrictChannelList)) {
if (DBG) {
- loge("WifiStateMachine starting scan, " +
+ logd("starting scan, " +
" did not find channels -> full");
}
lastFullBandConnectedTimeMilli = now_ms;
@@ -7158,10 +8199,10 @@
// Increase the interval
fullBandConnectedTimeIntervalMilli
= fullBandConnectedTimeIntervalMilli
- * mWifiConfigStore.associatedFullScanBackoff / 8;
+ * mWifiConfigStore.associatedFullScanBackoff.get() / 8;
if (DBG) {
- loge("WifiStateMachine CMD_START_SCAN bump interval ="
+ logd("CMD_START_SCAN bump interval ="
+ fullBandConnectedTimeIntervalMilli);
}
}
@@ -7171,7 +8212,7 @@
}
} else {
- loge("CMD_START_SCAN : connected mode and no configuration");
+ logd("CMD_START_SCAN : connected mode and no configuration");
messageHandlingStatus = MESSAGE_HANDLING_STATUS_HANDLING_ERROR;
}
} else {
@@ -7191,7 +8232,7 @@
break;
case CMD_RSSI_POLL:
if (message.arg1 == mRssiPollToken) {
- if (mWifiConfigStore.enableChipWakeUpWhenAssociated) {
+ if (mWifiConfigStore.enableChipWakeUpWhenAssociated.get()) {
if (VVDBG) log(" get link layer stats " + mWifiLinkLayerStatsSupported);
WifiLinkLayerStats stats = getWifiLinkLayerStats(VDBG);
if (stats != null) {
@@ -7215,7 +8256,8 @@
}
break;
case CMD_ENABLE_RSSI_POLL:
- if (mWifiConfigStore.enableRssiPollWhenAssociated) {
+ cleanWifiScore();
+ if (mWifiConfigStore.enableRssiPollWhenAssociated.get()) {
mEnableRssiPolling = (message.arg1 == 1);
} else {
mEnableRssiPolling = false;
@@ -7226,8 +8268,6 @@
fetchRssiLinkSpeedAndFrequencyNative();
sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
- } else {
- cleanWifiScore();
}
break;
case WifiManager.RSSI_PKTCNT_FETCH:
@@ -7241,11 +8281,11 @@
if (!linkDebouncing && mWifiConfigStore.enableLinkDebouncing) {
// Ignore if we are not debouncing
- loge("CMD_DELAYED_NETWORK_DISCONNECT and not debouncing - ignore "
+ logd("CMD_DELAYED_NETWORK_DISCONNECT and not debouncing - ignore "
+ message.arg1);
return HANDLED;
} else {
- loge("CMD_DELAYED_NETWORK_DISCONNECT and debouncing - disconnect "
+ logd("CMD_DELAYED_NETWORK_DISCONNECT and debouncing - disconnect "
+ message.arg1);
linkDebouncing = false;
@@ -7259,11 +8299,16 @@
break;
case CMD_ASSOCIATED_BSSID:
if ((String) message.obj == null) {
- loge("Associated command w/o BSSID");
+ logw("Associated command w/o BSSID");
break;
}
mLastBssid = (String) message.obj;
- mWifiInfo.setBSSID((String) message.obj);
+ if (mLastBssid != null
+ && (mWifiInfo.getBSSID() == null
+ || !mLastBssid.equals(mWifiInfo.getBSSID()))) {
+ mWifiInfo.setBSSID((String) message.obj);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+ }
break;
default:
return NOT_HANDLED;
@@ -7319,7 +8364,7 @@
startDhcp();
}
obtainingIpWatchdogCount++;
- loge("Start Dhcp Watchdog " + obtainingIpWatchdogCount);
+ logd("Start Dhcp Watchdog " + obtainingIpWatchdogCount);
// Get Link layer stats so as we get fresh tx packet counters
getWifiLinkLayerStats(true);
sendMessageDelayed(obtainMessage(CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER,
@@ -7330,7 +8375,7 @@
StaticIpConfiguration config = mWifiConfigStore.getStaticIpConfiguration(
mLastNetworkId);
if (config.ipAddress == null) {
- loge("Static IP lacks address");
+ logd("Static IP lacks address");
sendMessage(CMD_STATIC_IP_FAILURE);
} else {
InterfaceConfiguration ifcg = new InterfaceConfiguration();
@@ -7383,7 +8428,7 @@
break;
case CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER:
if (message.arg1 == obtainingIpWatchdogCount) {
- loge("ObtainingIpAddress: Watchdog Triggered, count="
+ logd("ObtainingIpAddress: Watchdog Triggered, count="
+ obtainingIpWatchdogCount);
handleIpConfigurationLost();
transitionTo(mDisconnectingState);
@@ -7398,6 +8443,8 @@
}
}
+ // Note: currently, this state is never used, because WifiWatchdogStateMachine unconditionally
+ // sets mPoorNetworkDetectionEnabled to false.
class VerifyingLinkState extends State {
@Override
public void enter() {
@@ -7418,8 +8465,6 @@
log(getName() + " POOR_LINK_DETECTED: no transition");
break;
case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
- log(getName() + " GOOD_LINK_DETECTED: transition to captive portal check");
-
log(getName() + " GOOD_LINK_DETECTED: transition to CONNECTED");
sendConnectedState();
transitionTo(mConnectedState);
@@ -7433,16 +8478,23 @@
}
private void sendConnectedState() {
- // Send out a broadcast with the CAPTIVE_PORTAL_CHECK to preserve
- // existing behaviour. The captive portal check really happens after we
- // transition into DetailedState.CONNECTED.
- setNetworkDetailedState(DetailedState.CAPTIVE_PORTAL_CHECK);
- mWifiConfigStore.updateStatus(mLastNetworkId,
- DetailedState.CAPTIVE_PORTAL_CHECK);
- sendNetworkStateChangeBroadcast(mLastBssid);
-
- if (mWifiConfigStore.getLastSelectedConfiguration() != null) {
- if (mNetworkAgent != null) mNetworkAgent.explicitlySelected();
+ // If this network was explicitly selected by the user, evaluate whether to call
+ // explicitlySelected() so the system can treat it appropriately.
+ WifiConfiguration config = getCurrentWifiConfiguration();
+ if (mWifiConfigStore.isLastSelectedConfiguration(config)) {
+ boolean prompt = mWifiConfigStore.checkConfigOverridePermission(config.lastConnectUid);
+ if (DBG) {
+ log("Network selected by UID " + config.lastConnectUid + " prompt=" + prompt);
+ }
+ if (prompt) {
+ // Selected by the user via Settings or QuickSettings. If this network has Internet
+ // access, switch to it. Otherwise, switch to it only if the user confirms that they
+ // really want to switch, or has already confirmed and selected "Don't ask again".
+ if (DBG) {
+ log("explictlySelected acceptUnvalidated=" + config.noInternetAccessExpected);
+ }
+ mNetworkAgent.explicitlySelected(config.noInternetAccessExpected);
+ }
}
setNetworkDetailedState(DetailedState.CONNECTED);
@@ -7462,7 +8514,7 @@
// Make sure we disconnect if roaming fails
roamWatchdogCount++;
- loge("Start Roam Watchdog " + roamWatchdogCount);
+ logd("Start Roam Watchdog " + roamWatchdogCount);
sendMessageDelayed(obtainMessage(CMD_ROAM_WATCHDOG_TIMER,
roamWatchdogCount, 0), ROAM_GUARD_TIMER_MSEC);
mAssociated = false;
@@ -7475,22 +8527,23 @@
case CMD_IP_CONFIGURATION_LOST:
config = getCurrentWifiConfiguration();
if (config != null) {
+ mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_AUTOROAM_FAILURE);
mWifiConfigStore.noteRoamingFailure(config,
WifiConfiguration.ROAMING_FAILURE_IP_CONFIG);
}
return NOT_HANDLED;
- case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
+ case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
if (DBG) log("Roaming and Watchdog reports poor link -> ignore");
return HANDLED;
- case CMD_UNWANTED_NETWORK:
+ case CMD_UNWANTED_NETWORK:
if (DBG) log("Roaming and CS doesnt want the network -> ignore");
return HANDLED;
- case CMD_SET_OPERATIONAL_MODE:
+ case CMD_SET_OPERATIONAL_MODE:
if (message.arg1 != CONNECT_MODE) {
deferMessage(message);
}
break;
- case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
+ case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
/**
* If we get a SUPPLICANT_STATE_CHANGE_EVENT indicating a DISCONNECT
* before NETWORK_DISCONNECTION_EVENT
@@ -7537,6 +8590,7 @@
mWifiInfo.setBSSID(mLastBssid);
mWifiInfo.setNetworkId(mLastNetworkId);
mWifiConfigStore.handleBSSIDBlackList(mLastNetworkId, mLastBssid, true);
+ sendNetworkStateChangeBroadcast(mLastBssid);
transitionTo(mObtainingIpState);
} else {
messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
@@ -7559,13 +8613,15 @@
break;
case WifiMonitor.SSID_TEMP_DISABLED:
// Auth error while roaming
- loge("SSID_TEMP_DISABLED nid=" + Integer.toString(mLastNetworkId)
+ logd("SSID_TEMP_DISABLED nid=" + Integer.toString(mLastNetworkId)
+ " id=" + Integer.toString(message.arg1)
+ " isRoaming=" + isRoaming()
+ " roam=" + Integer.toString(mAutoRoaming));
if (message.arg1 == mLastNetworkId) {
config = getCurrentWifiConfiguration();
if (config != null) {
+ mWifiLogger.captureBugReportData(
+ WifiLogger.REPORT_REASON_AUTOROAM_FAILURE);
mWifiConfigStore.noteRoamingFailure(config,
WifiConfiguration.ROAMING_FAILURE_AUTH_FAILURE);
}
@@ -7584,7 +8640,7 @@
@Override
public void exit() {
- loge("WifiStateMachine: Leaving Roaming state");
+ logd("WifiStateMachine: Leaving Roaming state");
}
}
@@ -7594,18 +8650,26 @@
String address;
updateDefaultRouteMacAddress(1000);
if (DBG) {
- log("ConnectedState Enter "
- + " mScreenOn=" + mScreenOn
- + " scanperiod="
- + Integer.toString(mWifiConfigStore.associatedPartialScanPeriodMilli) );
+ log("Enter ConnectedState "
+ + " mScreenOn=" + mScreenOn
+ + " scanperiod="
+ + Integer.toString(mWifiConfigStore.wifiAssociatedShortScanIntervalMilli.get())
+ + " useGscan=" + mHalBasedPnoDriverSupported + "/"
+ + mWifiConfigStore.enableHalBasedPno.get()
+ + " mHalBasedPnoEnableInDevSettings " + mHalBasedPnoEnableInDevSettings);
}
if (mScreenOn
- && mWifiConfigStore.enableAutoJoinScanWhenAssociated) {
- // restart scan alarm
- startDelayedScan(mWifiConfigStore.associatedPartialScanPeriodMilli, null, null);
+ && getEnableAutoJoinWhenAssociated()) {
+ if (useHalBasedAutoJoinOffload()) {
+ startGScanConnectedModeOffload("connectedEnter");
+ } else {
+ // restart scan alarm
+ startDelayedScan(mWifiConfigStore.wifiAssociatedShortScanIntervalMilli.get(),
+ null, null);
+ }
}
registerConnected();
- lastConnectAttempt = 0;
+ lastConnectAttemptTimestamp = 0;
targetWificonfiguration = null;
// Paranoia
linkDebouncing = false;
@@ -7615,7 +8679,7 @@
if (testNetworkDisconnect) {
testNetworkDisconnectCounter++;
- loge("ConnectedState Enter start disconnect test " +
+ logd("ConnectedState Enter start disconnect test " +
testNetworkDisconnectCounter);
sendMessageDelayed(obtainMessage(CMD_TEST_NETWORK_DISCONNECT,
testNetworkDisconnectCounter, 0), 15000);
@@ -7625,6 +8689,8 @@
mWifiConfigStore.enableAllNetworks();
mLastDriverRoamAttempt = 0;
+
+ //startLazyRoam();
}
@Override
public boolean processMessage(Message message) {
@@ -7632,20 +8698,63 @@
logStateAndMessage(message, getClass().getSimpleName());
switch (message.what) {
+ case CMD_RESTART_AUTOJOIN_OFFLOAD:
+ if ( (int)message.arg2 < mRestartAutoJoinOffloadCounter ) {
+ messageHandlingStatus = MESSAGE_HANDLING_STATUS_OBSOLETE;
+ return HANDLED;
+ }
+ /* If we are still in Disconnected state after having discovered a valid
+ * network this means autojoin didnt managed to associate to the network,
+ * then restart PNO so as we will try associating to it again.
+ */
+ if (useHalBasedAutoJoinOffload()) {
+ if (mGScanStartTimeMilli == 0) {
+ // If offload is not started, then start it...
+ startGScanConnectedModeOffload("connectedRestart");
+ } else {
+ // If offload is already started, then check if we need to increase
+ // the scan period and restart the Gscan
+ long now = System.currentTimeMillis();
+ if (mGScanStartTimeMilli != 0 && now > mGScanStartTimeMilli
+ && ((now - mGScanStartTimeMilli)
+ > DISCONNECTED_SHORT_SCANS_DURATION_MILLI)
+ && (mGScanPeriodMilli
+ < mWifiConfigStore.wifiDisconnectedLongScanIntervalMilli.get()))
+ {
+ startConnectedGScan("Connected restart gscan");
+ }
+ }
+ }
+ break;
+ case CMD_UPDATE_ASSOCIATED_SCAN_PERMISSION:
+ updateAssociatedScanPermission();
+ break;
case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
if (DBG) log("Watchdog reports poor link");
transitionTo(mVerifyingLinkState);
break;
case CMD_UNWANTED_NETWORK:
- if (message.arg1 == network_status_unwanted_disconnect) {
+ if (message.arg1 == NETWORK_STATUS_UNWANTED_DISCONNECT) {
mWifiConfigStore.handleBadNetworkDisconnectReport(mLastNetworkId, mWifiInfo);
mWifiNative.disconnect();
transitionTo(mDisconnectingState);
- } else if (message.arg1 == network_status_unwanted_disable_autojoin) {
+ } else if (message.arg1 == NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN ||
+ message.arg1 == NETWORK_STATUS_UNWANTED_VALIDATION_FAILED) {
config = getCurrentWifiConfiguration();
if (config != null) {
// Disable autojoin
+ if (message.arg1 == NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN) {
+ config.validatedInternetAccess = false;
+ // Clear last-selected status, as being last-selected also avoids
+ // disabling auto-join.
+ if (mWifiConfigStore.isLastSelectedConfiguration(config)) {
+ mWifiConfigStore.setLastSelectedConfiguration(
+ WifiConfiguration.INVALID_NETWORK_ID);
+ }
+ }
config.numNoInternetAccessReports += 1;
+ config.dirty = true;
+ mWifiConfigStore.writeKnownNetworkHistory(false);
}
}
return HANDLED;
@@ -7653,12 +8762,24 @@
if (message.arg1 == NetworkAgent.VALID_NETWORK) {
config = getCurrentWifiConfiguration();
if (config != null) {
+ if (!config.validatedInternetAccess
+ || config.numNoInternetAccessReports != 0) {
+ config.dirty = true;
+ }
// re-enable autojoin
config.numNoInternetAccessReports = 0;
config.validatedInternetAccess = true;
+ mWifiConfigStore.writeKnownNetworkHistory(false);
}
}
return HANDLED;
+ case CMD_ACCEPT_UNVALIDATED:
+ boolean accept = (message.arg1 != 0);
+ config = getCurrentWifiConfiguration();
+ if (config != null) {
+ config.noInternetAccessExpected = accept;
+ }
+ return HANDLED;
case CMD_TEST_NETWORK_DISCONNECT:
// Force a disconnect
if (message.arg1 == testNetworkDisconnectCounter) {
@@ -7681,6 +8802,10 @@
lastRoam = System.currentTimeMillis() - mLastDriverRoamAttempt;
mLastDriverRoamAttempt = 0;
}
+ if (unexpectedDisconnectedReason(message.arg2)) {
+ mWifiLogger.captureBugReportData(
+ WifiLogger.REPORT_REASON_UNEXPECTED_DISCONNECT);
+ }
config = getCurrentWifiConfiguration();
if (mScreenOn
&& !linkDebouncing
@@ -7730,7 +8855,7 @@
}
break;
case CMD_AUTO_ROAM:
- // Clear the driver roam indication since we are attempting a framerwork roam
+ // Clear the driver roam indication since we are attempting a framework roam
mLastDriverRoamAttempt = 0;
/* Connect command coming from auto-join */
@@ -7749,7 +8874,7 @@
break;
}
- loge("CMD_AUTO_ROAM sup state "
+ logd("CMD_AUTO_ROAM sup state "
+ mSupplicantStateTracker.getSupplicantStateName()
+ " my state " + getCurrentState().getName()
+ " nid=" + Integer.toString(netId)
@@ -7760,26 +8885,35 @@
/* Save the BSSID so as to lock it @ firmware */
if (!autoRoamSetBSSID(config, bssid) && !linkDebouncing) {
- loge("AUTO_ROAM nothing to do");
+ logd("AUTO_ROAM nothing to do");
// Same BSSID, nothing to do
messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
break;
};
- // Make sure the network is enabled, since supplicant will not reenable it
+ // Make sure the network is enabled, since supplicant will not re-enable it
mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
+ if (deferForUserInput(message, netId, false)) {
+ break;
+ } else if (mWifiConfigStore.getWifiConfiguration(netId).userApproved ==
+ WifiConfiguration.USER_BANNED) {
+ replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
+ WifiManager.NOT_AUTHORIZED);
+ break;
+ }
+
boolean ret = false;
if (mLastNetworkId != netId) {
- if (mWifiConfigStore.selectNetwork(netId) &&
- mWifiNative.reconnect()) {
+ if (mWifiConfigStore.selectNetwork(config, /* updatePriorities = */ false,
+ WifiConfiguration.UNKNOWN_UID) && mWifiNative.reconnect()) {
ret = true;
}
} else {
ret = mWifiNative.reassociate();
}
if (ret) {
- lastConnectAttempt = System.currentTimeMillis();
+ lastConnectAttemptTimestamp = System.currentTimeMillis();
targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
// replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
@@ -7802,9 +8936,13 @@
@Override
public void exit() {
- loge("WifiStateMachine: Leaving Connected state");
+ logd("WifiStateMachine: Leaving Connected state");
setScanAlarm(false);
mLastDriverRoamAttempt = 0;
+
+ stopLazyRoam();
+
+ mWhiteListedSsids = null;
}
}
@@ -7814,19 +8952,20 @@
public void enter() {
if (PDBG) {
- loge(" Enter DisconnectingState State scan interval " + mFrameworkScanIntervalMs
- + " mEnableBackgroundScan= " + mEnableBackgroundScan
+ logd(" Enter DisconnectingState State scan interval "
+ + mWifiConfigStore.wifiDisconnectedShortScanIntervalMilli.get()
+ + " mLegacyPnoEnabled= " + mLegacyPnoEnabled
+ " screenOn=" + mScreenOn);
}
- // Make sure we disconnect: we enter this state prior connecting to a new
- // network, waiting for either a DISCONECT event or a SUPPLICANT_STATE_CHANGE
+ // Make sure we disconnect: we enter this state prior to connecting to a new
+ // network, waiting for either a DISCONNECT event or a SUPPLICANT_STATE_CHANGE
// event which in this case will be indicating that supplicant started to associate.
// In some cases supplicant doesn't ignore the connect requests (it might not
// find the target SSID in its cache),
// Therefore we end up stuck that state, hence the need for the watchdog.
disconnectingWatchdogCount++;
- loge("Start Disconnecting Watchdog " + disconnectingWatchdogCount);
+ logd("Start Disconnecting Watchdog " + disconnectingWatchdogCount);
sendMessageDelayed(obtainMessage(CMD_DISCONNECTING_WATCHDOG_TIMER,
disconnectingWatchdogCount, 0), DISCONNECTING_GUARD_TIMER_MSEC);
}
@@ -7877,41 +9016,43 @@
return;
}
- mFrameworkScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
- Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
- mDefaultFrameworkScanIntervalMs);
-
if (PDBG) {
- loge(" Enter disconnected State scan interval " + mFrameworkScanIntervalMs
- + " mEnableBackgroundScan= " + mEnableBackgroundScan
+ logd(" Enter DisconnectedState scan interval "
+ + mWifiConfigStore.wifiDisconnectedShortScanIntervalMilli.get()
+ + " mLegacyPnoEnabled= " + mLegacyPnoEnabled
+ " screenOn=" + mScreenOn
- + " mFrameworkScanIntervalMs=" + mFrameworkScanIntervalMs);
+ + " useGscan=" + mHalBasedPnoDriverSupported + "/"
+ + mWifiConfigStore.enableHalBasedPno.get());
}
/** clear the roaming state, if we were roaming, we failed */
mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
- if (mScreenOn) {
- /**
- * screen lit and => delayed timer
- */
- startDelayedScan(mDisconnectedScanPeriodMs, null, null);
+ if (useHalBasedAutoJoinOffload()) {
+ startGScanDisconnectedModeOffload("disconnectedEnter");
} else {
- /**
- * screen dark and PNO supported => scan alarm disabled
- */
- if (mEnableBackgroundScan) {
- /* If a regular scan result is pending, do not initiate background
- * scan until the scan results are returned. This is needed because
- * initiating a background scan will cancel the regular scan and
- * scan results will not be returned until background scanning is
- * cleared
+ if (mScreenOn) {
+ /**
+ * screen lit and => delayed timer
*/
- if (!mIsScanOngoing) {
- enableBackgroundScan(true);
- }
+ startDelayedScan(500, null, null);
} else {
- setScanAlarm(true);
+ /**
+ * screen dark and PNO supported => scan alarm disabled
+ */
+ if (mBackgroundScanSupported) {
+ /* If a regular scan result is pending, do not initiate background
+ * scan until the scan results are returned. This is needed because
+ * initiating a background scan will cancel the regular scan and
+ * scan results will not be returned until background scanning is
+ * cleared
+ */
+ if (!mIsScanOngoing) {
+ enableBackgroundScan(true);
+ }
+ } else {
+ setScanAlarm(true);
+ }
}
}
@@ -7920,13 +9061,14 @@
* The scans are useful to notify the user of the presence of an open network.
* Note that these are not wake up scans.
*/
- if (!mP2pConnected.get() && mWifiConfigStore.getConfiguredNetworks().size() == 0) {
+ if (mNoNetworksPeriodicScan != 0 && !mP2pConnected.get()
+ && mWifiConfigStore.getConfiguredNetworks().size() == 0) {
sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
- ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
+ ++mPeriodicScanToken, 0), mNoNetworksPeriodicScan);
}
mDisconnectedTimeStamp = System.currentTimeMillis();
-
+ mDisconnectedPnoAlarmCount = 0;
}
@Override
public boolean processMessage(Message message) {
@@ -7937,20 +9079,22 @@
switch (message.what) {
case CMD_NO_NETWORKS_PERIODIC_SCAN:
if (mP2pConnected.get()) break;
- if (message.arg1 == mPeriodicScanToken &&
+ if (mNoNetworksPeriodicScan != 0 && message.arg1 == mPeriodicScanToken &&
mWifiConfigStore.getConfiguredNetworks().size() == 0) {
startScan(UNKNOWN_SCAN_SOURCE, -1, null, null);
sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
- ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
+ ++mPeriodicScanToken, 0), mNoNetworksPeriodicScan);
}
break;
case WifiManager.FORGET_NETWORK:
case CMD_REMOVE_NETWORK:
+ case CMD_REMOVE_APP_CONFIGURATIONS:
+ case CMD_REMOVE_USER_CONFIGURATIONS:
// Set up a delayed message here. After the forget/remove is handled
// the handled delayed message will determine if there is a need to
// scan and continue
sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
- ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
+ ++mPeriodicScanToken, 0), mNoNetworksPeriodicScan);
ret = NOT_HANDLED;
break;
case CMD_SET_OPERATIONAL_MODE:
@@ -7973,7 +9117,7 @@
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
if (DBG) {
- loge("SUPPLICANT_STATE_CHANGE_EVENT state=" + stateChangeResult.state +
+ logd("SUPPLICANT_STATE_CHANGE_EVENT state=" + stateChangeResult.state +
" -> state= " + WifiInfo.getDetailedStateOf(stateChangeResult.state)
+ " debouncing=" + linkDebouncing);
}
@@ -7987,38 +9131,113 @@
messageHandlingStatus = MESSAGE_HANDLING_STATUS_REFUSED;
return HANDLED;
}
- /* Disable background scan temporarily during a regular scan */
- if (mEnableBackgroundScan) {
- enableBackgroundScan(false);
- }
if (message.arg1 == SCAN_ALARM_SOURCE) {
// Check if the CMD_START_SCAN message is obsolete (and thus if it should
// not be processed) and restart the scan
- int period = mDisconnectedScanPeriodMs;
+ int period = mWifiConfigStore.wifiDisconnectedShortScanIntervalMilli.get();
if (mP2pConnected.get()) {
period = (int)Settings.Global.getLong(mContext.getContentResolver(),
Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
- mDisconnectedScanPeriodMs);
+ period);
}
if (!checkAndRestartDelayedScan(message.arg2,
true, period, null, null)) {
messageHandlingStatus = MESSAGE_HANDLING_STATUS_OBSOLETE;
- loge("WifiStateMachine Disconnected CMD_START_SCAN source "
+ logd("Disconnected CMD_START_SCAN source "
+ message.arg1
+ " " + message.arg2 + ", " + mDelayedScanCounter
+ " -> obsolete");
return HANDLED;
}
+ /* Disable background scan temporarily during a regular scan */
+ enableBackgroundScan(false);
handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
ret = HANDLED;
} else {
+
+ /*
+ * The SCAN request is not handled in this state and
+ * would eventually might/will get handled in the
+ * parent's state. The PNO, if already enabled had to
+ * get disabled before the SCAN trigger. Hence, stop
+ * the PNO if already enabled in this state, though the
+ * SCAN request is not handled(PNO disable before the
+ * SCAN trigger in any other state is not the right
+ * place to issue).
+ */
+
+ enableBackgroundScan(false);
ret = NOT_HANDLED;
}
break;
+ case CMD_RESTART_AUTOJOIN_OFFLOAD:
+ if ( (int)message.arg2 < mRestartAutoJoinOffloadCounter ) {
+ messageHandlingStatus = MESSAGE_HANDLING_STATUS_OBSOLETE;
+ return HANDLED;
+ }
+ /* If we are still in Disconnected state after having discovered a valid
+ * network this means autojoin didnt managed to associate to the network,
+ * then restart PNO so as we will try associating to it again.
+ */
+ if (useHalBasedAutoJoinOffload()) {
+ if (mGScanStartTimeMilli == 0) {
+ // If offload is not started, then start it...
+ startGScanDisconnectedModeOffload("disconnectedRestart");
+ } else {
+ // If offload is already started, then check if we need to increase
+ // the scan period and restart the Gscan
+ long now = System.currentTimeMillis();
+ if (mGScanStartTimeMilli != 0 && now > mGScanStartTimeMilli
+ && ((now - mGScanStartTimeMilli)
+ > DISCONNECTED_SHORT_SCANS_DURATION_MILLI)
+ && (mGScanPeriodMilli
+ < mWifiConfigStore.wifiDisconnectedLongScanIntervalMilli.get()))
+ {
+ startDisconnectedGScan("disconnected restart gscan");
+ }
+ }
+ } else {
+ // If we are still disconnected for a short while after having found a
+ // network thru PNO, then something went wrong, and for some reason we
+ // couldn't join this network.
+ // It might be due to a SW bug in supplicant or the wifi stack, or an
+ // interoperability issue, or we try to join a bad bss and failed
+ // In that case we want to restart pno so as to make sure that we will
+ // attempt again to join that network.
+ if (!mScreenOn && !mIsScanOngoing && mBackgroundScanSupported) {
+ enableBackgroundScan(false);
+ enableBackgroundScan(true);
+ }
+ return HANDLED;
+ }
+ break;
case WifiMonitor.SCAN_RESULTS_EVENT:
+ case WifiMonitor.SCAN_FAILED_EVENT:
/* Re-enable background scan when a pending scan result is received */
- if (mEnableBackgroundScan && mIsScanOngoing) {
+ if (!mScreenOn && mIsScanOngoing
+ && mBackgroundScanSupported
+ && !useHalBasedAutoJoinOffload()) {
enableBackgroundScan(true);
+ } else if (!mScreenOn
+ && !mIsScanOngoing
+ && mBackgroundScanSupported
+ && !useHalBasedAutoJoinOffload()) {
+ // We receive scan results from legacy PNO, hence restart the PNO alarm
+ int delay;
+ if (mDisconnectedPnoAlarmCount < 1) {
+ delay = 30 * 1000;
+ } else if (mDisconnectedPnoAlarmCount < 3) {
+ delay = 60 * 1000;
+ } else {
+ delay = 360 * 1000;
+ }
+ mDisconnectedPnoAlarmCount++;
+ if (VDBG) {
+ logd("Starting PNO alarm " + delay);
+ }
+ mAlarmManager.set(AlarmManager.RTC_WAKEUP,
+ System.currentTimeMillis() + delay,
+ mPnoIntent);
}
/* Handled in parent state */
ret = NOT_HANDLED;
@@ -8036,14 +9255,21 @@
} else if (mWifiConfigStore.getConfiguredNetworks().size() == 0) {
if (DBG) log("Turn on scanning after p2p disconnected");
sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
- ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
+ ++mPeriodicScanToken, 0), mNoNetworksPeriodicScan);
} else {
// If P2P is not connected and there are saved networks, then restart
// scanning at the normal period. This is necessary because scanning might
// have been disabled altogether if WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS
// was set to zero.
- startDelayedScan(mDisconnectedScanPeriodMs, null, null);
+ if (useHalBasedAutoJoinOffload()) {
+ startGScanDisconnectedModeOffload("p2pRestart");
+ } else {
+ startDelayedScan(
+ mWifiConfigStore.wifiDisconnectedShortScanIntervalMilli.get(),
+ null, null);
+ }
}
+ break;
case CMD_RECONNECT:
case CMD_REASSOCIATE:
if (mTemporarilyDisconnectWifi) {
@@ -8056,8 +9282,7 @@
}
break;
case CMD_SCREEN_STATE_CHANGED:
- handleScreenStateChanged(message.arg1 != 0,
- /* startBackgroundScanIfNeeded = */ true);
+ handleScreenStateChanged(message.arg1 != 0);
break;
default:
ret = NOT_HANDLED;
@@ -8067,11 +9292,11 @@
@Override
public void exit() {
+ mDisconnectedPnoAlarmCount = 0;
/* No need for a background scan upon exit from a disconnected state */
- if (mEnableBackgroundScan) {
- enableBackgroundScan(false);
- }
+ enableBackgroundScan(false);
setScanAlarm(false);
+ mAlarmManager.cancel(mPnoIntent);
}
}
@@ -8143,6 +9368,7 @@
case CMD_ENABLE_NETWORK:
case CMD_RECONNECT:
case CMD_REASSOCIATE:
+ case CMD_ENABLE_ALL_NETWORKS:
deferMessage(message);
break;
case CMD_AUTO_CONNECT:
@@ -8226,15 +9452,15 @@
startSoftApWithConfig(config);
} else {
loge("Softap config is null!");
- sendMessage(CMD_START_AP_FAILURE);
+ sendMessage(CMD_START_AP_FAILURE, WifiManager.SAP_START_FAILURE_GENERAL);
}
break;
case CMD_START_AP_SUCCESS:
- setWifiApState(WIFI_AP_STATE_ENABLED);
+ setWifiApState(WIFI_AP_STATE_ENABLED, 0);
transitionTo(mSoftApStartedState);
break;
case CMD_START_AP_FAILURE:
- setWifiApState(WIFI_AP_STATE_FAILED);
+ setWifiApState(WIFI_AP_STATE_FAILED, message.arg1);
transitionTo(mInitialState);
break;
default:
@@ -8258,7 +9484,7 @@
} catch(Exception e) {
loge("Exception in stopAccessPoint()");
}
- setWifiApState(WIFI_AP_STATE_DISABLED);
+ setWifiApState(WIFI_AP_STATE_DISABLED, 0);
transitionTo(mInitialState);
break;
case CMD_START_AP:
@@ -8344,7 +9570,7 @@
return HANDLED;
case CMD_STOP_AP:
if (DBG) log("Untethering before stopping AP");
- setWifiApState(WIFI_AP_STATE_DISABLING);
+ setWifiApState(WIFI_AP_STATE_DISABLING, 0);
stopTethering();
transitionTo(mUntetheringState);
// More work to do after untethering
@@ -8404,27 +9630,26 @@
}
}
- //State machine initiated requests can have replyTo set to null indicating
- //there are no recepients, we ignore those reply actions
+ /**
+ * State machine initiated requests can have replyTo set to null indicating
+ * there are no recepients, we ignore those reply actions.
+ */
private void replyToMessage(Message msg, int what) {
if (msg.replyTo == null) return;
- Message dstMsg = obtainMessageWithArg2(msg);
- dstMsg.what = what;
+ Message dstMsg = obtainMessageWithWhatAndArg2(msg, what);
mReplyChannel.replyToMessage(msg, dstMsg);
}
private void replyToMessage(Message msg, int what, int arg1) {
if (msg.replyTo == null) return;
- Message dstMsg = obtainMessageWithArg2(msg);
- dstMsg.what = what;
+ Message dstMsg = obtainMessageWithWhatAndArg2(msg, what);
dstMsg.arg1 = arg1;
mReplyChannel.replyToMessage(msg, dstMsg);
}
private void replyToMessage(Message msg, int what, Object obj) {
if (msg.replyTo == null) return;
- Message dstMsg = obtainMessageWithArg2(msg);
- dstMsg.what = what;
+ Message dstMsg = obtainMessageWithWhatAndArg2(msg, what);
dstMsg.obj = obj;
mReplyChannel.replyToMessage(msg, dstMsg);
}
@@ -8432,15 +9657,31 @@
/**
* arg2 on the source message has a unique id that needs to be retained in replies
* to match the request
-
- * see WifiManager for details
+ * <p>see WifiManager for details
*/
- private Message obtainMessageWithArg2(Message srcMsg) {
+ private Message obtainMessageWithWhatAndArg2(Message srcMsg, int what) {
Message msg = Message.obtain();
+ msg.what = what;
msg.arg2 = srcMsg.arg2;
return msg;
}
+ /**
+ * @param wifiCredentialEventType WIFI_CREDENTIAL_SAVED or WIFI_CREDENTIAL_FORGOT
+ * @param msg Must have a WifiConfiguration obj to succeed
+ */
+ private void broadcastWifiCredentialChanged(int wifiCredentialEventType,
+ WifiConfiguration config) {
+ if (config != null && config.preSharedKey != null) {
+ Intent intent = new Intent(WifiManager.WIFI_CREDENTIAL_CHANGED_ACTION);
+ intent.putExtra(WifiManager.EXTRA_WIFI_CREDENTIAL_SSID, config.SSID);
+ intent.putExtra(WifiManager.EXTRA_WIFI_CREDENTIAL_EVENT_TYPE,
+ wifiCredentialEventType);
+ mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT,
+ android.Manifest.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE);
+ }
+ }
+
private static int parseHex(char ch) {
if ('0' <= ch && ch <= '9') {
return ch - '0';
@@ -8490,7 +9731,6 @@
return sb.toString();
}
-
private static byte[] concat(byte[] array1, byte[] array2, byte[] array3) {
int len = array1.length + array2.length + array3.length;
@@ -8539,6 +9779,30 @@
return result;
}
+ private static byte[] concatHex(byte[] array1, byte[] array2) {
+
+ int len = array1.length + array2.length;
+
+ byte[] result = new byte[len];
+
+ int index = 0;
+ if (array1.length != 0) {
+ for (byte b : array1) {
+ result[index] = b;
+ index++;
+ }
+ }
+
+ if (array2.length != 0) {
+ for (byte b : array2) {
+ result[index] = b;
+ index++;
+ }
+ }
+
+ return result;
+ }
+
void handleGsmAuthRequest(SimAuthRequestData requestData) {
if (targetWificonfiguration == null
|| targetWificonfiguration.networkId == requestData.networkId) {
@@ -8553,8 +9817,10 @@
if (tm != null) {
StringBuilder sb = new StringBuilder();
- for (String challenge : requestData.challenges) {
+ for (String challenge : requestData.data) {
+ if (challenge == null || challenge.isEmpty())
+ continue;
logd("RAND = " + challenge);
byte[] rand = null;
@@ -8568,11 +9834,18 @@
String base64Challenge = android.util.Base64.encodeToString(
rand, android.util.Base64.NO_WRAP);
/*
- * appType = 1 => SIM, 2 => USIM according to
+ * First, try with appType = 2 => USIM according to
* com.android.internal.telephony.PhoneConstants#APPTYPE_xxx
*/
int appType = 2;
String tmResponse = tm.getIccSimChallengeResponse(appType, base64Challenge);
+ if (tmResponse == null) {
+ /* Then, in case of failure, issue may be due to sim type, retry as a simple sim
+ * appType = 1 => SIM
+ */
+ appType = 1;
+ tmResponse = tm.getIccSimChallengeResponse(appType, base64Challenge);
+ }
logv("Raw Response - " + tmResponse);
if (tmResponse != null && tmResponse.length() > 4) {
@@ -8593,13 +9866,104 @@
String response = sb.toString();
logv("Supplicant Response -" + response);
- mWifiNative.simAuthResponse(requestData.networkId, response);
+ mWifiNative.simAuthResponse(requestData.networkId, "GSM-AUTH", response);
} else {
loge("could not get telephony manager");
}
}
void handle3GAuthRequest(SimAuthRequestData requestData) {
+ StringBuilder sb = new StringBuilder();
+ byte[] rand = null;
+ byte[] authn = null;
+ String res_type = "UMTS-AUTH";
+ if (targetWificonfiguration == null
+ || targetWificonfiguration.networkId == requestData.networkId) {
+ logd("id matches targetWifiConfiguration");
+ } else {
+ logd("id does not match targetWifiConfiguration");
+ return;
+ }
+ if (requestData.data.length == 2) {
+ try {
+ rand = parseHex(requestData.data[0]);
+ authn = parseHex(requestData.data[1]);
+ } catch (NumberFormatException e) {
+ loge("malformed challenge");
+ }
+ } else {
+ loge("malformed challenge");
+ }
+
+ String tmResponse = "";
+ if (rand != null && authn != null) {
+ String base64Challenge = android.util.Base64.encodeToString(
+ concatHex(rand,authn), android.util.Base64.NO_WRAP);
+
+ TelephonyManager tm = (TelephonyManager)
+ mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ if (tm != null) {
+ int appType = 2; // 2 => USIM
+ tmResponse = tm.getIccSimChallengeResponse(appType, base64Challenge);
+ logv("Raw Response - " + tmResponse);
+ } else {
+ loge("could not get telephony manager");
+ }
+ }
+
+ if (tmResponse != null && tmResponse.length() > 4) {
+ byte[] result = android.util.Base64.decode(tmResponse,
+ android.util.Base64.DEFAULT);
+ loge("Hex Response - " + makeHex(result));
+ byte tag = result[0];
+ if (tag == (byte) 0xdb) {
+ logv("successful 3G authentication ");
+ int res_len = result[1];
+ String res = makeHex(result, 2, res_len);
+ int ck_len = result[res_len + 2];
+ String ck = makeHex(result, res_len + 3, ck_len);
+ int ik_len = result[res_len + ck_len + 3];
+ String ik = makeHex(result, res_len + ck_len + 4, ik_len);
+ sb.append(":" + ik + ":" + ck + ":" + res);
+ logv("ik:" + ik + "ck:" + ck + " res:" + res);
+ } else if (tag == (byte) 0xdc) {
+ loge("synchronisation failure");
+ int auts_len = result[1];
+ String auts = makeHex(result, 2, auts_len);
+ res_type = "UMTS-AUTS";
+ sb.append(":" + auts);
+ logv("auts:" + auts);
+ } else {
+ loge("bad response - unknown tag = " + tag);
+ return;
+ }
+ } else {
+ loge("bad response - " + tmResponse);
+ return;
+ }
+
+ String response = sb.toString();
+ logv("Supplicant Response -" + response);
+ mWifiNative.simAuthResponse(requestData.networkId, res_type, response);
+ }
+
+ /**
+ * @param reason reason code from supplicant on network disconnected event
+ * @return true if this is a suspicious disconnect
+ */
+ static boolean unexpectedDisconnectedReason(int reason) {
+ return reason == 2 // PREV_AUTH_NOT_VALID
+ || reason == 6 // CLASS2_FRAME_FROM_NONAUTH_STA
+ || reason == 7 // FRAME_FROM_NONASSOC_STA
+ || reason == 8 // STA_HAS_LEFT
+ || reason == 9 // STA_REQ_ASSOC_WITHOUT_AUTH
+ || reason == 14 // MICHAEL_MIC_FAILURE
+ || reason == 15 // 4WAY_HANDSHAKE_TIMEOUT
+ || reason == 16 // GROUP_KEY_UPDATE_TIMEOUT
+ || reason == 18 // GROUP_CIPHER_NOT_VALID
+ || reason == 19 // PAIRWISE_CIPHER_NOT_VALID
+ || reason == 23 // IEEE_802_1X_AUTH_FAILED
+ || reason == 34; // DISASSOC_LOW_ACK
}
}
diff --git a/service/java/com/android/server/wifi/anqp/ANQPElement.java b/service/java/com/android/server/wifi/anqp/ANQPElement.java
new file mode 100644
index 0000000..aec9537
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/ANQPElement.java
@@ -0,0 +1,16 @@
+package com.android.server.wifi.anqp;
+
+/**
+ * Base class for an IEEE802.11u ANQP element.
+ */
+public abstract class ANQPElement {
+ private final Constants.ANQPElementType mID;
+
+ protected ANQPElement(Constants.ANQPElementType id) {
+ mID = id;
+ }
+
+ public Constants.ANQPElementType getID() {
+ return mID;
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/ANQPFactory.java b/service/java/com/android/server/wifi/anqp/ANQPFactory.java
new file mode 100644
index 0000000..deca9c6
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/ANQPFactory.java
@@ -0,0 +1,256 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
+
+/**
+ * Factory to build a collection of 802.11u ANQP elements from a byte buffer.
+ */
+public class ANQPFactory {
+
+ private static final Constants.ANQPElementType[] BaseANQPSet = new Constants.ANQPElementType[]{
+ Constants.ANQPElementType.ANQPVenueName,
+ Constants.ANQPElementType.ANQPNwkAuthType,
+ Constants.ANQPElementType.ANQPRoamingConsortium,
+ Constants.ANQPElementType.ANQPIPAddrAvailability,
+ Constants.ANQPElementType.ANQPNAIRealm,
+ Constants.ANQPElementType.ANQP3GPPNetwork,
+ Constants.ANQPElementType.ANQPDomName
+ };
+
+ private static final Constants.ANQPElementType[] HS20ANQPSet = new Constants.ANQPElementType[]{
+ Constants.ANQPElementType.HSFriendlyName,
+ Constants.ANQPElementType.HSWANMetrics,
+ Constants.ANQPElementType.HSConnCapability
+ };
+
+ public static Constants.ANQPElementType[] getBaseANQPSet() {
+ return BaseANQPSet;
+ }
+
+ public static Constants.ANQPElementType[] getHS20ANQPSet() {
+ return HS20ANQPSet;
+ }
+
+ public static ByteBuffer buildQueryRequest(Set<Constants.ANQPElementType> elements,
+ ByteBuffer target) {
+ List<Constants.ANQPElementType> list = new ArrayList<Constants.ANQPElementType>(elements);
+ Collections.sort(list);
+
+ ListIterator<Constants.ANQPElementType> elementIterator = list.listIterator();
+
+ target.order(ByteOrder.LITTLE_ENDIAN);
+ target.putShort((short) Constants.ANQP_QUERY_LIST);
+ int lenPos = target.position();
+ target.putShort((short) 0);
+
+ while (elementIterator.hasNext()) {
+ Integer id = Constants.getANQPElementID(elementIterator.next());
+ if (id != null) {
+ target.putShort(id.shortValue());
+ } else {
+ elementIterator.previous();
+ break;
+ }
+ }
+ target.putShort(lenPos, (short) (target.position() - lenPos - Constants.BYTES_IN_SHORT));
+
+ // Start a new vendor specific element for HS2.0 elements:
+ if (elementIterator.hasNext()) {
+ target.putShort((short) Constants.ANQP_VENDOR_SPEC);
+ int vsLenPos = target.position();
+ target.putShort((short) 0);
+
+ target.putInt(Constants.HS20_PREFIX);
+ target.put((byte) Constants.HS_QUERY_LIST);
+ target.put((byte) 0);
+
+ while (elementIterator.hasNext()) {
+ Constants.ANQPElementType elementType = elementIterator.next();
+ Integer id = Constants.getHS20ElementID(elementType);
+ if (id == null) {
+ throw new RuntimeException("Unmapped ANQPElementType: " + elementType);
+ } else {
+ target.put(id.byteValue());
+ }
+ }
+ target.putShort(vsLenPos,
+ (short) (target.position() - vsLenPos - Constants.BYTES_IN_SHORT));
+ }
+
+ target.flip();
+ return target;
+ }
+
+ public static ByteBuffer buildHomeRealmRequest(List<String> realmNames, ByteBuffer target) {
+ target.order(ByteOrder.LITTLE_ENDIAN);
+ target.putShort((short) Constants.ANQP_VENDOR_SPEC);
+ int lenPos = target.position();
+ target.putShort((short) 0);
+
+ target.putInt(Constants.HS20_PREFIX);
+ target.put((byte) Constants.HS_NAI_HOME_REALM_QUERY);
+ target.put((byte) 0);
+
+ target.put((byte) realmNames.size());
+ for (String realmName : realmNames) {
+ target.put((byte) Constants.UTF8_INDICATOR);
+ byte[] octets = realmName.getBytes(StandardCharsets.UTF_8);
+ target.put((byte) octets.length);
+ target.put(octets);
+ }
+ target.putShort(lenPos, (short) (target.position() - lenPos - Constants.BYTES_IN_SHORT));
+
+ target.flip();
+ return target;
+ }
+
+ public static ByteBuffer buildIconRequest(String fileName, ByteBuffer target) {
+ target.order(ByteOrder.LITTLE_ENDIAN);
+ target.putShort((short) Constants.ANQP_VENDOR_SPEC);
+ int lenPos = target.position();
+ target.putShort((short) 0);
+
+ target.putInt(Constants.HS20_PREFIX);
+ target.put((byte) Constants.HS_ICON_REQUEST);
+ target.put((byte) 0);
+
+ target.put(fileName.getBytes(StandardCharsets.UTF_8));
+ target.putShort(lenPos, (short) (target.position() - lenPos - Constants.BYTES_IN_SHORT));
+
+ target.flip();
+ return target;
+ }
+
+ public static List<ANQPElement> parsePayload(ByteBuffer payload) throws ProtocolException {
+ payload.order(ByteOrder.LITTLE_ENDIAN);
+ List<ANQPElement> elements = new ArrayList<ANQPElement>();
+ while (payload.hasRemaining()) {
+ elements.add(buildElement(payload));
+ }
+ return elements;
+ }
+
+ private static ANQPElement buildElement(ByteBuffer payload) throws ProtocolException {
+ if (payload.remaining() < 4)
+ throw new ProtocolException("Runt payload: " + payload.remaining());
+
+ int infoIDNumber = payload.getShort() & Constants.SHORT_MASK;
+ Constants.ANQPElementType infoID = Constants.mapANQPElement(infoIDNumber);
+ if (infoID == null) {
+ throw new ProtocolException("Bad info ID: " + infoIDNumber);
+ }
+ int length = payload.getShort() & Constants.SHORT_MASK;
+
+ if (payload.remaining() < length) {
+ throw new ProtocolException("Truncated payload: " +
+ payload.remaining() + " vs " + length);
+ }
+ return buildElement(payload, infoID, length);
+ }
+
+ public static ANQPElement buildElement(ByteBuffer payload, Constants.ANQPElementType infoID,
+ int length) throws ProtocolException {
+ try {
+ ByteBuffer elementPayload = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN);
+ payload.position(payload.position() + length);
+ elementPayload.limit(elementPayload.position() + length);
+
+ switch (infoID) {
+ case ANQPCapabilityList:
+ return new CapabilityListElement(infoID, elementPayload);
+ case ANQPVenueName:
+ return new VenueNameElement(infoID, elementPayload);
+ case ANQPEmergencyNumber:
+ return new EmergencyNumberElement(infoID, elementPayload);
+ case ANQPNwkAuthType:
+ return new NetworkAuthenticationTypeElement(infoID, elementPayload);
+ case ANQPRoamingConsortium:
+ return new RoamingConsortiumElement(infoID, elementPayload);
+ case ANQPIPAddrAvailability:
+ return new IPAddressTypeAvailabilityElement(infoID, elementPayload);
+ case ANQPNAIRealm:
+ return new NAIRealmElement(infoID, elementPayload);
+ case ANQP3GPPNetwork:
+ return new ThreeGPPNetworkElement(infoID, elementPayload);
+ case ANQPGeoLoc:
+ return new GEOLocationElement(infoID, elementPayload);
+ case ANQPCivicLoc:
+ return new CivicLocationElement(infoID, elementPayload);
+ case ANQPLocURI:
+ return new GenericStringElement(infoID, elementPayload);
+ case ANQPDomName:
+ return new DomainNameElement(infoID, elementPayload);
+ case ANQPEmergencyAlert:
+ return new GenericStringElement(infoID, elementPayload);
+ case ANQPTDLSCap:
+ return new GenericBlobElement(infoID, elementPayload);
+ case ANQPEmergencyNAI:
+ return new GenericStringElement(infoID, elementPayload);
+ case ANQPNeighborReport:
+ return new GenericBlobElement(infoID, elementPayload);
+ case ANQPVendorSpec:
+ if (elementPayload.remaining() > 5) {
+ int oi = elementPayload.getInt();
+ if (oi != Constants.HS20_PREFIX) {
+ return null;
+ }
+ int subType = elementPayload.get() & Constants.BYTE_MASK;
+ Constants.ANQPElementType hs20ID = Constants.mapHS20Element(subType);
+ if (hs20ID == null) {
+ throw new ProtocolException("Bad HS20 info ID: " + subType);
+ }
+ elementPayload.get(); // Skip the reserved octet
+ return buildHS20Element(hs20ID, elementPayload);
+ } else {
+ return new GenericBlobElement(infoID, elementPayload);
+ }
+ default:
+ throw new ProtocolException("Unknown element ID: " + infoID);
+ }
+ } catch (ProtocolException e) {
+ throw e;
+ } catch (Exception e) {
+ // TODO: remove this catch-all for exceptions, once the element parsing code
+ // has been thoroughly unit tested. b/30562650
+ throw new ProtocolException("Unknown parsing error", e);
+ }
+ }
+
+ public static ANQPElement buildHS20Element(Constants.ANQPElementType infoID,
+ ByteBuffer payload) throws ProtocolException {
+ try {
+ switch (infoID) {
+ case HSCapabilityList:
+ return new HSCapabilityListElement(infoID, payload);
+ case HSFriendlyName:
+ return new HSFriendlyNameElement(infoID, payload);
+ case HSWANMetrics:
+ return new HSWanMetricsElement(infoID, payload);
+ case HSConnCapability:
+ return new HSConnectionCapabilityElement(infoID, payload);
+ case HSOperatingclass:
+ return new GenericBlobElement(infoID, payload);
+ case HSOSUProviders:
+ return new HSOsuProvidersElement(infoID, payload);
+ case HSIconFile:
+ return new HSIconFileElement(infoID, payload);
+ default:
+ return null;
+ }
+ } catch (ProtocolException e) {
+ throw e;
+ } catch (Exception e) {
+ // TODO: remove this catch-all for exceptions, once the element parsing code
+ // has been thoroughly unit tested. b/30562650
+ throw new ProtocolException("Unknown parsing error", e);
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/CapabilityListElement.java b/service/java/com/android/server/wifi/anqp/CapabilityListElement.java
new file mode 100644
index 0000000..301d417
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/CapabilityListElement.java
@@ -0,0 +1,40 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * The ANQP Capability List element, 802.11-2012 section 8.4.4.3
+ */
+public class CapabilityListElement extends ANQPElement {
+ private final Constants.ANQPElementType[] mCapabilities;
+
+ public CapabilityListElement(Constants.ANQPElementType infoID, ByteBuffer payload)
+ throws ProtocolException {
+ super(infoID);
+ if ((payload.remaining() & 1) == 1)
+ throw new ProtocolException("Odd length");
+ mCapabilities = new Constants.ANQPElementType[payload.remaining() / Constants.BYTES_IN_SHORT];
+
+ int index = 0;
+ while (payload.hasRemaining()) {
+ int capID = payload.getShort() & Constants.SHORT_MASK;
+ Constants.ANQPElementType capability = Constants.mapANQPElement(capID);
+ if (capability == null)
+ throw new ProtocolException("Unknown capability: " + capID);
+ mCapabilities[index++] = capability;
+ }
+ }
+
+ public Constants.ANQPElementType[] getCapabilities() {
+ return mCapabilities;
+ }
+
+ @Override
+ public String toString() {
+ return "CapabilityList{" +
+ "mCapabilities=" + Arrays.toString(mCapabilities) +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/CellularNetwork.java b/service/java/com/android/server/wifi/anqp/CellularNetwork.java
new file mode 100644
index 0000000..03d607e
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/CellularNetwork.java
@@ -0,0 +1,70 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
+
+public class CellularNetwork implements Iterable<String> {
+ private static final int PLMNListType = 0;
+
+ private final List<String> mMccMnc;
+
+ private CellularNetwork(int plmnCount, ByteBuffer payload) throws ProtocolException {
+ mMccMnc = new ArrayList<>(plmnCount);
+
+ while (plmnCount > 0) {
+ if (payload.remaining() < 3) {
+ throw new ProtocolException("Truncated PLMN info");
+ }
+ byte[] plmn = new byte[3];
+ payload.get(plmn);
+
+ int mcc = ((plmn[0] << 8) & 0xf00) |
+ (plmn[0] & 0x0f0) |
+ (plmn[1] & 0x00f);
+
+ int mnc = ((plmn[2] << 4) & 0xf0) |
+ ((plmn[2] >> 4) & 0x0f);
+
+ int n2 = (plmn[1] >> 4) & 0x0f;
+ String mccMnc = n2 != 0xf ?
+ String.format("%03x%03x", mcc, (mnc << 4) | n2) :
+ String.format("%03x%02x", mcc, mnc);
+
+ mMccMnc.add(mccMnc);
+ plmnCount--;
+ }
+ }
+
+ public static CellularNetwork buildCellularNetwork(ByteBuffer payload)
+ throws ProtocolException {
+ int iei = payload.get() & BYTE_MASK;
+ int plmnLen = payload.get() & 0x7f;
+
+ if (iei != PLMNListType) {
+ payload.position(payload.position() + plmnLen);
+ return null;
+ }
+
+ int plmnCount = payload.get() & BYTE_MASK;
+ return new CellularNetwork(plmnCount, payload);
+ }
+
+ @Override
+ public Iterator<String> iterator() {
+ return mMccMnc.iterator();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("PLMN:");
+ for (String mccMnc : mMccMnc) {
+ sb.append(' ').append(mccMnc);
+ }
+ return sb.toString();
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/CivicLocationElement.java b/service/java/com/android/server/wifi/anqp/CivicLocationElement.java
new file mode 100644
index 0000000..7269336
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/CivicLocationElement.java
@@ -0,0 +1,199 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * The Civic Location ANQP Element, IEEE802.11-2012 section 8.4.4.13
+ */
+public class CivicLocationElement extends ANQPElement {
+ public enum LocationType {DHCPServer, NwkElement, Client}
+
+ private static final int GEOCONF_CIVIC4 = 99;
+ private static final int RFC4776 = 0; // Table 8-77, 1=vendor specific
+
+ private final LocationType mLocationType;
+ private final Locale mLocale;
+ private final Map<CAType, String> mValues;
+
+ public CivicLocationElement(Constants.ANQPElementType infoID, ByteBuffer payload)
+ throws ProtocolException {
+ super(infoID);
+
+ if (payload.remaining() < 6) {
+ throw new ProtocolException("Runt civic location:" + payload.remaining());
+ }
+
+ int locType = payload.get() & Constants.BYTE_MASK;
+ if (locType != RFC4776) {
+ throw new ProtocolException("Bad Civic location type: " + locType);
+ }
+
+ int locSubType = payload.get() & Constants.BYTE_MASK;
+ if (locSubType != GEOCONF_CIVIC4) {
+ throw new ProtocolException("Unexpected Civic location sub-type: " + locSubType +
+ " (cannot handle sub elements)");
+ }
+
+ int length = payload.get() & Constants.BYTE_MASK;
+ if (length > payload.remaining()) {
+ throw new ProtocolException("Invalid CA type length: " + length);
+ }
+
+ int what = payload.get() & Constants.BYTE_MASK;
+ mLocationType = what < LocationType.values().length ? LocationType.values()[what] : null;
+
+ mLocale = Locale.forLanguageTag(Constants.getString(payload, 2, StandardCharsets.US_ASCII));
+
+ mValues = new HashMap<CAType, String>();
+ while (payload.hasRemaining()) {
+ int caTypeNumber = payload.get() & Constants.BYTE_MASK;
+ CAType caType = s_caTypes.get(caTypeNumber);
+
+ int caValLen = payload.get() & Constants.BYTE_MASK;
+ if (caValLen > payload.remaining()) {
+ throw new ProtocolException("Bad CA value length: " + caValLen);
+ }
+ byte[] caValOctets = new byte[caValLen];
+ payload.get(caValOctets);
+
+ if (caType != null) {
+ mValues.put(caType, new String(caValOctets, StandardCharsets.UTF_8));
+ }
+ }
+ }
+
+ public LocationType getLocationType() {
+ return mLocationType;
+ }
+
+ public Locale getLocale() {
+ return mLocale;
+ }
+
+ public Map<CAType, String> getValues() {
+ return Collections.unmodifiableMap(mValues);
+ }
+
+ @Override
+ public String toString() {
+ return "CivicLocation{" +
+ "mLocationType=" + mLocationType +
+ ", mLocale=" + mLocale +
+ ", mValues=" + mValues +
+ '}';
+ }
+
+ private static final Map<Integer, CAType> s_caTypes = new HashMap<Integer, CAType>();
+
+ public static final int LANGUAGE = 0;
+ public static final int STATE_PROVINCE = 1;
+ public static final int COUNTY_DISTRICT = 2;
+ public static final int CITY = 3;
+ public static final int DIVISION_BOROUGH = 4;
+ public static final int BLOCK = 5;
+ public static final int STREET_GROUP = 6;
+ public static final int STREET_DIRECTION = 16;
+ public static final int LEADING_STREET_SUFFIX = 17;
+ public static final int STREET_SUFFIX = 18;
+ public static final int HOUSE_NUMBER = 19;
+ public static final int HOUSE_NUMBER_SUFFIX = 20;
+ public static final int LANDMARK = 21;
+ public static final int ADDITIONAL_LOCATION = 22;
+ public static final int NAME = 23;
+ public static final int POSTAL_ZIP = 24;
+ public static final int BUILDING = 25;
+ public static final int UNIT = 26;
+ public static final int FLOOR = 27;
+ public static final int ROOM = 28;
+ public static final int TYPE = 29;
+ public static final int POSTAL_COMMUNITY = 30;
+ public static final int PO_BOX = 31;
+ public static final int ADDITIONAL_CODE = 32;
+ public static final int SEAT_DESK = 33;
+ public static final int PRIMARY_ROAD = 34;
+ public static final int ROAD_SECTION = 35;
+ public static final int BRANCH_ROAD = 36;
+ public static final int SUB_BRANCH_ROAD = 37;
+ public static final int STREET_NAME_PRE_MOD = 38;
+ public static final int STREET_NAME_POST_MOD = 39;
+ public static final int SCRIPT = 128;
+ public static final int RESERVED = 255;
+
+ public enum CAType {
+ Language,
+ StateProvince,
+ CountyDistrict,
+ City,
+ DivisionBorough,
+ Block,
+ StreetGroup,
+ StreetDirection,
+ LeadingStreetSuffix,
+ StreetSuffix,
+ HouseNumber,
+ HouseNumberSuffix,
+ Landmark,
+ AdditionalLocation,
+ Name,
+ PostalZIP,
+ Building,
+ Unit,
+ Floor,
+ Room,
+ Type,
+ PostalCommunity,
+ POBox,
+ AdditionalCode,
+ SeatDesk,
+ PrimaryRoad,
+ RoadSection,
+ BranchRoad,
+ SubBranchRoad,
+ StreetNamePreMod,
+ StreetNamePostMod,
+ Script,
+ Reserved
+ }
+
+ static {
+ s_caTypes.put(LANGUAGE, CAType.Language);
+ s_caTypes.put(STATE_PROVINCE, CAType.StateProvince);
+ s_caTypes.put(COUNTY_DISTRICT, CAType.CountyDistrict);
+ s_caTypes.put(CITY, CAType.City);
+ s_caTypes.put(DIVISION_BOROUGH, CAType.DivisionBorough);
+ s_caTypes.put(BLOCK, CAType.Block);
+ s_caTypes.put(STREET_GROUP, CAType.StreetGroup);
+ s_caTypes.put(STREET_DIRECTION, CAType.StreetDirection);
+ s_caTypes.put(LEADING_STREET_SUFFIX, CAType.LeadingStreetSuffix);
+ s_caTypes.put(STREET_SUFFIX, CAType.StreetSuffix);
+ s_caTypes.put(HOUSE_NUMBER, CAType.HouseNumber);
+ s_caTypes.put(HOUSE_NUMBER_SUFFIX, CAType.HouseNumberSuffix);
+ s_caTypes.put(LANDMARK, CAType.Landmark);
+ s_caTypes.put(ADDITIONAL_LOCATION, CAType.AdditionalLocation);
+ s_caTypes.put(NAME, CAType.Name);
+ s_caTypes.put(POSTAL_ZIP, CAType.PostalZIP);
+ s_caTypes.put(BUILDING, CAType.Building);
+ s_caTypes.put(UNIT, CAType.Unit);
+ s_caTypes.put(FLOOR, CAType.Floor);
+ s_caTypes.put(ROOM, CAType.Room);
+ s_caTypes.put(TYPE, CAType.Type);
+ s_caTypes.put(POSTAL_COMMUNITY, CAType.PostalCommunity);
+ s_caTypes.put(PO_BOX, CAType.POBox);
+ s_caTypes.put(ADDITIONAL_CODE, CAType.AdditionalCode);
+ s_caTypes.put(SEAT_DESK, CAType.SeatDesk);
+ s_caTypes.put(PRIMARY_ROAD, CAType.PrimaryRoad);
+ s_caTypes.put(ROAD_SECTION, CAType.RoadSection);
+ s_caTypes.put(BRANCH_ROAD, CAType.BranchRoad);
+ s_caTypes.put(SUB_BRANCH_ROAD, CAType.SubBranchRoad);
+ s_caTypes.put(STREET_NAME_PRE_MOD, CAType.StreetNamePreMod);
+ s_caTypes.put(STREET_NAME_POST_MOD, CAType.StreetNamePostMod);
+ s_caTypes.put(SCRIPT, CAType.Script);
+ s_caTypes.put(RESERVED, CAType.Reserved);
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/Constants.java b/service/java/com/android/server/wifi/anqp/Constants.java
new file mode 100644
index 0000000..e32c209
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/Constants.java
@@ -0,0 +1,196 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * ANQP related constants (802.11-2012)
+ */
+public class Constants {
+
+ public static final int NIBBLE_MASK = 0x0f;
+ public static final int BYTE_MASK = 0xff;
+ public static final int SHORT_MASK = 0xffff;
+ public static final long INT_MASK = 0xffffffffL;
+ public static final int BYTES_IN_SHORT = 2;
+ public static final int BYTES_IN_INT = 4;
+ public static final int BYTES_IN_EUI48 = 6;
+
+ public static final int HS20_PREFIX = 0x119a6f50; // Note that this is represented as a LE int
+ public static final int HS20_FRAME_PREFIX = 0x109a6f50;
+ public static final int UTF8_INDICATOR = 1;
+
+ public static final int ANQP_QUERY_LIST = 256;
+ public static final int ANQP_CAPABILITY_LIST = 257;
+ public static final int ANQP_VENUE_NAME = 258;
+ public static final int ANQP_EMERGENCY_NUMBER = 259;
+ public static final int ANQP_NWK_AUTH_TYPE = 260;
+ public static final int ANQP_ROAMING_CONSORTIUM = 261;
+ public static final int ANQP_IP_ADDR_AVAILABILITY = 262;
+ public static final int ANQP_NAI_REALM = 263;
+ public static final int ANQP_3GPP_NETWORK = 264;
+ public static final int ANQP_GEO_LOC = 265;
+ public static final int ANQP_CIVIC_LOC = 266;
+ public static final int ANQP_LOC_URI = 267;
+ public static final int ANQP_DOM_NAME = 268;
+ public static final int ANQP_EMERGENCY_ALERT = 269;
+ public static final int ANQP_TDLS_CAP = 270;
+ public static final int ANQP_EMERGENCY_NAI = 271;
+ public static final int ANQP_NEIGHBOR_REPORT = 272;
+ public static final int ANQP_VENDOR_SPEC = 56797;
+
+ public static final int HS_QUERY_LIST = 1;
+ public static final int HS_CAPABILITY_LIST = 2;
+ public static final int HS_FRIENDLY_NAME = 3;
+ public static final int HS_WAN_METRICS = 4;
+ public static final int HS_CONN_CAPABILITY = 5;
+ public static final int HS_NAI_HOME_REALM_QUERY = 6;
+ public static final int HS_OPERATING_CLASS = 7;
+ public static final int HS_OSU_PROVIDERS = 8;
+ public static final int HS_ICON_REQUEST = 10;
+ public static final int HS_ICON_FILE = 11;
+
+ public enum ANQPElementType {
+ ANQPQueryList,
+ ANQPCapabilityList,
+ ANQPVenueName,
+ ANQPEmergencyNumber,
+ ANQPNwkAuthType,
+ ANQPRoamingConsortium,
+ ANQPIPAddrAvailability,
+ ANQPNAIRealm,
+ ANQP3GPPNetwork,
+ ANQPGeoLoc,
+ ANQPCivicLoc,
+ ANQPLocURI,
+ ANQPDomName,
+ ANQPEmergencyAlert,
+ ANQPTDLSCap,
+ ANQPEmergencyNAI,
+ ANQPNeighborReport,
+ ANQPVendorSpec,
+ HSQueryList,
+ HSCapabilityList,
+ HSFriendlyName,
+ HSWANMetrics,
+ HSConnCapability,
+ HSNAIHomeRealmQuery,
+ HSOperatingclass,
+ HSOSUProviders,
+ HSIconRequest,
+ HSIconFile
+ }
+
+ private static final Map<Integer, ANQPElementType> sAnqpMap = new HashMap<Integer, ANQPElementType>();
+ private static final Map<Integer, ANQPElementType> sHs20Map = new HashMap<Integer, ANQPElementType>();
+ private static final Map<ANQPElementType, Integer> sRevAnqpmap = new HashMap<ANQPElementType, Integer>();
+ private static final Map<ANQPElementType, Integer> sRevHs20map = new HashMap<ANQPElementType, Integer>();
+
+ static {
+ sAnqpMap.put(ANQP_QUERY_LIST, ANQPElementType.ANQPQueryList);
+ sAnqpMap.put(ANQP_CAPABILITY_LIST, ANQPElementType.ANQPCapabilityList);
+ sAnqpMap.put(ANQP_VENUE_NAME, ANQPElementType.ANQPVenueName);
+ sAnqpMap.put(ANQP_EMERGENCY_NUMBER, ANQPElementType.ANQPEmergencyNumber);
+ sAnqpMap.put(ANQP_NWK_AUTH_TYPE, ANQPElementType.ANQPNwkAuthType);
+ sAnqpMap.put(ANQP_ROAMING_CONSORTIUM, ANQPElementType.ANQPRoamingConsortium);
+ sAnqpMap.put(ANQP_IP_ADDR_AVAILABILITY, ANQPElementType.ANQPIPAddrAvailability);
+ sAnqpMap.put(ANQP_NAI_REALM, ANQPElementType.ANQPNAIRealm);
+ sAnqpMap.put(ANQP_3GPP_NETWORK, ANQPElementType.ANQP3GPPNetwork);
+ sAnqpMap.put(ANQP_GEO_LOC, ANQPElementType.ANQPGeoLoc);
+ sAnqpMap.put(ANQP_CIVIC_LOC, ANQPElementType.ANQPCivicLoc);
+ sAnqpMap.put(ANQP_LOC_URI, ANQPElementType.ANQPLocURI);
+ sAnqpMap.put(ANQP_DOM_NAME, ANQPElementType.ANQPDomName);
+ sAnqpMap.put(ANQP_EMERGENCY_ALERT, ANQPElementType.ANQPEmergencyAlert);
+ sAnqpMap.put(ANQP_TDLS_CAP, ANQPElementType.ANQPTDLSCap);
+ sAnqpMap.put(ANQP_EMERGENCY_NAI, ANQPElementType.ANQPEmergencyNAI);
+ sAnqpMap.put(ANQP_NEIGHBOR_REPORT, ANQPElementType.ANQPNeighborReport);
+ sAnqpMap.put(ANQP_VENDOR_SPEC, ANQPElementType.ANQPVendorSpec);
+
+ sHs20Map.put(HS_QUERY_LIST, ANQPElementType.HSQueryList);
+ sHs20Map.put(HS_CAPABILITY_LIST, ANQPElementType.HSCapabilityList);
+ sHs20Map.put(HS_FRIENDLY_NAME, ANQPElementType.HSFriendlyName);
+ sHs20Map.put(HS_WAN_METRICS, ANQPElementType.HSWANMetrics);
+ sHs20Map.put(HS_CONN_CAPABILITY, ANQPElementType.HSConnCapability);
+ sHs20Map.put(HS_NAI_HOME_REALM_QUERY, ANQPElementType.HSNAIHomeRealmQuery);
+ sHs20Map.put(HS_OPERATING_CLASS, ANQPElementType.HSOperatingclass);
+ sHs20Map.put(HS_OSU_PROVIDERS, ANQPElementType.HSOSUProviders);
+ sHs20Map.put(HS_ICON_REQUEST, ANQPElementType.HSIconRequest);
+ sHs20Map.put(HS_ICON_FILE, ANQPElementType.HSIconFile);
+
+ for (Map.Entry<Integer, ANQPElementType> entry : sAnqpMap.entrySet()) {
+ sRevAnqpmap.put(entry.getValue(), entry.getKey());
+ }
+ for (Map.Entry<Integer, ANQPElementType> entry : sHs20Map.entrySet()) {
+ sRevHs20map.put(entry.getValue(), entry.getKey());
+ }
+ }
+
+ public static ANQPElementType mapANQPElement(int id) {
+ return sAnqpMap.get(id);
+ }
+
+ public static ANQPElementType mapHS20Element(int id) {
+ return sHs20Map.get(id);
+ }
+
+ public static Integer getANQPElementID(ANQPElementType elementType) {
+ return sRevAnqpmap.get(elementType);
+ }
+
+ public static Integer getHS20ElementID(ANQPElementType elementType) {
+ return sRevHs20map.get(elementType);
+ }
+
+ public static long getInteger(ByteBuffer payload, ByteOrder bo, int size) {
+ byte[] octets = new byte[size];
+ payload.get(octets);
+ long value = 0;
+ if (bo == ByteOrder.LITTLE_ENDIAN) {
+ for (int n = octets.length - 1; n >= 0; n--) {
+ value = (value << Byte.SIZE) | (octets[n] & BYTE_MASK);
+ }
+ }
+ else {
+ for (byte octet : octets) {
+ value = (value << Byte.SIZE) | (octet & BYTE_MASK);
+ }
+ }
+ return value;
+ }
+
+ public static String getPrefixedString(ByteBuffer payload, int lengthLength, Charset charset)
+ throws ProtocolException {
+ return getPrefixedString(payload, lengthLength, charset, false);
+ }
+
+ public static String getPrefixedString(ByteBuffer payload, int lengthLength, Charset charset,
+ boolean useNull) throws ProtocolException {
+ if (payload.remaining() < lengthLength) {
+ throw new ProtocolException("Runt string: " + payload.remaining());
+ }
+ return getString(payload, (int) getInteger(payload, ByteOrder.LITTLE_ENDIAN,
+ lengthLength), charset, useNull);
+ }
+
+ public static String getString(ByteBuffer payload, int length, Charset charset)
+ throws ProtocolException {
+ return getString(payload, length, charset, false);
+ }
+
+ public static String getString(ByteBuffer payload, int length, Charset charset, boolean useNull)
+ throws ProtocolException {
+ if (length > payload.remaining()) {
+ throw new ProtocolException("Bad string length: " + length);
+ }
+ if (useNull && length == 0) {
+ return null;
+ }
+ byte[] octets = new byte[length];
+ payload.get(octets);
+ return new String(octets, charset);
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/DomainNameElement.java b/service/java/com/android/server/wifi/anqp/DomainNameElement.java
new file mode 100644
index 0000000..69edc90
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/DomainNameElement.java
@@ -0,0 +1,37 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The Domain Name ANQP Element, IEEE802.11-2012 section 8.4.4.15
+ */
+public class DomainNameElement extends ANQPElement {
+ private final List<String> mDomains;
+
+ public DomainNameElement(Constants.ANQPElementType infoID, ByteBuffer payload)
+ throws ProtocolException {
+ super(infoID);
+ mDomains = new ArrayList<>();
+
+ while (payload.hasRemaining()) {
+ // Use latin-1 to decode for now - safe for ASCII and retains encoding
+ mDomains.add(Constants.getPrefixedString(payload, 1, StandardCharsets.ISO_8859_1));
+ }
+ }
+
+ public List<String> getDomains() {
+ return Collections.unmodifiableList(mDomains);
+ }
+
+ @Override
+ public String toString() {
+ return "DomainName{" +
+ "mDomains=" + mDomains +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/EmergencyNumberElement.java b/service/java/com/android/server/wifi/anqp/EmergencyNumberElement.java
new file mode 100644
index 0000000..6954480
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/EmergencyNumberElement.java
@@ -0,0 +1,36 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The Emergency Number ANQP Element, IEEE802.11-2012 section 8.4.4.5
+ */
+public class EmergencyNumberElement extends ANQPElement {
+ private final List<String> mNumbers;
+
+ public EmergencyNumberElement(Constants.ANQPElementType infoID, ByteBuffer payload)
+ throws ProtocolException {
+ super(infoID);
+
+ mNumbers = new ArrayList<String>();
+
+ while (payload.hasRemaining()) {
+ mNumbers.add(Constants.getPrefixedString(payload, 1, StandardCharsets.UTF_8));
+ }
+ }
+
+ public List<String> getNumbers() {
+ return mNumbers;
+ }
+
+ @Override
+ public String toString() {
+ return "EmergencyNumber{" +
+ "mNumbers=" + mNumbers +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/GEOLocationElement.java b/service/java/com/android/server/wifi/anqp/GEOLocationElement.java
new file mode 100644
index 0000000..d434c73
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/GEOLocationElement.java
@@ -0,0 +1,311 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+
+/**
+ * Holds an AP Geospatial Location ANQP Element, as specified in IEEE802.11-2012 section
+ * 8.4.4.12.
+ * <p/>
+ * <p>
+ * Section 8.4.2.24.10 of the IEEE802.11-2012 specification refers to RFC-3825 for the format of the
+ * Geospatial location information. RFC-3825 has subsequently been obsoleted by RFC-6225 which
+ * defines the same basic binary format for the DHCPv4 payload except that a few unused bits of the
+ * Datum field have been reserved for other uses.
+ * </p>
+ * <p/>
+ * <p>
+ * RFC-3825 defines a resolution field for each of latitude, longitude and altitude as "the number
+ * of significant bits" of precision in the respective values and implies through examples and
+ * otherwise that the non-significant bits should be simply disregarded and the range of values are
+ * calculated as the numeric interval obtained by varying the range of "insignificant bits" between
+ * its extremes. As a simple example, consider the value 33 as a simple 8-bit number with three
+ * significant bits: 33 is 00100001 binary and the leading 001 are the significant bits. With the
+ * above definition, the range of numbers are [32,63] with 33 asymmetrically located at the low end
+ * of the interval. In a more realistic setting an instrument, such as a GPS, would most likely
+ * deliver measurements with a gaussian distribution around the exact value, meaning it is more
+ * reasonable to assume the value as a "center" value with a symmetric uncertainty interval.
+ * RFC-6225 redefines the "resolution" from RFC-3825 with an "uncertainty" value with these
+ * properties, which is also the definition suggested here.
+ * </p>
+ * <p/>
+ * <p>
+ * The res fields provides the resolution as the exponent to a power of two,
+ * e.g. 8 means 2^8 = +/- 256, 0 means 2^0 = +/- 1 and -7 means 2^-7 +/- 0.00781250.
+ * Unknown resolution is indicated by not setting the respective resolution field in the RealValue.
+ * </p>
+ */
+public class GEOLocationElement extends ANQPElement {
+ public enum AltitudeType {Unknown, Meters, Floors}
+
+ public enum Datum {Unknown, WGS84, NAD83Land, NAD83Water}
+
+ private static final int ELEMENT_ID = 123; // ???
+ private static final int GEO_LOCATION_LENGTH = 16;
+
+ private static final int LL_FRACTION_SIZE = 25;
+ private static final int LL_WIDTH = 34;
+ private static final int ALT_FRACTION_SIZE = 8;
+ private static final int ALT_WIDTH = 30;
+ private static final int RES_WIDTH = 6;
+ private static final int ALT_TYPE_WIDTH = 4;
+ private static final int DATUM_WIDTH = 8;
+
+ private final RealValue mLatitude;
+ private final RealValue mLongitude;
+ private final RealValue mAltitude;
+ private final AltitudeType mAltitudeType;
+ private final Datum mDatum;
+
+ public static class RealValue {
+ private final double mValue;
+ private final boolean mResolutionSet;
+ private final int mResolution;
+
+ public RealValue(double value) {
+ mValue = value;
+ mResolution = Integer.MIN_VALUE;
+ mResolutionSet = false;
+ }
+
+ public RealValue(double value, int resolution) {
+ mValue = value;
+ mResolution = resolution;
+ mResolutionSet = true;
+ }
+
+ public double getValue() {
+ return mValue;
+ }
+
+ public boolean isResolutionSet() {
+ return mResolutionSet;
+ }
+
+ public int getResolution() {
+ return mResolution;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(String.format("%f", mValue));
+ if (mResolutionSet) {
+ sb.append("+/-2^").append(mResolution);
+ }
+ return sb.toString();
+ }
+ }
+
+ public GEOLocationElement(Constants.ANQPElementType infoID, ByteBuffer payload)
+ throws ProtocolException {
+ super(infoID);
+
+ payload.get();
+ int locLength = payload.get() & Constants.BYTE_MASK;
+
+ if (locLength != GEO_LOCATION_LENGTH) {
+ throw new ProtocolException("GeoLocation length field value " + locLength +
+ " incorrect, expected 16");
+ }
+ if (payload.remaining() != GEO_LOCATION_LENGTH) {
+ throw new ProtocolException("Bad buffer length " + payload.remaining() +
+ ", expected 16");
+ }
+
+ ReverseBitStream reverseBitStream = new ReverseBitStream(payload);
+
+ int rawLatRes = (int) reverseBitStream.sliceOff(RES_WIDTH);
+ double latitude =
+ fixToFloat(reverseBitStream.sliceOff(LL_WIDTH), LL_FRACTION_SIZE, LL_WIDTH);
+
+ mLatitude = rawLatRes != 0 ?
+ new RealValue(latitude, bitsToAbsResolution(rawLatRes, LL_WIDTH,
+ LL_FRACTION_SIZE)) :
+ new RealValue(latitude);
+
+ int rawLonRes = (int) reverseBitStream.sliceOff(RES_WIDTH);
+ double longitude =
+ fixToFloat(reverseBitStream.sliceOff(LL_WIDTH), LL_FRACTION_SIZE, LL_WIDTH);
+
+ mLongitude = rawLonRes != 0 ?
+ new RealValue(longitude, bitsToAbsResolution(rawLonRes, LL_WIDTH,
+ LL_FRACTION_SIZE)) :
+ new RealValue(longitude);
+
+ int altType = (int) reverseBitStream.sliceOff(ALT_TYPE_WIDTH);
+ mAltitudeType = altType < AltitudeType.values().length ?
+ AltitudeType.values()[altType] :
+ AltitudeType.Unknown;
+
+ int rawAltRes = (int) reverseBitStream.sliceOff(RES_WIDTH);
+ double altitude = fixToFloat(reverseBitStream.sliceOff(ALT_WIDTH), ALT_FRACTION_SIZE,
+ ALT_WIDTH);
+
+ mAltitude = rawAltRes != 0 ?
+ new RealValue(altitude, bitsToAbsResolution(rawAltRes, ALT_WIDTH,
+ ALT_FRACTION_SIZE)) :
+ new RealValue(altitude);
+
+ int datumValue = (int) reverseBitStream.sliceOff(DATUM_WIDTH);
+ mDatum = datumValue < Datum.values().length ? Datum.values()[datumValue] : Datum.Unknown;
+ }
+
+ public RealValue getLatitude() {
+ return mLatitude;
+ }
+
+ public RealValue getLongitude() {
+ return mLongitude;
+ }
+
+ public RealValue getAltitude() {
+ return mAltitude;
+ }
+
+ public AltitudeType getAltitudeType() {
+ return mAltitudeType;
+ }
+
+ public Datum getDatum() {
+ return mDatum;
+ }
+
+ @Override
+ public String toString() {
+ return "GEOLocation{" +
+ "mLatitude=" + mLatitude +
+ ", mLongitude=" + mLongitude +
+ ", mAltitude=" + mAltitude +
+ ", mAltitudeType=" + mAltitudeType +
+ ", mDatum=" + mDatum +
+ '}';
+ }
+
+ private static class ReverseBitStream {
+
+ private final byte[] mOctets;
+ private int mBitoffset;
+
+ private ReverseBitStream(ByteBuffer octets) {
+ mOctets = new byte[octets.remaining()];
+ octets.get(mOctets);
+ }
+
+ private long sliceOff(int bits) {
+ final int bn = mBitoffset + bits;
+ int remaining = bits;
+ long value = 0;
+
+ while (mBitoffset < bn) {
+ int sbit = mBitoffset & 0x7; // Bit #0 is MSB, inclusive
+ int octet = mBitoffset >>> 3;
+
+ // Copy the minimum of what's to the right of sbit
+ // and how much more goes to the target
+ int width = Math.min(Byte.SIZE - sbit, remaining);
+
+ value = (value << width) | getBits(mOctets[octet], sbit, width);
+
+ mBitoffset += width;
+ remaining -= width;
+ }
+
+ return value;
+ }
+
+ private static int getBits(byte b, int b0, int width) {
+ int mask = (1 << width) - 1;
+ return (b >> (Byte.SIZE - b0 - width)) & mask;
+ }
+ }
+
+ private static class BitStream {
+
+ private final byte[] data;
+ private int bitOffset; // bit 0 is MSB of data[0]
+
+ private BitStream(int octets) {
+ data = new byte[octets];
+ }
+
+ private void append(long value, int width) {
+ System.out.printf("Appending %x:%d\n", value, width);
+ for (int sbit = width - 1; sbit >= 0; ) {
+ int b0 = bitOffset >>> 3;
+ int dbit = bitOffset & 0x7;
+
+ int shr = sbit - 7 + dbit;
+ int dmask = 0xff >>> dbit;
+
+ if (shr >= 0) {
+ data[b0] = (byte) ((data[b0] & ~dmask) | ((value >>> shr) & dmask));
+ bitOffset += Byte.SIZE - dbit;
+ sbit -= Byte.SIZE - dbit;
+ } else {
+ data[b0] = (byte) ((data[b0] & ~dmask) | ((value << -shr) & dmask));
+ bitOffset += sbit + 1;
+ sbit = -1;
+ }
+ }
+ }
+
+ private byte[] getOctets() {
+ return data;
+ }
+ }
+
+ static double fixToFloat(long value, int fractionSize, int width) {
+ long sign = 1L << (width - 1);
+ if ((value & sign) != 0) {
+ value = -value;
+ return -(double) (value & (sign - 1)) / (double) (1L << fractionSize);
+ } else {
+ return (double) (value & (sign - 1)) / (double) (1L << fractionSize);
+ }
+ }
+
+ private static long floatToFix(double value, int fractionSize, int width) {
+ return Math.round(value * (1L << fractionSize)) & ((1L << width) - 1);
+ }
+
+ private static final double LOG2_FACTOR = 1.0 / Math.log(2.0);
+
+ /**
+ * Convert an absolute variance value into absolute resolution representation,
+ * where the variance = 2^resolution.
+ *
+ * @param variance The absolute variance
+ * @return the absolute resolution.
+ */
+ private static int getResolution(double variance) {
+ return (int) Math.ceil(Math.log(variance) * LOG2_FACTOR);
+ }
+
+ /**
+ * Convert an absolute resolution, into the "number of significant bits" for the given fixed
+ * point notation as defined in RFC-3825 and refined in RFC-6225.
+ *
+ * @param resolution absolute resolution given as 2^resolution.
+ * @param fieldWidth Full width of the fixed point number used to represent the value.
+ * @param fractionBits Number of fraction bits in the fixed point number used to represent the
+ * value.
+ * @return The number of "significant bits".
+ */
+ private static int absResolutionToBits(int resolution, int fieldWidth, int fractionBits) {
+ return fieldWidth - fractionBits - 1 - resolution;
+ }
+
+ /**
+ * Convert the protocol definition of "number of significant bits" into an absolute resolution.
+ *
+ * @param bits The number of "significant bits" from the binary protocol.
+ * @param fieldWidth Full width of the fixed point number used to represent the value.
+ * @param fractionBits Number of fraction bits in the fixed point number used to represent the
+ * value.
+ * @return The absolute resolution given as 2^resolution.
+ */
+ private static int bitsToAbsResolution(long bits, int fieldWidth, int fractionBits) {
+ return fieldWidth - fractionBits - 1 - (int) bits;
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/GenericBlobElement.java b/service/java/com/android/server/wifi/anqp/GenericBlobElement.java
new file mode 100644
index 0000000..eff130d
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/GenericBlobElement.java
@@ -0,0 +1,27 @@
+package com.android.server.wifi.anqp;
+
+import com.android.server.wifi.hotspot2.Utils;
+
+import java.nio.ByteBuffer;
+
+/**
+ * ANQP Element to hold a raw, unparsed, octet blob
+ */
+public class GenericBlobElement extends ANQPElement {
+ private final byte[] mData;
+
+ public GenericBlobElement(Constants.ANQPElementType infoID, ByteBuffer payload) {
+ super(infoID);
+ mData = new byte[payload.remaining()];
+ payload.get(mData);
+ }
+
+ public byte[] getData() {
+ return mData;
+ }
+
+ @Override
+ public String toString() {
+ return "Element ID " + getID() + ": " + Utils.toHexString(mData);
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/GenericStringElement.java b/service/java/com/android/server/wifi/anqp/GenericStringElement.java
new file mode 100644
index 0000000..6cf7b1a
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/GenericStringElement.java
@@ -0,0 +1,26 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * ANQP Element to hold a generic (UTF-8 decoded) character string
+ */
+public class GenericStringElement extends ANQPElement {
+ private final String mText;
+
+ public GenericStringElement(Constants.ANQPElementType infoID, ByteBuffer payload) throws ProtocolException {
+ super(infoID);
+ mText = Constants.getString(payload, payload.remaining(), StandardCharsets.UTF_8);
+ }
+
+ public String getM_text() {
+ return mText;
+ }
+
+ @Override
+ public String toString() {
+ return "Element ID " + getID() + ": '" + mText + "'";
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/HSCapabilityListElement.java b/service/java/com/android/server/wifi/anqp/HSCapabilityListElement.java
new file mode 100644
index 0000000..8269324
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/HSCapabilityListElement.java
@@ -0,0 +1,42 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * The HS Capability list vendor specific ANQP Element,
+ * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
+ * section 4.2
+ */
+public class HSCapabilityListElement extends ANQPElement {
+ private final Constants.ANQPElementType[] mCapabilities;
+
+ public HSCapabilityListElement(Constants.ANQPElementType infoID, ByteBuffer payload)
+ throws ProtocolException {
+ super(infoID);
+
+ mCapabilities = new Constants.ANQPElementType[payload.remaining()];
+
+ int index = 0;
+ while (payload.hasRemaining()) {
+ int capID = payload.get() & Constants.BYTE_MASK;
+ Constants.ANQPElementType capability = Constants.mapHS20Element(capID);
+ if (capability == null) {
+ throw new ProtocolException("Unknown capability: " + capID);
+ }
+ mCapabilities[index++] = capability;
+ }
+ }
+
+ public Constants.ANQPElementType[] getCapabilities() {
+ return mCapabilities;
+ }
+
+ @Override
+ public String toString() {
+ return "HSCapabilityList{" +
+ "mCapabilities=" + Arrays.toString(mCapabilities) +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/HSConnectionCapabilityElement.java b/service/java/com/android/server/wifi/anqp/HSConnectionCapabilityElement.java
new file mode 100644
index 0000000..53f1051
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/HSConnectionCapabilityElement.java
@@ -0,0 +1,79 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The Connection Capability vendor specific ANQP Element,
+ * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
+ * section 4.5
+ */
+public class HSConnectionCapabilityElement extends ANQPElement {
+
+ public enum ProtoStatus {Closed, Open, Unknown}
+
+ private final List<ProtocolTuple> mStatusList;
+
+ public static class ProtocolTuple {
+ private final int mProtocol;
+ private final int mPort;
+ private final ProtoStatus mStatus;
+
+ private ProtocolTuple(ByteBuffer payload) throws ProtocolException {
+ if (payload.remaining() < 4) {
+ throw new ProtocolException("Runt protocol tuple: " + payload.remaining());
+ }
+ mProtocol = payload.get() & Constants.BYTE_MASK;
+ mPort = payload.getShort() & Constants.SHORT_MASK;
+ int statusNumber = payload.get() & Constants.BYTE_MASK;
+ mStatus = statusNumber < ProtoStatus.values().length ?
+ ProtoStatus.values()[statusNumber] :
+ null;
+ }
+
+ public int getProtocol() {
+ return mProtocol;
+ }
+
+ public int getPort() {
+ return mPort;
+ }
+
+ public ProtoStatus getStatus() {
+ return mStatus;
+ }
+
+ @Override
+ public String toString() {
+ return "ProtocolTuple{" +
+ "mProtocol=" + mProtocol +
+ ", mPort=" + mPort +
+ ", mStatus=" + mStatus +
+ '}';
+ }
+ }
+
+ public HSConnectionCapabilityElement(Constants.ANQPElementType infoID, ByteBuffer payload)
+ throws ProtocolException {
+ super(infoID);
+
+ mStatusList = new ArrayList<>();
+ while (payload.hasRemaining()) {
+ mStatusList.add(new ProtocolTuple(payload));
+ }
+ }
+
+ public List<ProtocolTuple> getStatusList() {
+ return Collections.unmodifiableList(mStatusList);
+ }
+
+ @Override
+ public String toString() {
+ return "HSConnectionCapability{" +
+ "mStatusList=" + mStatusList +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/HSFriendlyNameElement.java b/service/java/com/android/server/wifi/anqp/HSFriendlyNameElement.java
new file mode 100644
index 0000000..a15fc29
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/HSFriendlyNameElement.java
@@ -0,0 +1,38 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The Operator Friendly Name vendor specific ANQP Element,
+ * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
+ * section 4.3
+ */
+public class HSFriendlyNameElement extends ANQPElement {
+ private final List<I18Name> mNames;
+
+ public HSFriendlyNameElement(Constants.ANQPElementType infoID, ByteBuffer payload)
+ throws ProtocolException {
+ super(infoID);
+
+ mNames = new ArrayList<I18Name>();
+
+ while (payload.hasRemaining()) {
+ mNames.add(new I18Name(payload));
+ }
+ }
+
+ public List<I18Name> getNames() {
+ return Collections.unmodifiableList(mNames);
+ }
+
+ @Override
+ public String toString() {
+ return "HSFriendlyName{" +
+ "mNames=" + mNames +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/HSIconFileElement.java b/service/java/com/android/server/wifi/anqp/HSIconFileElement.java
new file mode 100644
index 0000000..8a7740d
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/HSIconFileElement.java
@@ -0,0 +1,59 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
+import static com.android.server.wifi.anqp.Constants.SHORT_MASK;
+
+/**
+ * The Icon Binary File vendor specific ANQP Element,
+ * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
+ * section 4.11
+ */
+public class HSIconFileElement extends ANQPElement {
+
+ public enum StatusCode {Success, FileNotFound, Unspecified}
+
+ private final StatusCode mStatusCode;
+ private final String mType;
+ private final byte[] mIconData;
+
+ public HSIconFileElement(Constants.ANQPElementType infoID, ByteBuffer payload)
+ throws ProtocolException {
+ super(infoID);
+
+ if (payload.remaining() < 4) {
+ throw new ProtocolException("Truncated icon file: " + payload.remaining());
+ }
+
+ int statusID = payload.get() & BYTE_MASK;
+ mStatusCode = statusID < StatusCode.values().length ? StatusCode.values()[statusID] : null;
+ mType = Constants.getPrefixedString(payload, 1, StandardCharsets.US_ASCII);
+
+ int dataLength = payload.getShort() & SHORT_MASK;
+ mIconData = new byte[dataLength];
+ payload.get(mIconData);
+ }
+
+ public StatusCode getStatusCode() {
+ return mStatusCode;
+ }
+
+ public String getType() {
+ return mType;
+ }
+
+ public byte[] getIconData() {
+ return mIconData;
+ }
+
+ @Override
+ public String toString() {
+ return "HSIconFile{" +
+ "mStatusCode=" + mStatusCode +
+ ", mType='" + mType + '\'' +
+ ", mIconData=" + mIconData.length + " bytes }";
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/HSOsuProvidersElement.java b/service/java/com/android/server/wifi/anqp/HSOsuProvidersElement.java
new file mode 100644
index 0000000..15ed8c6
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/HSOsuProvidersElement.java
@@ -0,0 +1,49 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The OSU Providers List vendor specific ANQP Element,
+ * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
+ * section 4.8
+ */
+public class HSOsuProvidersElement extends ANQPElement {
+ private final String mSSID;
+ private final List<OSUProvider> mProviders;
+
+ public HSOsuProvidersElement(Constants.ANQPElementType infoID, ByteBuffer payload)
+ throws ProtocolException {
+ super(infoID);
+
+ mSSID = Constants.getPrefixedString(payload, 1, StandardCharsets.UTF_8);
+ int providerCount = payload.get() & Constants.BYTE_MASK;
+
+ mProviders = new ArrayList<OSUProvider>(providerCount);
+
+ while (providerCount > 0) {
+ mProviders.add(new OSUProvider(payload));
+ providerCount--;
+ }
+ }
+
+ public String getSSID() {
+ return mSSID;
+ }
+
+ public List<OSUProvider> getProviders() {
+ return Collections.unmodifiableList(mProviders);
+ }
+
+ @Override
+ public String toString() {
+ return "HSOsuProviders{" +
+ "mSSID='" + mSSID + '\'' +
+ ", mProviders=" + mProviders +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/HSWanMetricsElement.java b/service/java/com/android/server/wifi/anqp/HSWanMetricsElement.java
new file mode 100644
index 0000000..a1e1ca9
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/HSWanMetricsElement.java
@@ -0,0 +1,89 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+
+import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
+import static com.android.server.wifi.anqp.Constants.INT_MASK;
+import static com.android.server.wifi.anqp.Constants.SHORT_MASK;
+
+/**
+ * The WAN Metrics vendor specific ANQP Element,
+ * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
+ * section 4.4
+ */
+public class HSWanMetricsElement extends ANQPElement {
+
+ public enum LinkStatus {Reserved, Up, Down, Test}
+
+ private final LinkStatus mStatus;
+ private final boolean mSymmetric;
+ private final boolean mCapped;
+ private final long mDlSpeed;
+ private final long mUlSpeed;
+ private final int mDlLoad;
+ private final int mUlLoad;
+ private final int mLMD;
+
+ public HSWanMetricsElement(Constants.ANQPElementType infoID, ByteBuffer payload)
+ throws ProtocolException {
+ super(infoID);
+
+ if (payload.remaining() != 13) {
+ throw new ProtocolException("Bad WAN metrics length: " + payload.remaining());
+ }
+
+ int status = payload.get() & BYTE_MASK;
+ mStatus = LinkStatus.values()[status & 0x03];
+ mSymmetric = (status & 0x04) != 0;
+ mCapped = (status & 0x08) != 0;
+ mDlSpeed = payload.getInt() & INT_MASK;
+ mUlSpeed = payload.getInt() & INT_MASK;
+ mDlLoad = payload.get() & BYTE_MASK;
+ mUlLoad = payload.get() & BYTE_MASK;
+ mLMD = payload.getShort() & SHORT_MASK;
+ }
+
+ public LinkStatus getStatus() {
+ return mStatus;
+ }
+
+ public boolean isSymmetric() {
+ return mSymmetric;
+ }
+
+ public boolean isCapped() {
+ return mCapped;
+ }
+
+ public long getDlSpeed() {
+ return mDlSpeed;
+ }
+
+ public long getUlSpeed() {
+ return mUlSpeed;
+ }
+
+ public int getDlLoad() {
+ return mDlLoad;
+ }
+
+ public int getUlLoad() {
+ return mUlLoad;
+ }
+
+ public int getLMD() {
+ return mLMD;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("HSWanMetrics{mStatus=%s, mSymmetric=%s, mCapped=%s, " +
+ "mDlSpeed=%d, mUlSpeed=%d, mDlLoad=%f, mUlLoad=%f, mLMD=%d}",
+ mStatus, mSymmetric, mCapped,
+ mDlSpeed, mUlSpeed,
+ (double)mDlLoad * 100.0 / 256.0,
+ (double)mUlLoad * 100.0 / 256.0,
+ mLMD);
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/I18Name.java b/service/java/com/android/server/wifi/anqp/I18Name.java
new file mode 100644
index 0000000..1def6cf
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/I18Name.java
@@ -0,0 +1,45 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Locale;
+
+import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
+
+/**
+ * A generic Internationalized name used in ANQP elements as specified in 802.11-2012 and
+ * "Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00"
+ */
+public class I18Name {
+ private static final int LANG_CODE_LENGTH = 3;
+
+ private final Locale mLocale;
+ private final String mText;
+
+ public I18Name(ByteBuffer payload) throws ProtocolException {
+ if (payload.remaining() < 4) {
+ throw new ProtocolException("Truncated I18Name: " + payload.remaining());
+ }
+ int nameLength = payload.get() & BYTE_MASK;
+ if (nameLength < 3) {
+ throw new ProtocolException("Runt I18Name: " + nameLength);
+ }
+ String language = Constants.getString(payload, LANG_CODE_LENGTH, StandardCharsets.US_ASCII);
+ mLocale = Locale.forLanguageTag(language);
+ mText = Constants.getString(payload, nameLength - LANG_CODE_LENGTH, StandardCharsets.UTF_8);
+ }
+
+ public Locale getLocale() {
+ return mLocale;
+ }
+
+ public String getText() {
+ return mText;
+ }
+
+ @Override
+ public String toString() {
+ return mText + ':' + mLocale.getLanguage();
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/IPAddressTypeAvailabilityElement.java b/service/java/com/android/server/wifi/anqp/IPAddressTypeAvailabilityElement.java
new file mode 100644
index 0000000..8d71a3b
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/IPAddressTypeAvailabilityElement.java
@@ -0,0 +1,52 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+
+/**
+ * The IP Address Type availability ANQP Element, IEEE802.11-2012 section 8.4.4.9
+ */
+public class IPAddressTypeAvailabilityElement extends ANQPElement {
+ public enum IPv4Availability {
+ NotAvailable, Public, PortRestricted, SingleNAT, DoubleNAT,
+ PortRestrictedAndSingleNAT, PortRestrictedAndDoubleNAT, Unknown
+ }
+
+ public enum IPv6Availability {NotAvailable, Available, Unknown, Reserved}
+
+ private final IPv4Availability mV4Availability;
+ private final IPv6Availability mV6Availability;
+
+ public IPAddressTypeAvailabilityElement(Constants.ANQPElementType infoID, ByteBuffer payload)
+ throws ProtocolException {
+ super(infoID);
+
+ if (payload.remaining() != 1)
+ throw new ProtocolException("Bad IP Address Type Availability length: " +
+ payload.remaining());
+
+ int ipField = payload.get();
+ mV6Availability = IPv6Availability.values()[ipField & 0x3];
+
+ ipField = (ipField >> 2) & 0x3f;
+ mV4Availability = ipField < IPv4Availability.values().length ?
+ IPv4Availability.values()[ipField] :
+ IPv4Availability.Unknown;
+ }
+
+ public IPv4Availability getV4Availability() {
+ return mV4Availability;
+ }
+
+ public IPv6Availability getV6Availability() {
+ return mV6Availability;
+ }
+
+ @Override
+ public String toString() {
+ return "IPAddressTypeAvailability{" +
+ "mV4Availability=" + mV4Availability +
+ ", mV6Availability=" + mV6Availability +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/IconInfo.java b/service/java/com/android/server/wifi/anqp/IconInfo.java
new file mode 100644
index 0000000..6cc72d7
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/IconInfo.java
@@ -0,0 +1,64 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Locale;
+
+import static com.android.server.wifi.anqp.Constants.SHORT_MASK;
+
+/**
+ * The Icons available OSU Providers sub field, as specified in
+ * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
+ * section 4.8.1.4
+ */
+public class IconInfo {
+ private final int mWidth;
+ private final int mHeight;
+ private final Locale mLocale;
+ private final String mIconType;
+ private final String mFileName;
+
+ public IconInfo(ByteBuffer payload) throws ProtocolException {
+ if (payload.remaining() < 9) {
+ throw new ProtocolException("Truncated icon meta data");
+ }
+
+ mWidth = payload.getShort() & SHORT_MASK;
+ mHeight = payload.getShort() & SHORT_MASK;
+ mLocale = Locale.forLanguageTag(Constants.getString(payload, 3, StandardCharsets.US_ASCII));
+ mIconType = Constants.getPrefixedString(payload, 1, StandardCharsets.US_ASCII);
+ mFileName = Constants.getPrefixedString(payload, 1, StandardCharsets.UTF_8);
+ }
+
+ public int getWidth() {
+ return mWidth;
+ }
+
+ public int getHeight() {
+ return mHeight;
+ }
+
+ public Locale getLocale() {
+ return mLocale;
+ }
+
+ public String getIconType() {
+ return mIconType;
+ }
+
+ public String getFileName() {
+ return mFileName;
+ }
+
+ @Override
+ public String toString() {
+ return "IconInfo{" +
+ "mWidth=" + mWidth +
+ ", mHeight=" + mHeight +
+ ", mLocale=" + mLocale +
+ ", mIconType='" + mIconType + '\'' +
+ ", mFileName='" + mFileName + '\'' +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/NAIRealmData.java b/service/java/com/android/server/wifi/anqp/NAIRealmData.java
new file mode 100644
index 0000000..36323f7
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/NAIRealmData.java
@@ -0,0 +1,108 @@
+package com.android.server.wifi.anqp;
+
+import com.android.server.wifi.anqp.eap.EAPMethod;
+import com.android.server.wifi.hotspot2.AuthMatch;
+import com.android.server.wifi.hotspot2.Utils;
+import com.android.server.wifi.hotspot2.pps.Credential;
+import com.android.server.wifi.hotspot2.pps.DomainMatcher;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The NAI Realm Data ANQP sub-element, IEEE802.11-2012 section 8.4.4.10 figure 8-418
+ */
+public class NAIRealmData {
+ private final List<String> mRealms;
+ private final List<EAPMethod> mEAPMethods;
+
+ public NAIRealmData(ByteBuffer payload) throws ProtocolException {
+ if (payload.remaining() < 5) {
+ throw new ProtocolException("Runt payload: " + payload.remaining());
+ }
+
+ int length = payload.getShort() & Constants.SHORT_MASK;
+ if (length > payload.remaining()) {
+ throw new ProtocolException("Invalid data length: " + length);
+ }
+ boolean utf8 = (payload.get() & 1) == Constants.UTF8_INDICATOR;
+
+ String realm = Constants.getPrefixedString(payload, 1, utf8 ?
+ StandardCharsets.UTF_8 :
+ StandardCharsets.US_ASCII);
+ String[] realms = realm.split(";");
+ mRealms = new ArrayList<>();
+ for (String realmElement : realms) {
+ if (realmElement.length() > 0) {
+ mRealms.add(realmElement);
+ }
+ }
+
+ int methodCount = payload.get() & Constants.BYTE_MASK;
+ mEAPMethods = new ArrayList<>(methodCount);
+ while (methodCount > 0) {
+ mEAPMethods.add(new EAPMethod(payload));
+ methodCount--;
+ }
+ }
+
+ public List<String> getRealms() {
+ return Collections.unmodifiableList(mRealms);
+ }
+
+ public List<EAPMethod> getEAPMethods() {
+ return Collections.unmodifiableList(mEAPMethods);
+ }
+
+ public int match(List<String> credLabels, Credential credential) {
+ int realmMatch = AuthMatch.None;
+ if (!mRealms.isEmpty()) {
+ for (String realm : mRealms) {
+ List<String> labels = Utils.splitDomain(realm);
+ if (DomainMatcher.arg2SubdomainOfArg1(credLabels, labels)) {
+ realmMatch = AuthMatch.Realm;
+ break;
+ }
+ }
+ if (realmMatch == AuthMatch.None || mEAPMethods.isEmpty()) {
+ return realmMatch;
+ }
+ // else there is a realm match and one or more EAP methods - check them.
+ }
+ else if (mEAPMethods.isEmpty()) {
+ return AuthMatch.Indeterminate;
+ }
+
+ int best = AuthMatch.None;
+ for (EAPMethod eapMethod : mEAPMethods) {
+ int match = eapMethod.match(credential) | realmMatch;
+ if (match > best) {
+ best = match;
+ if (best == AuthMatch.Exact) {
+ return best;
+ }
+ }
+ }
+ return best;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(" NAI Realm(s)");
+ for (String realm : mRealms) {
+ sb.append(' ').append(realm);
+ }
+ sb.append('\n');
+
+ for (EAPMethod eapMethod : mEAPMethods) {
+ sb.append( " " ).append(eapMethod.toString());
+ }
+ return sb.toString();
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/NAIRealmElement.java b/service/java/com/android/server/wifi/anqp/NAIRealmElement.java
new file mode 100644
index 0000000..c21dbee
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/NAIRealmElement.java
@@ -0,0 +1,76 @@
+package com.android.server.wifi.anqp;
+
+import com.android.server.wifi.SIMAccessor;
+import com.android.server.wifi.hotspot2.AuthMatch;
+import com.android.server.wifi.hotspot2.Utils;
+import com.android.server.wifi.hotspot2.pps.Credential;
+import com.android.server.wifi.hotspot2.pps.HomeSP;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static com.android.server.wifi.anqp.Constants.BYTES_IN_SHORT;
+import static com.android.server.wifi.anqp.Constants.SHORT_MASK;
+
+/**
+ * The NAI Realm ANQP Element, IEEE802.11-2012 section 8.4.4.10
+ */
+public class NAIRealmElement extends ANQPElement {
+ private final List<NAIRealmData> mRealmData;
+
+ public NAIRealmElement(Constants.ANQPElementType infoID, ByteBuffer payload)
+ throws ProtocolException {
+ super(infoID);
+
+ if (!payload.hasRemaining()) {
+ mRealmData = Collections.emptyList();
+ return;
+ }
+
+ if (payload.remaining() < BYTES_IN_SHORT) {
+ throw new ProtocolException("Runt NAI Realm: " + payload.remaining());
+ }
+
+ int count = payload.getShort() & SHORT_MASK;
+ mRealmData = new ArrayList<>(count);
+ while (count > 0) {
+ mRealmData.add(new NAIRealmData(payload));
+ count--;
+ }
+ }
+
+ public List<NAIRealmData> getRealmData() {
+ return Collections.unmodifiableList(mRealmData);
+ }
+
+ public int match(Credential credential) {
+ if (mRealmData.isEmpty())
+ return AuthMatch.Indeterminate;
+
+ List<String> credLabels = Utils.splitDomain(credential.getRealm());
+ int best = AuthMatch.None;
+ for (NAIRealmData realmData : mRealmData) {
+ int match = realmData.match(credLabels, credential);
+ if (match > best) {
+ best = match;
+ if (best == AuthMatch.Exact) {
+ return best;
+ }
+ }
+ }
+ return best;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("NAI Realm:\n");
+ for (NAIRealmData data : mRealmData) {
+ sb.append(data);
+ }
+ return sb.toString();
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/NetworkAuthenticationTypeElement.java b/service/java/com/android/server/wifi/anqp/NetworkAuthenticationTypeElement.java
new file mode 100644
index 0000000..b496e3e
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/NetworkAuthenticationTypeElement.java
@@ -0,0 +1,82 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
+
+/**
+ * The Network Authentication Type ANQP Element, IEEE802.11-2012 section 8.4.4.6
+ */
+public class NetworkAuthenticationTypeElement extends ANQPElement {
+
+ private final List<NetworkAuthentication> m_authenticationTypes;
+
+ public enum NwkAuthTypeEnum {
+ TermsAndConditions,
+ OnLineEnrollment,
+ HTTPRedirection,
+ DNSRedirection,
+ Reserved
+ }
+
+ public static class NetworkAuthentication {
+ private final NwkAuthTypeEnum m_type;
+ private final String m_url;
+
+ private NetworkAuthentication(NwkAuthTypeEnum type, String url) {
+ m_type = type;
+ m_url = url;
+ }
+
+ public NwkAuthTypeEnum getType() {
+ return m_type;
+ }
+
+ public String getURL() {
+ return m_url;
+ }
+
+ @Override
+ public String toString() {
+ return "NetworkAuthentication{" +
+ "m_type=" + m_type +
+ ", m_url='" + m_url + '\'' +
+ '}';
+ }
+ }
+
+ public NetworkAuthenticationTypeElement(Constants.ANQPElementType infoID, ByteBuffer payload)
+ throws ProtocolException {
+
+ super(infoID);
+
+ m_authenticationTypes = new ArrayList<NetworkAuthentication>();
+
+ while (payload.hasRemaining()) {
+ int typeNumber = payload.get() & BYTE_MASK;
+ NwkAuthTypeEnum type;
+ type = typeNumber >= NwkAuthTypeEnum.values().length ?
+ NwkAuthTypeEnum.Reserved :
+ NwkAuthTypeEnum.values()[typeNumber];
+
+ m_authenticationTypes.add(new NetworkAuthentication(type,
+ Constants.getPrefixedString(payload, 2, StandardCharsets.UTF_8)));
+ }
+ }
+
+ public List<NetworkAuthentication> getAuthenticationTypes() {
+ return Collections.unmodifiableList(m_authenticationTypes);
+ }
+
+ @Override
+ public String toString() {
+ return "NetworkAuthenticationType{" +
+ "m_authenticationTypes=" + m_authenticationTypes +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/OSUProvider.java b/service/java/com/android/server/wifi/anqp/OSUProvider.java
new file mode 100644
index 0000000..422343d
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/OSUProvider.java
@@ -0,0 +1,118 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
+import static com.android.server.wifi.anqp.Constants.SHORT_MASK;
+
+/**
+ * An OSU Provider, as specified in
+ * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
+ * section 4.8.1
+ */
+public class OSUProvider {
+
+ public enum OSUMethod {OmaDm, SoapXml}
+
+ private final List<I18Name> mNames;
+ private final String mOSUServer;
+ private final List<OSUMethod> mOSUMethods;
+ private final List<IconInfo> mIcons;
+ private final String mOsuNai;
+ private final List<I18Name> mServiceDescriptions;
+
+ public OSUProvider(ByteBuffer payload) throws ProtocolException {
+ if (payload.remaining() < 11) {
+ throw new ProtocolException("Truncated OSU provider: " + payload.remaining());
+ }
+
+ int length = payload.getShort() & SHORT_MASK;
+ int namesLength = payload.getShort() & SHORT_MASK;
+
+ ByteBuffer namesBuffer = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN);
+ namesBuffer.limit(namesBuffer.position() + namesLength);
+ payload.position(payload.position() + namesLength);
+
+ mNames = new ArrayList<I18Name>();
+
+ while (namesBuffer.hasRemaining()) {
+ mNames.add(new I18Name(namesBuffer));
+ }
+
+ mOSUServer = Constants.getPrefixedString(payload, 1, StandardCharsets.UTF_8);
+ int methodLength = payload.get() & BYTE_MASK;
+ mOSUMethods = new ArrayList<OSUMethod>(methodLength);
+ while (methodLength > 0) {
+ int methodID = payload.get() & BYTE_MASK;
+ mOSUMethods.add(methodID < OSUMethod.values().length ?
+ OSUMethod.values()[methodID] :
+ null);
+ methodLength--;
+ }
+
+ int iconsLength = payload.getShort() & SHORT_MASK;
+ ByteBuffer iconsBuffer = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN);
+ iconsBuffer.limit(iconsBuffer.position() + iconsLength);
+ payload.position(payload.position() + iconsLength);
+
+ mIcons = new ArrayList<IconInfo>();
+
+ while (iconsBuffer.hasRemaining()) {
+ mIcons.add(new IconInfo(iconsBuffer));
+ }
+
+ mOsuNai = Constants.getPrefixedString(payload, 1, StandardCharsets.UTF_8, true);
+
+ int descriptionsLength = payload.getShort() & SHORT_MASK;
+ ByteBuffer descriptionsBuffer = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN);
+ descriptionsBuffer.limit(descriptionsBuffer.position() + descriptionsLength);
+ payload.position(payload.position() + descriptionsLength);
+
+ mServiceDescriptions = new ArrayList<I18Name>();
+
+ while (descriptionsBuffer.hasRemaining()) {
+ mServiceDescriptions.add(new I18Name(descriptionsBuffer));
+ }
+ }
+
+ public List<I18Name> getNames() {
+ return mNames;
+ }
+
+ public String getOSUServer() {
+ return mOSUServer;
+ }
+
+ public List<OSUMethod> getOSUMethods() {
+ return mOSUMethods;
+ }
+
+ public List<IconInfo> getIcons() {
+ return mIcons;
+ }
+
+ public String getOsuNai() {
+ return mOsuNai;
+ }
+
+ public List<I18Name> getServiceDescriptions() {
+ return mServiceDescriptions;
+ }
+
+ @Override
+ public String toString() {
+ return "OSUProvider{" +
+ "mNames=" + mNames +
+ ", mOSUServer='" + mOSUServer + '\'' +
+ ", mOSUMethods=" + mOSUMethods +
+ ", mIcons=" + mIcons +
+ ", mOsuNai='" + mOsuNai + '\'' +
+ ", mServiceDescriptions=" + mServiceDescriptions +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/RoamingConsortiumElement.java b/service/java/com/android/server/wifi/anqp/RoamingConsortiumElement.java
new file mode 100644
index 0000000..80d9b72
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/RoamingConsortiumElement.java
@@ -0,0 +1,45 @@
+package com.android.server.wifi.anqp;
+
+import com.android.server.wifi.hotspot2.Utils;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
+import static com.android.server.wifi.anqp.Constants.getInteger;
+
+/**
+ * The Roaming Consortium ANQP Element, IEEE802.11-2012 section 8.4.4.7
+ */
+public class RoamingConsortiumElement extends ANQPElement {
+
+ private final List<Long> mOis;
+
+ public RoamingConsortiumElement(Constants.ANQPElementType infoID, ByteBuffer payload)
+ throws ProtocolException {
+ super(infoID);
+
+ mOis = new ArrayList<Long>();
+
+ while (payload.hasRemaining()) {
+ int length = payload.get() & BYTE_MASK;
+ if (length > payload.remaining()) {
+ throw new ProtocolException("Bad OI length: " + length);
+ }
+ mOis.add(getInteger(payload, ByteOrder.BIG_ENDIAN, length));
+ }
+ }
+
+ public List<Long> getOIs() {
+ return Collections.unmodifiableList(mOis);
+ }
+
+ @Override
+ public String toString() {
+ return "RoamingConsortium{mOis=[" + Utils.roamingConsortiumsToString(mOis) + "]}";
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/TestDriver.java b/service/java/com/android/server/wifi/anqp/TestDriver.java
new file mode 100644
index 0000000..b6be0e4
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/TestDriver.java
@@ -0,0 +1,168 @@
+package com.android.server.wifi.anqp;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Test ANQP code by talking to an ANQP server of a socket.
+ */
+public class TestDriver {
+
+ private static final Constants.ANQPElementType[] QueryElements = {
+ Constants.ANQPElementType.ANQPCapabilityList,
+ Constants.ANQPElementType.ANQPVenueName,
+ Constants.ANQPElementType.ANQPEmergencyNumber,
+ Constants.ANQPElementType.ANQPNwkAuthType,
+ Constants.ANQPElementType.ANQPRoamingConsortium,
+ Constants.ANQPElementType.ANQPIPAddrAvailability,
+ Constants.ANQPElementType.ANQPNAIRealm,
+ Constants.ANQPElementType.ANQP3GPPNetwork,
+ Constants.ANQPElementType.ANQPGeoLoc,
+ Constants.ANQPElementType.ANQPCivicLoc,
+ Constants.ANQPElementType.ANQPLocURI,
+ Constants.ANQPElementType.ANQPDomName,
+ Constants.ANQPElementType.ANQPEmergencyAlert,
+ Constants.ANQPElementType.ANQPTDLSCap,
+ Constants.ANQPElementType.ANQPEmergencyNAI,
+ Constants.ANQPElementType.ANQPNeighborReport,
+
+ Constants.ANQPElementType.HSCapabilityList,
+ Constants.ANQPElementType.HSFriendlyName,
+ Constants.ANQPElementType.HSWANMetrics,
+ Constants.ANQPElementType.HSConnCapability,
+ Constants.ANQPElementType.HSNAIHomeRealmQuery,
+ Constants.ANQPElementType.HSOperatingclass,
+ Constants.ANQPElementType.HSOSUProviders
+ };
+
+ public static void runTest() throws IOException {
+
+ Set<Constants.ANQPElementType> elements =
+ new HashSet<Constants.ANQPElementType>(QueryElements.length);
+ elements.addAll(Arrays.asList(QueryElements));
+
+ ByteBuffer request = ByteBuffer.allocate(8192);
+ request.order(ByteOrder.LITTLE_ENDIAN);
+ int lenPos = request.position();
+ request.putShort((short) 0);
+ ANQPFactory.buildQueryRequest(elements, request);
+
+ byte[] requestBytes = prepRequest(lenPos, request);
+
+ System.out.println( "Connecting...");
+ Socket sock = new Socket(InetAddress.getLoopbackAddress(), 6104);
+ BufferedOutputStream out = new BufferedOutputStream(sock.getOutputStream());
+ System.out.println(" ### Querying for " + Arrays.toString(QueryElements));
+ out.write(requestBytes);
+ out.flush();
+
+ BufferedInputStream in = new BufferedInputStream(sock.getInputStream());
+ ByteBuffer payload = getResponse(in);
+
+ HSOsuProvidersElement osuProvidersElement = null;
+ List<ANQPElement> anqpResult = ANQPFactory.parsePayload(payload);
+ for ( ANQPElement element : anqpResult ) {
+ System.out.println( element );
+ if (element.getID() == Constants.ANQPElementType.HSOSUProviders) {
+ osuProvidersElement = (HSOsuProvidersElement)element;
+ }
+ }
+
+ if ( osuProvidersElement != null ) {
+ for (OSUProvider provider : osuProvidersElement.getProviders()) {
+ for (IconInfo iconInfo : provider.getIcons()) {
+ sendIconRequest(iconInfo.getFileName());
+ }
+ }
+ }
+ sendIconRequest("doesNotExist.noimg");
+
+ sendHomeRealmQuery("nxdomain.abc", "jan.com");
+ }
+
+ private static void sendIconRequest(String fileName) throws IOException {
+ ByteBuffer iconRequest = ByteBuffer.allocate(fileName.length()*2)
+ .order(ByteOrder.LITTLE_ENDIAN);
+ int iconPos = iconRequest.position();
+ iconRequest.putShort((short) 0);
+ ANQPFactory.buildIconRequest(fileName, iconRequest);
+ byte[] iconBytes = prepRequest(iconPos, iconRequest);
+
+ System.out.println( "Connecting...");
+ Socket sock = new Socket(InetAddress.getLoopbackAddress(), 6104);
+ BufferedOutputStream out = new BufferedOutputStream(sock.getOutputStream());
+
+ System.out.println(" ### Requesting icon '" + fileName + "'");
+ out.write(iconBytes);
+ out.flush();
+
+ BufferedInputStream in = new BufferedInputStream(sock.getInputStream());
+ ByteBuffer payload = getResponse(in);
+ List<ANQPElement> anqpResult = ANQPFactory.parsePayload(payload);
+ System.out.println("Icon: " + anqpResult );
+ }
+
+ private static void sendHomeRealmQuery(String ... realms) throws IOException{
+ ByteBuffer request = ByteBuffer.allocate(1024).order(ByteOrder.LITTLE_ENDIAN);
+ int iconPos = request.position();
+ request.putShort((short) 0);
+ ANQPFactory.buildHomeRealmRequest(Arrays.asList(realms), request);
+ byte[] iconBytes = prepRequest(iconPos, request);
+
+ System.out.println( "Connecting...");
+ Socket sock = new Socket(InetAddress.getLoopbackAddress(), 6104);
+ BufferedOutputStream out = new BufferedOutputStream(sock.getOutputStream());
+
+ System.out.println(" ### Home realm query for " + Arrays.toString(realms));
+ out.write(iconBytes);
+ out.flush();
+
+ BufferedInputStream in = new BufferedInputStream(sock.getInputStream());
+ ByteBuffer payload = getResponse(in);
+ List<ANQPElement> anqpResult = ANQPFactory.parsePayload(payload);
+ System.out.println("Home realm query: " + anqpResult );
+ }
+
+ private static ByteBuffer getResponse(InputStream in) throws IOException {
+ ByteBuffer lengthBuffer = read( in, 2 );
+ int length = lengthBuffer.getShort() & Constants.SHORT_MASK;
+ System.out.println("Length " + length);
+
+ return read(in, length);
+ }
+
+ private static byte[] prepRequest(int pos0, ByteBuffer request) {
+ request.putShort(pos0, (short)( request.limit() - pos0 - Constants.BYTES_IN_SHORT ));
+ byte[] octets = new byte[request.remaining()];
+ request.get(octets);
+ return octets;
+ }
+
+ private static ByteBuffer read(InputStream in, int length) throws IOException {
+ byte[] payload = new byte[length];
+ int position = 0;
+ while ( position < length ) {
+ int amount = in.read(payload, position, length - position);
+ if ( amount <= 0 ) {
+ throw new EOFException("Got " + amount);
+ }
+ position += amount;
+ }
+ return ByteBuffer.wrap(payload).order(ByteOrder.LITTLE_ENDIAN);
+ }
+
+ public static void main(String[] args) throws IOException {
+ runTest();
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/ThreeGPPNetworkElement.java b/service/java/com/android/server/wifi/anqp/ThreeGPPNetworkElement.java
new file mode 100644
index 0000000..083d280
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/ThreeGPPNetworkElement.java
@@ -0,0 +1,53 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
+
+
+/**
+ * The 3GPP Cellular Network ANQP Element, IEEE802.11-2012 section 8.4.4.11
+ */
+public class ThreeGPPNetworkElement extends ANQPElement {
+ private final int mUserData;
+ private final List<CellularNetwork> mPlmns;
+
+ public ThreeGPPNetworkElement(Constants.ANQPElementType infoID, ByteBuffer payload)
+ throws ProtocolException {
+ super(infoID);
+
+ mPlmns = new ArrayList<CellularNetwork>();
+ mUserData = payload.get() & BYTE_MASK;
+ int length = payload.get() & BYTE_MASK;
+ if (length > payload.remaining()) {
+ throw new ProtocolException("Runt payload");
+ }
+
+ while (payload.hasRemaining()) {
+ CellularNetwork network = CellularNetwork.buildCellularNetwork(payload);
+ if (network != null) {
+ mPlmns.add(network);
+ }
+ }
+ }
+
+ public int getUserData() {
+ return mUserData;
+ }
+
+ public List<CellularNetwork> getPlmns() {
+ return Collections.unmodifiableList(mPlmns);
+ }
+
+ @Override
+ public String toString() {
+ return "ThreeGPPNetwork{" +
+ "mUserData=" + mUserData +
+ ", mPlmns=" + mPlmns +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/VenueNameElement.java b/service/java/com/android/server/wifi/anqp/VenueNameElement.java
new file mode 100644
index 0000000..f944c40
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/VenueNameElement.java
@@ -0,0 +1,192 @@
+package com.android.server.wifi.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The Venue Name ANQP Element, IEEE802.11-2012 section 8.4.4.4
+ */
+public class VenueNameElement extends ANQPElement {
+ private final VenueGroup mGroup;
+ private final VenueType mType;
+ private final List<I18Name> mNames;
+
+ private static final Map<VenueGroup, Integer> sGroupBases =
+ new EnumMap<VenueGroup, Integer>(VenueGroup.class);
+
+ public VenueNameElement(Constants.ANQPElementType infoID, ByteBuffer payload)
+ throws ProtocolException {
+ super(infoID);
+
+ if (payload.remaining() < 2)
+ throw new ProtocolException("Runt Venue Name");
+
+ int group = payload.get() & Constants.BYTE_MASK;
+ int type = payload.get() & Constants.BYTE_MASK;
+
+ if (group >= VenueGroup.Reserved.ordinal()) {
+ mGroup = VenueGroup.Reserved;
+ mType = VenueType.Reserved;
+ } else {
+ mGroup = VenueGroup.values()[group];
+ type += sGroupBases.get(mGroup);
+ if (type >= VenueType.Reserved.ordinal()) {
+ mType = VenueType.Reserved;
+ } else {
+ mType = VenueType.values()[type];
+ }
+ }
+
+ mNames = new ArrayList<I18Name>();
+ while (payload.hasRemaining()) {
+ mNames.add(new I18Name(payload));
+ }
+ }
+
+ public VenueGroup getGroup() {
+ return mGroup;
+ }
+
+ public VenueType getType() {
+ return mType;
+ }
+
+ public List<I18Name> getNames() {
+ return Collections.unmodifiableList(mNames);
+ }
+
+ @Override
+ public String toString() {
+ return "VenueName{" +
+ "m_group=" + mGroup +
+ ", m_type=" + mType +
+ ", m_names=" + mNames +
+ '}';
+ }
+
+ public enum VenueGroup {
+ Unspecified,
+ Assembly,
+ Business,
+ Educational,
+ FactoryIndustrial,
+ Institutional,
+ Mercantile,
+ Residential,
+ Storage,
+ UtilityMiscellaneous,
+ Vehicular,
+ Outdoor,
+ Reserved // Note: this must be the last enum constant
+ }
+
+ public enum VenueType {
+ Unspecified,
+
+ UnspecifiedAssembly,
+ Arena,
+ Stadium,
+ PassengerTerminal,
+ Amphitheater,
+ AmusementPark,
+ PlaceOfWorship,
+ ConventionCenter,
+ Library,
+ Museum,
+ Restaurant,
+ Theater,
+ Bar,
+ CoffeeShop,
+ ZooOrAquarium,
+ EmergencyCoordinationCenter,
+
+ UnspecifiedBusiness,
+ DoctorDentistoffice,
+ Bank,
+ FireStation,
+ PoliceStation,
+ PostOffice,
+ ProfessionalOffice,
+ ResearchDevelopmentFacility,
+ AttorneyOffice,
+
+ UnspecifiedEducational,
+ SchoolPrimary,
+ SchoolSecondary,
+ UniversityCollege,
+
+ UnspecifiedFactoryIndustrial,
+ Factory,
+
+ UnspecifiedInstitutional,
+ Hospital,
+ LongTermCareFacility,
+ AlcoholAndDrugRehabilitationCenter,
+ GroupHome,
+ PrisonJail,
+
+ UnspecifiedMercantile,
+ RetailStore,
+ GroceryMarket,
+ AutomotiveServiceStation,
+ ShoppingMall,
+ GasStation,
+
+ UnspecifiedResidential,
+ PrivateResidence,
+ HotelMotel,
+ Dormitory,
+ BoardingHouse,
+
+ UnspecifiedStorage,
+
+ UnspecifiedUtilityMiscellaneous,
+
+ UnspecifiedVehicular,
+ AutomobileOrTruck,
+ Airplane,
+ Bus,
+ Ferry,
+ ShipOrBoat,
+ Train,
+ MotorBike,
+
+ UnspecifiedOutdoor,
+ MuniMeshNetwork,
+ CityPark,
+ RestArea,
+ TrafficControl,
+ BusStop,
+ Kiosk,
+
+ Reserved // Note: this must be the last enum constant
+ }
+
+ private static final VenueType[] PerGroup =
+ {
+ VenueType.Unspecified,
+ VenueType.UnspecifiedAssembly,
+ VenueType.UnspecifiedBusiness,
+ VenueType.UnspecifiedEducational,
+ VenueType.UnspecifiedFactoryIndustrial,
+ VenueType.UnspecifiedInstitutional,
+ VenueType.UnspecifiedMercantile,
+ VenueType.UnspecifiedResidential,
+ VenueType.UnspecifiedStorage,
+ VenueType.UnspecifiedUtilityMiscellaneous,
+ VenueType.UnspecifiedVehicular,
+ VenueType.UnspecifiedOutdoor
+ };
+
+ static {
+ int index = 0;
+ for (VenueType venue : PerGroup) {
+ sGroupBases.put(VenueGroup.values()[index++], venue.ordinal());
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/eap/AuthParam.java b/service/java/com/android/server/wifi/anqp/eap/AuthParam.java
new file mode 100644
index 0000000..f7c877a
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/eap/AuthParam.java
@@ -0,0 +1,9 @@
+package com.android.server.wifi.anqp.eap;
+
+/**
+ * An Authentication parameter, part of the NAI Realm ANQP element, specified in
+ * IEEE802.11-2012 section 8.4.4.10, table 8-188
+ */
+public interface AuthParam {
+ public EAP.AuthInfoID getAuthInfoID();
+}
diff --git a/service/java/com/android/server/wifi/anqp/eap/Credential.java b/service/java/com/android/server/wifi/anqp/eap/Credential.java
new file mode 100644
index 0000000..d3aca07
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/eap/Credential.java
@@ -0,0 +1,72 @@
+package com.android.server.wifi.anqp.eap;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+
+import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
+
+/**
+ * An EAP authentication parameter, IEEE802.11-2012, table 8-188
+ */
+public class Credential implements AuthParam {
+
+ public enum CredType {
+ Reserved,
+ SIM,
+ USIM,
+ NFC,
+ HWToken,
+ Softoken,
+ Certificate,
+ Username,
+ None,
+ Anonymous,
+ VendorSpecific}
+
+ private final EAP.AuthInfoID mAuthInfoID;
+ private final CredType mCredType;
+
+ public Credential(EAP.AuthInfoID infoID, int length, ByteBuffer payload)
+ throws ProtocolException {
+ if (length != 1) {
+ throw new ProtocolException("Bad length: " + length);
+ }
+
+ mAuthInfoID = infoID;
+ int typeID = payload.get() & BYTE_MASK;
+
+ mCredType = typeID < CredType.values().length ?
+ CredType.values()[typeID] :
+ CredType.Reserved;
+ }
+
+ @Override
+ public EAP.AuthInfoID getAuthInfoID() {
+ return mAuthInfoID;
+ }
+
+ @Override
+ public int hashCode() {
+ return mAuthInfoID.hashCode() * 31 + mCredType.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (thatObject == this) {
+ return true;
+ } else if (thatObject == null || thatObject.getClass() != Credential.class) {
+ return false;
+ } else {
+ return ((Credential) thatObject).getCredType() == getCredType();
+ }
+ }
+
+ public CredType getCredType() {
+ return mCredType;
+ }
+
+ @Override
+ public String toString() {
+ return "Auth method " + mAuthInfoID + " = " + mCredType + "\n";
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/eap/EAP.java b/service/java/com/android/server/wifi/anqp/eap/EAP.java
new file mode 100644
index 0000000..bdb88e4
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/eap/EAP.java
@@ -0,0 +1,155 @@
+package com.android.server.wifi.anqp.eap;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * EAP Related constants for the ANQP NAIRealm element, IEEE802.11-2012 section 8.4.4.10
+ */
+public abstract class EAP {
+
+ private static final Map<Integer, EAPMethodID> sEapIds = new HashMap<>();
+ private static final Map<EAPMethodID, Integer> sRevEapIds = new HashMap<>();
+ private static final Map<Integer, AuthInfoID> sAuthIds = new HashMap<>();
+
+ public static final int EAP_MD5 = 4;
+ public static final int EAP_OTP = 5;
+ public static final int EAP_RSA = 9;
+ public static final int EAP_KEA = 11;
+ public static final int EAP_KEA_VALIDATE = 12;
+ public static final int EAP_TLS = 13;
+ public static final int EAP_LEAP = 17;
+ public static final int EAP_SIM = 18;
+ public static final int EAP_TTLS = 21;
+ public static final int EAP_AKA = 23;
+ public static final int EAP_3Com = 24;
+ public static final int EAP_MSCHAPv2 = 26;
+ public static final int EAP_PEAP = 29;
+ public static final int EAP_POTP = 32;
+ public static final int EAP_ActiontecWireless = 35;
+ public static final int EAP_HTTPDigest = 38;
+ public static final int EAP_SPEKE = 41;
+ public static final int EAP_MOBAC = 42;
+ public static final int EAP_FAST = 43;
+ public static final int EAP_ZLXEAP = 44;
+ public static final int EAP_Link = 45;
+ public static final int EAP_PAX = 46;
+ public static final int EAP_PSK = 47;
+ public static final int EAP_SAKE = 48;
+ public static final int EAP_IKEv2 = 49;
+ public static final int EAP_AKAPrim = 50;
+ public static final int EAP_GPSK = 51;
+ public static final int EAP_PWD = 52;
+ public static final int EAP_EKE = 53;
+ public static final int EAP_TEAP = 55;
+
+ public enum EAPMethodID {
+ EAP_MD5,
+ EAP_OTP,
+ EAP_RSA,
+ EAP_KEA,
+ EAP_KEA_VALIDATE,
+ EAP_TLS,
+ EAP_LEAP,
+ EAP_SIM,
+ EAP_TTLS,
+ EAP_AKA,
+ EAP_3Com,
+ EAP_MSCHAPv2,
+ EAP_PEAP,
+ EAP_POTP,
+ EAP_ActiontecWireless,
+ EAP_HTTPDigest,
+ EAP_SPEKE,
+ EAP_MOBAC,
+ EAP_FAST,
+ EAP_ZLXEAP,
+ EAP_Link,
+ EAP_PAX,
+ EAP_PSK,
+ EAP_SAKE,
+ EAP_IKEv2,
+ EAP_AKAPrim,
+ EAP_GPSK,
+ EAP_PWD,
+ EAP_EKE,
+ EAP_TEAP
+ }
+
+ public static final int ExpandedEAPMethod = 1;
+ public static final int NonEAPInnerAuthType = 2;
+ public static final int InnerAuthEAPMethodType = 3;
+ public static final int ExpandedInnerEAPMethod = 4;
+ public static final int CredentialType = 5;
+ public static final int TunneledEAPMethodCredType = 6;
+ public static final int VendorSpecific = 221;
+
+ public enum AuthInfoID {
+ Undefined,
+ ExpandedEAPMethod,
+ NonEAPInnerAuthType,
+ InnerAuthEAPMethodType,
+ ExpandedInnerEAPMethod,
+ CredentialType,
+ TunneledEAPMethodCredType,
+ VendorSpecific
+ }
+
+ static {
+ sEapIds.put(EAP_MD5, EAPMethodID.EAP_MD5);
+ sEapIds.put(EAP_OTP, EAPMethodID.EAP_OTP);
+ sEapIds.put(EAP_RSA, EAPMethodID.EAP_RSA);
+ sEapIds.put(EAP_KEA, EAPMethodID.EAP_KEA);
+ sEapIds.put(EAP_KEA_VALIDATE, EAPMethodID.EAP_KEA_VALIDATE);
+ sEapIds.put(EAP_TLS, EAPMethodID.EAP_TLS);
+ sEapIds.put(EAP_LEAP, EAPMethodID.EAP_LEAP);
+ sEapIds.put(EAP_SIM, EAPMethodID.EAP_SIM);
+ sEapIds.put(EAP_TTLS, EAPMethodID.EAP_TTLS);
+ sEapIds.put(EAP_AKA, EAPMethodID.EAP_AKA);
+ sEapIds.put(EAP_3Com, EAPMethodID.EAP_3Com);
+ sEapIds.put(EAP_MSCHAPv2, EAPMethodID.EAP_MSCHAPv2);
+ sEapIds.put(EAP_PEAP, EAPMethodID.EAP_PEAP);
+ sEapIds.put(EAP_POTP, EAPMethodID.EAP_POTP);
+ sEapIds.put(EAP_ActiontecWireless, EAPMethodID.EAP_ActiontecWireless);
+ sEapIds.put(EAP_HTTPDigest, EAPMethodID.EAP_HTTPDigest);
+ sEapIds.put(EAP_SPEKE, EAPMethodID.EAP_SPEKE);
+ sEapIds.put(EAP_MOBAC, EAPMethodID.EAP_MOBAC);
+ sEapIds.put(EAP_FAST, EAPMethodID.EAP_FAST);
+ sEapIds.put(EAP_ZLXEAP, EAPMethodID.EAP_ZLXEAP);
+ sEapIds.put(EAP_Link, EAPMethodID.EAP_Link);
+ sEapIds.put(EAP_PAX, EAPMethodID.EAP_PAX);
+ sEapIds.put(EAP_PSK, EAPMethodID.EAP_PSK);
+ sEapIds.put(EAP_SAKE, EAPMethodID.EAP_SAKE);
+ sEapIds.put(EAP_IKEv2, EAPMethodID.EAP_IKEv2);
+ sEapIds.put(EAP_AKAPrim, EAPMethodID.EAP_AKAPrim);
+ sEapIds.put(EAP_GPSK, EAPMethodID.EAP_GPSK);
+ sEapIds.put(EAP_PWD, EAPMethodID.EAP_PWD);
+ sEapIds.put(EAP_EKE, EAPMethodID.EAP_EKE);
+ sEapIds.put(EAP_TEAP, EAPMethodID.EAP_TEAP);
+
+ for (Map.Entry<Integer, EAPMethodID> entry : sEapIds.entrySet()) {
+ sRevEapIds.put(entry.getValue(), entry.getKey());
+ }
+
+ sAuthIds.put(ExpandedEAPMethod, AuthInfoID.ExpandedEAPMethod);
+ sAuthIds.put(NonEAPInnerAuthType, AuthInfoID.NonEAPInnerAuthType);
+ sAuthIds.put(InnerAuthEAPMethodType, AuthInfoID.InnerAuthEAPMethodType);
+ sAuthIds.put(ExpandedInnerEAPMethod, AuthInfoID.ExpandedInnerEAPMethod);
+ sAuthIds.put(CredentialType, AuthInfoID.CredentialType);
+ sAuthIds.put(TunneledEAPMethodCredType, AuthInfoID.TunneledEAPMethodCredType);
+ sAuthIds.put(VendorSpecific, AuthInfoID.VendorSpecific);
+ }
+
+ public static EAPMethodID mapEAPMethod(int methodID) {
+ return sEapIds.get(methodID);
+ }
+
+ public static Integer mapEAPMethod(EAPMethodID methodID) {
+ return sRevEapIds.get(methodID);
+ }
+
+ public static AuthInfoID mapAuthMethod(int methodID) {
+ return sAuthIds.get(methodID);
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/eap/EAPMethod.java b/service/java/com/android/server/wifi/anqp/eap/EAPMethod.java
new file mode 100644
index 0000000..97c53a4
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/eap/EAPMethod.java
@@ -0,0 +1,192 @@
+package com.android.server.wifi.anqp.eap;
+
+
+import com.android.server.wifi.anqp.Constants;
+import com.android.server.wifi.hotspot2.AuthMatch;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * An EAP Method, part of the NAI Realm ANQP element, specified in
+ * IEEE802.11-2012 section 8.4.4.10, figure 8-420
+ */
+public class EAPMethod {
+ private final EAP.EAPMethodID mEAPMethodID;
+ private final Map<EAP.AuthInfoID, Set<AuthParam>> mAuthParams;
+
+ public EAPMethod(ByteBuffer payload) throws ProtocolException {
+ if (payload.remaining() < 3) {
+ throw new ProtocolException("Runt EAP Method: " + payload.remaining());
+ }
+
+ int length = payload.get() & Constants.BYTE_MASK;
+ int methodID = payload.get() & Constants.BYTE_MASK;
+ int count = payload.get() & Constants.BYTE_MASK;
+
+ mEAPMethodID = EAP.mapEAPMethod(methodID);
+ mAuthParams = new EnumMap<>(EAP.AuthInfoID.class);
+
+ int realCount = 0;
+
+ ByteBuffer paramPayload = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN);
+ paramPayload.limit(paramPayload.position() + length - 2);
+ payload.position(payload.position() + length - 2);
+ while (paramPayload.hasRemaining()) {
+ int id = paramPayload.get() & Constants.BYTE_MASK;
+
+ EAP.AuthInfoID authInfoID = EAP.mapAuthMethod(id);
+ if (authInfoID == null) {
+ throw new ProtocolException("Unknown auth parameter ID: " + id);
+ }
+
+ int len = paramPayload.get() & Constants.BYTE_MASK;
+ if (len == 0 || len > paramPayload.remaining()) {
+ throw new ProtocolException("Bad auth method length: " + len);
+ }
+
+ switch (authInfoID) {
+ case ExpandedEAPMethod:
+ addAuthParam(new ExpandedEAPMethod(authInfoID, len, paramPayload));
+ break;
+ case NonEAPInnerAuthType:
+ addAuthParam(new NonEAPInnerAuth(len, paramPayload));
+ break;
+ case InnerAuthEAPMethodType:
+ addAuthParam(new InnerAuthEAP(len, paramPayload));
+ break;
+ case ExpandedInnerEAPMethod:
+ addAuthParam(new ExpandedEAPMethod(authInfoID, len, paramPayload));
+ break;
+ case CredentialType:
+ addAuthParam(new Credential(authInfoID, len, paramPayload));
+ break;
+ case TunneledEAPMethodCredType:
+ addAuthParam(new Credential(authInfoID, len, paramPayload));
+ break;
+ case VendorSpecific:
+ addAuthParam(new VendorSpecificAuth(len, paramPayload));
+ break;
+ }
+
+ realCount++;
+ }
+ if (realCount != count)
+ throw new ProtocolException("Invalid parameter count: " + realCount +
+ ", expected " + count);
+ }
+
+ public EAPMethod(EAP.EAPMethodID eapMethodID, AuthParam authParam) {
+ mEAPMethodID = eapMethodID;
+ mAuthParams = new HashMap<>(1);
+ if (authParam != null) {
+ Set<AuthParam> authParams = new HashSet<>();
+ authParams.add(authParam);
+ mAuthParams.put(authParam.getAuthInfoID(), authParams);
+ }
+ }
+
+ private void addAuthParam(AuthParam param) {
+ Set<AuthParam> authParams = mAuthParams.get(param.getAuthInfoID());
+ if (authParams == null) {
+ authParams = new HashSet<>();
+ mAuthParams.put(param.getAuthInfoID(), authParams);
+ }
+ authParams.add(param);
+ }
+
+ public Map<EAP.AuthInfoID, Set<AuthParam>> getAuthParams() {
+ return Collections.unmodifiableMap(mAuthParams);
+ }
+
+ public EAP.EAPMethodID getEAPMethodID() {
+ return mEAPMethodID;
+ }
+
+ public int match(com.android.server.wifi.hotspot2.pps.Credential credential) {
+
+ EAPMethod credMethod = credential.getEAPMethod();
+ if (mEAPMethodID != credMethod.getEAPMethodID()) {
+ return AuthMatch.None;
+ }
+
+ switch (mEAPMethodID) {
+ case EAP_TTLS:
+ if (mAuthParams.isEmpty()) {
+ return AuthMatch.Method;
+ }
+ int paramCount = 0;
+ for (Map.Entry<EAP.AuthInfoID, Set<AuthParam>> entry :
+ credMethod.getAuthParams().entrySet()) {
+ Set<AuthParam> params = mAuthParams.get(entry.getKey());
+ if (params == null) {
+ continue;
+ }
+
+ if (!Collections.disjoint(params, entry.getValue())) {
+ return AuthMatch.MethodParam;
+ }
+ paramCount += params.size();
+ }
+ return paramCount > 0 ? AuthMatch.None : AuthMatch.Method;
+ case EAP_TLS:
+ return AuthMatch.MethodParam;
+ case EAP_SIM:
+ case EAP_AKA:
+ case EAP_AKAPrim:
+ return AuthMatch.Method;
+ default:
+ return AuthMatch.Method;
+ }
+ }
+
+ public AuthParam getAuthParam() {
+ if (mAuthParams.isEmpty()) {
+ return null;
+ }
+ Set<AuthParam> params = mAuthParams.values().iterator().next();
+ if (params.isEmpty()) {
+ return null;
+ }
+ return params.iterator().next();
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ else if (thatObject == null || getClass() != thatObject.getClass()) {
+ return false;
+ }
+
+ EAPMethod that = (EAPMethod) thatObject;
+ return mEAPMethodID == that.mEAPMethodID && mAuthParams.equals(that.mAuthParams);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mEAPMethodID.hashCode();
+ result = 31 * result + mAuthParams.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("EAP Method ").append(mEAPMethodID).append('\n');
+ for (Set<AuthParam> paramSet : mAuthParams.values()) {
+ for (AuthParam param : paramSet) {
+ sb.append(" ").append(param.toString());
+ }
+ }
+ return sb.toString();
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/eap/ExpandedEAPMethod.java b/service/java/com/android/server/wifi/anqp/eap/ExpandedEAPMethod.java
new file mode 100644
index 0000000..95763a4
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/eap/ExpandedEAPMethod.java
@@ -0,0 +1,78 @@
+package com.android.server.wifi.anqp.eap;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
+import static com.android.server.wifi.anqp.Constants.INT_MASK;
+import static com.android.server.wifi.anqp.Constants.SHORT_MASK;
+
+/**
+ * An EAP authentication parameter, IEEE802.11-2012, table 8-188
+ */
+public class ExpandedEAPMethod implements AuthParam {
+
+ private final EAP.AuthInfoID mAuthInfoID;
+ private final int mVendorID;
+ private final long mVendorType;
+
+ public ExpandedEAPMethod(EAP.AuthInfoID authInfoID, int length, ByteBuffer payload)
+ throws ProtocolException {
+ if (length != 7) {
+ throw new ProtocolException("Bad length: " + payload.remaining());
+ }
+
+ mAuthInfoID = authInfoID;
+
+ ByteBuffer vndBuffer = payload.duplicate().order(ByteOrder.BIG_ENDIAN);
+
+ int id = vndBuffer.getShort() & SHORT_MASK;
+ id = (id << Byte.SIZE) | (vndBuffer.get() & BYTE_MASK);
+ mVendorID = id;
+ mVendorType = vndBuffer.getInt() & INT_MASK;
+
+ payload.position(payload.position()+7);
+ }
+
+ public ExpandedEAPMethod(EAP.AuthInfoID authInfoID, int vendorID, long vendorType) {
+ mAuthInfoID = authInfoID;
+ mVendorID = vendorID;
+ mVendorType = vendorType;
+ }
+
+ @Override
+ public EAP.AuthInfoID getAuthInfoID() {
+ return mAuthInfoID;
+ }
+
+ @Override
+ public int hashCode() {
+ return (mAuthInfoID.hashCode() * 31 + mVendorID) * 31 + (int) mVendorType;
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (thatObject == this) {
+ return true;
+ } else if (thatObject == null || thatObject.getClass() != ExpandedEAPMethod.class) {
+ return false;
+ } else {
+ ExpandedEAPMethod that = (ExpandedEAPMethod) thatObject;
+ return that.getVendorID() == getVendorID() && that.getVendorType() == getVendorType();
+ }
+ }
+
+ public int getVendorID() {
+ return mVendorID;
+ }
+
+ public long getVendorType() {
+ return mVendorType;
+ }
+
+ @Override
+ public String toString() {
+ return "Auth method " + mAuthInfoID + ", id " + mVendorID + ", type " + mVendorType + "\n";
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/eap/InnerAuthEAP.java b/service/java/com/android/server/wifi/anqp/eap/InnerAuthEAP.java
new file mode 100644
index 0000000..a5bc4f1
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/eap/InnerAuthEAP.java
@@ -0,0 +1,56 @@
+package com.android.server.wifi.anqp.eap;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+
+import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
+
+/**
+ * An EAP authentication parameter, IEEE802.11-2012, table 8-188
+ */
+public class InnerAuthEAP implements AuthParam {
+
+ private final EAP.EAPMethodID mEapMethodID;
+
+ public InnerAuthEAP(int length, ByteBuffer payload) throws ProtocolException {
+ if (length != 1) {
+ throw new ProtocolException("Bad length: " + length);
+ }
+ int typeID = payload.get() & BYTE_MASK;
+ mEapMethodID = EAP.mapEAPMethod(typeID);
+ }
+
+ public InnerAuthEAP(EAP.EAPMethodID eapMethodID) {
+ mEapMethodID = eapMethodID;
+ }
+
+ @Override
+ public EAP.AuthInfoID getAuthInfoID() {
+ return EAP.AuthInfoID.InnerAuthEAPMethodType;
+ }
+
+ public EAP.EAPMethodID getEAPMethodID() {
+ return mEapMethodID;
+ }
+
+ @Override
+ public int hashCode() {
+ return mEapMethodID != null ? mEapMethodID.hashCode() : 0;
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (thatObject == this) {
+ return true;
+ } else if (thatObject == null || thatObject.getClass() != InnerAuthEAP.class) {
+ return false;
+ } else {
+ return ((InnerAuthEAP) thatObject).getEAPMethodID() == getEAPMethodID();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Auth method InnerAuthEAP, inner = " + mEapMethodID + '\n';
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/eap/NonEAPInnerAuth.java b/service/java/com/android/server/wifi/anqp/eap/NonEAPInnerAuth.java
new file mode 100644
index 0000000..0e04a23
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/eap/NonEAPInnerAuth.java
@@ -0,0 +1,89 @@
+package com.android.server.wifi.anqp.eap;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
+
+/**
+ * An EAP authentication parameter, IEEE802.11-2012, table 8-188
+ */
+public class NonEAPInnerAuth implements AuthParam {
+
+ public enum NonEAPType {Reserved, PAP, CHAP, MSCHAP, MSCHAPv2}
+ private static final Map<NonEAPType, String> sOmaMap = new EnumMap<>(NonEAPType.class);
+ private static final Map<String, NonEAPType> sRevOmaMap = new HashMap<>();
+
+ private final NonEAPType mType;
+
+ static {
+ sOmaMap.put(NonEAPType.PAP, "PAP");
+ sOmaMap.put(NonEAPType.CHAP, "CHAP");
+ sOmaMap.put(NonEAPType.MSCHAP, "MS-CHAP");
+ sOmaMap.put(NonEAPType.MSCHAPv2, "MS-CHAP-V2");
+
+ for (Map.Entry<NonEAPType, String> entry : sOmaMap.entrySet()) {
+ sRevOmaMap.put(entry.getValue(), entry.getKey());
+ }
+ }
+
+ public NonEAPInnerAuth(int length, ByteBuffer payload) throws ProtocolException {
+ if (length != 1) {
+ throw new ProtocolException("Bad length: " + payload.remaining());
+ }
+
+ int typeID = payload.get() & BYTE_MASK;
+ mType = typeID < NonEAPType.values().length ?
+ NonEAPType.values()[typeID] :
+ NonEAPType.Reserved;
+ }
+
+ public NonEAPInnerAuth(NonEAPType type) {
+ mType = type;
+ }
+
+ /**
+ * Construct from the OMA-DM PPS data
+ * @param eapType as defined in the HS2.0 spec.
+ */
+ public NonEAPInnerAuth(String eapType) {
+ mType = sRevOmaMap.get(eapType);
+ }
+
+ @Override
+ public EAP.AuthInfoID getAuthInfoID() {
+ return EAP.AuthInfoID.NonEAPInnerAuthType;
+ }
+
+ public NonEAPType getType() {
+ return mType;
+ }
+
+ public String getOMAtype() {
+ return sOmaMap.get(mType);
+ }
+
+ @Override
+ public int hashCode() {
+ return mType.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (thatObject == this) {
+ return true;
+ } else if (thatObject == null || thatObject.getClass() != NonEAPInnerAuth.class) {
+ return false;
+ } else {
+ return ((NonEAPInnerAuth) thatObject).getType() == getType();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Auth method NonEAPInnerAuthEAP, inner = " + mType + '\n';
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/eap/VendorSpecificAuth.java b/service/java/com/android/server/wifi/anqp/eap/VendorSpecificAuth.java
new file mode 100644
index 0000000..1d94192
--- /dev/null
+++ b/service/java/com/android/server/wifi/anqp/eap/VendorSpecificAuth.java
@@ -0,0 +1,47 @@
+package com.android.server.wifi.anqp.eap;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * An EAP authentication parameter, IEEE802.11-2012, table 8-188
+ */
+public class VendorSpecificAuth implements AuthParam {
+
+ private final byte[] mData;
+
+ public VendorSpecificAuth(int length, ByteBuffer payload) throws ProtocolException {
+ mData = new byte[length];
+ payload.get(mData);
+ }
+
+ @Override
+ public EAP.AuthInfoID getAuthInfoID() {
+ return EAP.AuthInfoID.VendorSpecific;
+ }
+
+ public int hashCode() {
+ return Arrays.hashCode(mData);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (thatObject == this) {
+ return true;
+ } else if (thatObject == null || thatObject.getClass() != VendorSpecificAuth.class) {
+ return false;
+ } else {
+ return Arrays.equals(((VendorSpecificAuth) thatObject).getData(), getData());
+ }
+ }
+
+ public byte[] getData() {
+ return mData;
+ }
+
+ @Override
+ public String toString() {
+ return "Auth method VendorSpecificAuth, data = " + Arrays.toString(mData) + '\n';
+ }
+}
diff --git a/service/java/com/android/server/wifi/configparse/ConfigBuilder.java b/service/java/com/android/server/wifi/configparse/ConfigBuilder.java
new file mode 100644
index 0000000..e8e5e6a
--- /dev/null
+++ b/service/java/com/android/server/wifi/configparse/ConfigBuilder.java
@@ -0,0 +1,388 @@
+package com.android.server.wifi.configparse;
+
+import android.content.Context;
+import android.net.Uri;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Base64;
+import android.util.Log;
+
+import com.android.server.wifi.IMSIParameter;
+import com.android.server.wifi.anqp.eap.AuthParam;
+import com.android.server.wifi.anqp.eap.EAP;
+import com.android.server.wifi.anqp.eap.EAPMethod;
+import com.android.server.wifi.anqp.eap.NonEAPInnerAuth;
+import com.android.server.wifi.hotspot2.omadm.MOManager;
+import com.android.server.wifi.hotspot2.pps.Credential;
+import com.android.server.wifi.hotspot2.pps.HomeSP;
+
+import org.xml.sax.SAXException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.MessageDigest;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+
+public class ConfigBuilder {
+ public static final String WifiConfigType = "application/x-wifi-config";
+ private static final String ProfileTag = "application/x-passpoint-profile";
+ private static final String KeyTag = "application/x-pkcs12";
+ private static final String CATag = "application/x-x509-ca-cert";
+
+ private static final String X509 = "X.509";
+
+ private static final String TAG = "WCFG";
+
+ public static WifiConfiguration buildConfig(String uriString, byte[] data, Context context)
+ throws IOException, GeneralSecurityException, SAXException {
+ Log.d(TAG, "Content: " + (data != null ? data.length : -1));
+
+ byte[] b64 = Base64.decode(new String(data, StandardCharsets.ISO_8859_1), Base64.DEFAULT);
+ Log.d(TAG, "Decoded: " + b64.length + " bytes.");
+
+ dropFile(Uri.parse(uriString), context);
+
+ MIMEContainer mimeContainer = new
+ MIMEContainer(new LineNumberReader(
+ new InputStreamReader(new ByteArrayInputStream(b64), StandardCharsets.ISO_8859_1)),
+ null);
+ if (!mimeContainer.isBase64()) {
+ throw new IOException("Encoding for " +
+ mimeContainer.getContentType() + " is not base64");
+ }
+ MIMEContainer inner;
+ if (mimeContainer.getContentType().equals(WifiConfigType)) {
+ byte[] wrappedContent = Base64.decode(mimeContainer.getText(), Base64.DEFAULT);
+ Log.d(TAG, "Building container from '" +
+ new String(wrappedContent, StandardCharsets.ISO_8859_1) + "'");
+ inner = new MIMEContainer(new LineNumberReader(
+ new InputStreamReader(new ByteArrayInputStream(wrappedContent),
+ StandardCharsets.ISO_8859_1)), null);
+ }
+ else {
+ inner = mimeContainer;
+ }
+ return parse(inner, context);
+ }
+
+ private static void dropFile(Uri uri, Context context) {
+ context.getContentResolver().delete(uri, null, null);
+ }
+
+ private static WifiConfiguration parse(MIMEContainer root, Context context)
+ throws IOException, GeneralSecurityException, SAXException {
+
+ if (root.getMimeContainers() == null) {
+ throw new IOException("Malformed MIME content: not multipart");
+ }
+
+ String moText = null;
+ X509Certificate caCert = null;
+ PrivateKey clientKey = null;
+ List<X509Certificate> clientChain = null;
+
+ for (MIMEContainer subContainer : root.getMimeContainers()) {
+ Log.d(TAG, " + Content Type: " + subContainer.getContentType());
+ switch (subContainer.getContentType()) {
+ case ProfileTag:
+ if (subContainer.isBase64()) {
+ byte[] octets = Base64.decode(subContainer.getText(), Base64.DEFAULT);
+ moText = new String(octets, StandardCharsets.UTF_8);
+ } else {
+ moText = subContainer.getText();
+ }
+ Log.d(TAG, "OMA: " + moText);
+ break;
+ case CATag: {
+ if (!subContainer.isBase64()) {
+ throw new IOException("Can't read non base64 encoded cert");
+ }
+
+ byte[] octets = Base64.decode(subContainer.getText(), Base64.DEFAULT);
+ CertificateFactory factory = CertificateFactory.getInstance(X509);
+ caCert = (X509Certificate) factory.generateCertificate(
+ new ByteArrayInputStream(octets));
+ Log.d(TAG, "Cert subject " + caCert.getSubjectX500Principal());
+ Log.d(TAG, "Full Cert: " + caCert);
+ break;
+ }
+ case KeyTag: {
+ if (!subContainer.isBase64()) {
+ throw new IOException("Can't read non base64 encoded key");
+ }
+
+ byte[] octets = Base64.decode(subContainer.getText(), Base64.DEFAULT);
+
+ KeyStore ks = KeyStore.getInstance("PKCS12");
+ ByteArrayInputStream in = new ByteArrayInputStream(octets);
+ ks.load(in, new char[0]);
+ in.close();
+ Log.d(TAG, "---- Start PKCS12 info " + octets.length + ", size " + ks.size());
+ Enumeration<String> aliases = ks.aliases();
+ while (aliases.hasMoreElements()) {
+ String alias = aliases.nextElement();
+ clientKey = (PrivateKey) ks.getKey(alias, null);
+ Log.d(TAG, "Key: " + clientKey.getFormat());
+ Certificate[] chain = ks.getCertificateChain(alias);
+ if (chain != null) {
+ clientChain = new ArrayList<>();
+ for (Certificate certificate : chain) {
+ if (!(certificate instanceof X509Certificate)) {
+ Log.w(TAG, "Element in cert chain is not an X509Certificate: " +
+ certificate.getClass());
+ }
+ clientChain.add((X509Certificate) certificate);
+ }
+ Log.d(TAG, "Chain: " + clientChain.size());
+ }
+ }
+ Log.d(TAG, "---- End PKCS12 info.");
+ break;
+ }
+ }
+ }
+
+ if (moText == null) {
+ throw new IOException("Missing profile");
+ }
+
+ return buildConfig(moText, caCert, clientChain, clientKey, context);
+ }
+
+ private static WifiConfiguration buildConfig(String text, X509Certificate caCert,
+ List<X509Certificate> clientChain, PrivateKey key,
+ Context context)
+ throws IOException, SAXException, GeneralSecurityException {
+
+ HomeSP homeSP = MOManager.buildSP(text);
+ Credential credential = homeSP.getCredential();
+
+ WifiConfiguration config;
+
+ EAP.EAPMethodID eapMethodID = credential.getEAPMethod().getEAPMethodID();
+ switch (eapMethodID) {
+ case EAP_TTLS:
+ if (key != null || clientChain != null) {
+ Log.w(TAG, "Client cert and/or key included with EAP-TTLS profile");
+ }
+ config = buildTTLSConfig(homeSP);
+ break;
+ case EAP_TLS:
+ config = buildTLSConfig(homeSP, clientChain, key);
+ break;
+ case EAP_AKA:
+ case EAP_AKAPrim:
+ case EAP_SIM:
+ if (key != null || clientChain != null || caCert != null) {
+ Log.i(TAG, "Client/CA cert and/or key included with " +
+ eapMethodID + " profile");
+ }
+ config = buildSIMConfig(homeSP, context);
+ break;
+ default:
+ throw new IOException("Unsupported EAP Method: " + eapMethodID);
+ }
+
+ WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
+
+ enterpriseConfig.setCaCertificate(caCert);
+ enterpriseConfig.setAnonymousIdentity("anonymous@" + credential.getRealm());
+ enterpriseConfig.setRealm(credential.getRealm());
+
+ return config;
+ }
+
+ // Retain for debugging purposes
+ /*
+ private static void xIterateCerts(KeyStore ks, X509Certificate caCert)
+ throws GeneralSecurityException {
+ Enumeration<String> aliases = ks.aliases();
+ while (aliases.hasMoreElements()) {
+ String alias = aliases.nextElement();
+ Certificate cert = ks.getCertificate(alias);
+ Log.d("HS2J", "Checking " + alias);
+ if (cert instanceof X509Certificate) {
+ X509Certificate x509Certificate = (X509Certificate) cert;
+ boolean sm = x509Certificate.getSubjectX500Principal().equals(
+ caCert.getSubjectX500Principal());
+ boolean eq = false;
+ if (sm) {
+ eq = Arrays.equals(x509Certificate.getEncoded(), caCert.getEncoded());
+ }
+ Log.d("HS2J", "Subject: " + x509Certificate.getSubjectX500Principal() +
+ ": " + sm + "/" + eq);
+ }
+ }
+ }
+ */
+
+ private static WifiConfiguration buildTTLSConfig(HomeSP homeSP)
+ throws IOException {
+ Credential credential = homeSP.getCredential();
+
+ if (credential.getUserName() == null || credential.getPassword() == null) {
+ throw new IOException("EAP-TTLS provisioned without user name or password");
+ }
+
+ EAPMethod eapMethod = credential.getEAPMethod();
+
+ AuthParam authParam = eapMethod.getAuthParam();
+ if (authParam == null ||
+ authParam.getAuthInfoID() != EAP.AuthInfoID.NonEAPInnerAuthType) {
+ throw new IOException("Bad auth parameter for EAP-TTLS: " + authParam);
+ }
+
+ WifiConfiguration config = buildBaseConfiguration(homeSP);
+ NonEAPInnerAuth ttlsParam = (NonEAPInnerAuth) authParam;
+ WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
+ enterpriseConfig.setPhase2Method(remapInnerMethod(ttlsParam.getType()));
+ enterpriseConfig.setIdentity(credential.getUserName());
+ enterpriseConfig.setPassword(credential.getPassword());
+
+ return config;
+ }
+
+ private static WifiConfiguration buildTLSConfig(HomeSP homeSP,
+ List<X509Certificate> clientChain,
+ PrivateKey clientKey)
+ throws IOException, GeneralSecurityException {
+
+ Credential credential = homeSP.getCredential();
+
+ X509Certificate clientCertificate = null;
+
+ if (clientKey == null || clientChain == null) {
+ throw new IOException("No key and/or cert passed for EAP-TLS");
+ }
+ if (credential.getCertType() != Credential.CertType.x509v3) {
+ throw new IOException("Invalid certificate type for TLS: " +
+ credential.getCertType());
+ }
+
+ byte[] reference = credential.getFingerPrint();
+ MessageDigest digester = MessageDigest.getInstance("SHA-256");
+ for (X509Certificate certificate : clientChain) {
+ digester.reset();
+ byte[] fingerprint = digester.digest(certificate.getEncoded());
+ if (Arrays.equals(reference, fingerprint)) {
+ clientCertificate = certificate;
+ break;
+ }
+ }
+ if (clientCertificate == null) {
+ throw new IOException("No certificate in chain matches supplied fingerprint");
+ }
+
+ String alias = Base64.encodeToString(reference, Base64.DEFAULT);
+
+ WifiConfiguration config = buildBaseConfiguration(homeSP);
+ WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
+ enterpriseConfig.setClientCertificateAlias(alias);
+ enterpriseConfig.setClientKeyEntry(clientKey, clientCertificate);
+
+ return config;
+ }
+
+ private static WifiConfiguration buildSIMConfig(HomeSP homeSP, Context context)
+ throws IOException {
+
+ Credential credential = homeSP.getCredential();
+ IMSIParameter credImsi = credential.getImsi();
+
+ /*
+ * Uncomment to enforce strict IMSI matching with currently installed SIM cards.
+ *
+ TelephonyManager tm = TelephonyManager.from(context);
+ SubscriptionManager sub = SubscriptionManager.from(context);
+ boolean match = false;
+
+ for (int subId : sub.getActiveSubscriptionIdList()) {
+ String imsi = tm.getSubscriberId(subId);
+ if (credImsi.matches(imsi)) {
+ match = true;
+ break;
+ }
+ }
+ if (!match) {
+ throw new IOException("Supplied IMSI does not match any SIM card");
+ }
+ */
+
+ WifiConfiguration config = buildBaseConfiguration(homeSP);
+ config.enterpriseConfig.setPlmn(credImsi.toString());
+ return config;
+ }
+
+ private static WifiConfiguration buildBaseConfiguration(HomeSP homeSP) throws IOException {
+ EAP.EAPMethodID eapMethodID = homeSP.getCredential().getEAPMethod().getEAPMethodID();
+
+ WifiConfiguration config = new WifiConfiguration();
+
+ config.FQDN = homeSP.getFQDN();
+
+ HashSet<Long> roamingConsortiumIds = homeSP.getRoamingConsortiums();
+ config.roamingConsortiumIds = new long[roamingConsortiumIds.size()];
+ int i = 0;
+ for (long id : roamingConsortiumIds) {
+ config.roamingConsortiumIds[i] = id;
+ i++;
+ }
+ config.providerFriendlyName = homeSP.getFriendlyName();
+
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
+
+ WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+ enterpriseConfig.setEapMethod(remapEAPMethod(eapMethodID));
+ enterpriseConfig.setRealm(homeSP.getCredential().getRealm());
+ config.enterpriseConfig = enterpriseConfig;
+
+ return config;
+ }
+
+ private static int remapEAPMethod(EAP.EAPMethodID eapMethodID) throws IOException {
+ switch (eapMethodID) {
+ case EAP_TTLS:
+ return WifiEnterpriseConfig.Eap.TTLS;
+ case EAP_TLS:
+ return WifiEnterpriseConfig.Eap.TLS;
+ case EAP_SIM:
+ return WifiEnterpriseConfig.Eap.SIM;
+ case EAP_AKA:
+ return WifiEnterpriseConfig.Eap.AKA;
+ case EAP_AKAPrim:
+ return WifiEnterpriseConfig.Eap.AKA_PRIME;
+ default:
+ throw new IOException("Bad EAP method: " + eapMethodID);
+ }
+ }
+
+ private static int remapInnerMethod(NonEAPInnerAuth.NonEAPType type) throws IOException {
+ switch (type) {
+ case PAP:
+ return WifiEnterpriseConfig.Phase2.PAP;
+ case MSCHAP:
+ return WifiEnterpriseConfig.Phase2.MSCHAP;
+ case MSCHAPv2:
+ return WifiEnterpriseConfig.Phase2.MSCHAPV2;
+ case CHAP:
+ default:
+ throw new IOException("Inner method " + type + " not supported");
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/configparse/MIMEContainer.java b/service/java/com/android/server/wifi/configparse/MIMEContainer.java
new file mode 100644
index 0000000..10ad456
--- /dev/null
+++ b/service/java/com/android/server/wifi/configparse/MIMEContainer.java
@@ -0,0 +1,345 @@
+package com.android.server.wifi.configparse;
+
+import android.util.Log;
+
+import com.android.server.wifi.hotspot2.Utils;
+
+import java.io.IOException;
+import java.io.LineNumberReader;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class MIMEContainer {
+ private static final String Type = "Content-Type";
+ private static final String Encoding = "Content-Transfer-Encoding";
+
+ private static final String Boundary = "boundary=";
+ private static final String CharsetTag = "charset=";
+
+ private final boolean mLast;
+ private final List<MIMEContainer> mMimeContainers;
+ private final String mText;
+
+ private final boolean mMixed;
+ private final boolean mBase64;
+ private final Charset mCharset;
+ private final String mContentType;
+
+ /**
+ * Parse nested MIME content
+ * @param in A reader to read MIME data from; Note that the charset should be ISO-8859-1 to
+ * ensure transparent octet to character mapping. This is because the content will
+ * be re-encoded using the correct charset once it is discovered.
+ * @param boundary A boundary string for the MIME section that this container is in.
+ * Pass null for the top level object.
+ * @throws java.io.IOException
+ */
+ public MIMEContainer(LineNumberReader in, String boundary) throws IOException {
+ Map<String,List<String>> headers = parseHeader(in);
+
+ List<String> type = headers.get(Type);
+ if (type == null || type.isEmpty()) {
+ throw new IOException("Missing " + Type + " @ " + in.getLineNumber());
+ }
+
+ boolean multiPart = false;
+ boolean mixed = false;
+ String subBoundary = null;
+ Charset charset = StandardCharsets.ISO_8859_1;
+
+ mContentType = type.get(0);
+
+ if (mContentType.startsWith("multipart/")) {
+ multiPart = true;
+
+ for (String attribute : type) {
+ if (attribute.startsWith(Boundary)) {
+ subBoundary = Utils.unquote(attribute.substring(Boundary.length()));
+ }
+ }
+
+ if (mContentType.endsWith("/mixed")) {
+ mixed = true;
+ }
+ }
+ else if (mContentType.startsWith("text/")) {
+ for (String attribute : type) {
+ if (attribute.startsWith(CharsetTag)) {
+ charset = Charset.forName(attribute.substring(CharsetTag.length()));
+ }
+ }
+ }
+
+ mMixed = mixed;
+ mCharset = charset;
+
+ if (multiPart && subBoundary != null) {
+ for (;;) {
+ String line = in.readLine();
+ if (line == null) {
+ throw new IOException("Unexpected EOF before first boundary @ " +
+ in.getLineNumber());
+ }
+ if (line.startsWith("--") && line.length() == subBoundary.length() + 2 &&
+ line.regionMatches(2, subBoundary, 0, subBoundary.length())) {
+ break;
+ }
+ }
+
+ mMimeContainers = new ArrayList<>();
+ for (;;) {
+ MIMEContainer container = new MIMEContainer(in, subBoundary);
+ mMimeContainers.add(container);
+ if (container.isLast()) {
+ break;
+ }
+ }
+ }
+ else {
+ mMimeContainers = null;
+ }
+
+ List<String> encoding = headers.get(Encoding);
+ boolean quoted = false;
+ boolean base64 = false;
+ if (encoding != null) {
+ for (String text : encoding) {
+ if (text.equalsIgnoreCase("quoted-printable")) {
+ quoted = true;
+ break;
+ }
+ else if (text.equalsIgnoreCase("base64")) {
+ base64 = true;
+ break;
+ }
+ }
+ }
+ mBase64 = base64;
+
+ Log.d(Utils.hs2LogTag(getClass()),
+ String.format("%s MIME container, boundary '%s', type '%s', encoding %s",
+ multiPart ? "multipart" : "plain", boundary, mContentType, encoding));
+
+ AtomicBoolean eof = new AtomicBoolean();
+ mText = recode(getBody(in, boundary, quoted, eof), charset);
+ mLast = eof.get();
+ }
+
+ public List<MIMEContainer> getMimeContainers() {
+ return mMimeContainers;
+ }
+
+ public String getText() {
+ return mText;
+ }
+
+ public boolean isMixed() {
+ return mMixed;
+ }
+
+ public boolean isBase64() {
+ return mBase64;
+ }
+
+ public String getContentType() {
+ return mContentType;
+ }
+
+ private boolean isLast() {
+ return mLast;
+ }
+
+ private void toString(StringBuilder sb, int nesting) {
+ char[] indent = new char[nesting*4];
+ Arrays.fill(indent, ' ');
+ if (mBase64) {
+ sb.append("base64, type ").append(mContentType).append('\n');
+ }
+ else if (mMimeContainers != null) {
+ sb.append(indent).append("multipart/").append((mMixed ? "mixed" : "other" )).append('\n');
+ }
+ else {
+ sb.append(indent).append(
+ String.format("%s, type %s",
+ mCharset,
+ mContentType)
+ ).append('\n');
+ }
+
+ if (mMimeContainers != null) {
+ for (MIMEContainer mimeContainer : mMimeContainers) {
+ mimeContainer.toString(sb, nesting + 1);
+ }
+ }
+ sb.append(indent).append("Text: ");
+ if (mText.length() < 100000) {
+ sb.append("'").append(mText).append("'\n");
+ }
+ else {
+ sb.append(mText.length()).append(" chars\n");
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ toString(sb, 0);
+ return sb.toString();
+ }
+
+ private static Map<String,List<String>> parseHeader(LineNumberReader in) throws IOException {
+
+ StringBuilder value = null;
+ String header = null;
+
+ Map<String,List<String>> headers = new HashMap<>();
+
+ for (;;) {
+ String line = in.readLine();
+ if ( line == null ) {
+ throw new IOException("Missing body @ " + in.getLineNumber());
+ }
+ else if (line.length() == 0) {
+ break;
+ }
+
+ if (line.charAt(0) <= ' ') {
+ if (value == null) {
+ throw new IOException("Illegal blank prefix in header line '" + line + "' @ " + in.getLineNumber());
+ }
+ value.append(' ').append(line.trim());
+ continue;
+ }
+
+ int nameEnd = line.indexOf(':');
+ if (nameEnd < 0) {
+ throw new IOException("Bad header line: '" + line + "' @ " + in.getLineNumber());
+ }
+
+ if (header != null) {
+ String[] values = value.toString().split(";");
+ List<String> valueList = new ArrayList<>(values.length);
+ for (String segment : values) {
+ valueList.add(segment.trim());
+ }
+ headers.put(header, valueList);
+ //System.out.println("Header '" + header + "' = " + valueList);
+ }
+
+ header = line.substring(0, nameEnd);
+ value = new StringBuilder();
+ value.append(line.substring(nameEnd+1).trim());
+ }
+
+ if (header != null) {
+ String[] values = value.toString().split(";");
+ List<String> valueList = new ArrayList<>(values.length);
+ for (String segment : values) {
+ valueList.add(segment.trim());
+ }
+ headers.put(header, valueList);
+ //System.out.println("Header '" + header + "' = " + valueList);
+ }
+
+ return headers;
+ }
+
+ private static String getBody(LineNumberReader in, String boundary, boolean quoted, AtomicBoolean eof)
+ throws IOException {
+
+ StringBuilder text = new StringBuilder();
+ for (;;) {
+ String line = in.readLine();
+ if (line == null) {
+ if (boundary != null) {
+ throw new IOException("Unexpected EOF file in body @ " + in.getLineNumber());
+ }
+ else {
+ return text.toString();
+ }
+ }
+ Boolean end = boundaryCheck(line, boundary);
+ if (end != null) {
+ eof.set(end);
+ //System.out.println("Boundary " + boundary + ": " + end);
+ return text.toString();
+ }
+
+ if (quoted) {
+ if (line.endsWith("=")) {
+ text.append(unescape(line.substring(line.length() - 1), in.getLineNumber()));
+ }
+ else {
+ text.append(unescape(line, in.getLineNumber()));
+ }
+ }
+ else {
+ text.append(line);
+ }
+ }
+ }
+
+ private static String recode(String s, Charset charset) {
+ if (charset.equals(StandardCharsets.ISO_8859_1) || charset.equals(StandardCharsets.US_ASCII)) {
+ return s;
+ }
+
+ byte[] octets = s.getBytes(StandardCharsets.ISO_8859_1);
+ return new String(octets, charset);
+ }
+
+ private static Boolean boundaryCheck(String line, String boundary) {
+ if (line.startsWith("--") && line.regionMatches(2, boundary, 0, boundary.length())) {
+ if (line.length() == boundary.length() + 2) {
+ return Boolean.FALSE;
+ }
+ else if (line.length() == boundary.length() + 4 && line.endsWith("--") ) {
+ return Boolean.TRUE;
+ }
+ }
+ return null;
+ }
+
+ private static String unescape(String text, int line) throws IOException {
+ StringBuilder sb = new StringBuilder();
+ for (int n = 0; n < text.length(); n++) {
+ char ch = text.charAt(n);
+ if (ch > 127) {
+ throw new IOException("Bad codepoint " + (int)ch + " in quoted printable @ " + line);
+ }
+ if (ch == '=' && n < text.length() - 2) {
+ int h1 = fromStrictHex(text.charAt(n+1));
+ int h2 = fromStrictHex(text.charAt(n+2));
+ if (h1 >= 0 && h2 >= 0) {
+ sb.append((char)((h1 << 4) | h2));
+ n += 2;
+ }
+ else {
+ sb.append(ch);
+ }
+ }
+ else {
+ sb.append(ch);
+ }
+ }
+ return sb.toString();
+ }
+
+ private static int fromStrictHex(char ch) {
+ if (ch >= '0' && ch <= '9') {
+ return ch - '0';
+ }
+ else if (ch >= 'A' && ch <= 'F') {
+ return ch - 'A' + 10;
+ }
+ else {
+ return -1;
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/ANQPData.java b/service/java/com/android/server/wifi/hotspot2/ANQPData.java
new file mode 100644
index 0000000..8f2dc0b
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/ANQPData.java
@@ -0,0 +1,122 @@
+package com.android.server.wifi.hotspot2;
+
+import com.android.server.wifi.anqp.ANQPElement;
+import com.android.server.wifi.anqp.Constants;
+
+import java.util.Collections;
+import java.util.Map;
+
+public class ANQPData {
+ /**
+ * The regular cache time for entries with a non-zero domain id.
+ */
+ private static final long ANQP_QUALIFIED_CACHE_TIMEOUT = 3600000L;
+ /**
+ * The cache time for entries with a zero domain id. The zero domain id indicates that ANQP
+ * data from the AP may change at any time, thus a relatively short cache time is given to
+ * such data, but still long enough to avoid excessive querying.
+ */
+ private static final long ANQP_UNQUALIFIED_CACHE_TIMEOUT = 300000L;
+ /**
+ * This is the hold off time for pending queries, i.e. the time during which subsequent queries
+ * are squelched.
+ */
+ private static final long ANQP_HOLDOFF_TIME = 10000L;
+
+ /**
+ * Max value for the retry counter for unanswered queries. This limits the maximum time-out to
+ * ANQP_HOLDOFF_TIME * 2^MAX_RETRY. With current values this results in 640s.
+ */
+ private static final int MAX_RETRY = 6;
+
+ private final NetworkDetail mNetwork;
+ private final Map<Constants.ANQPElementType, ANQPElement> mANQPElements;
+ private final long mCtime;
+ private final long mExpiry;
+ private final int mRetry;
+
+ public ANQPData(NetworkDetail network,
+ Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
+
+ mNetwork = network;
+ mANQPElements = anqpElements != null ? Collections.unmodifiableMap(anqpElements) : null;
+ mCtime = System.currentTimeMillis();
+ mRetry = 0;
+ if (anqpElements == null) {
+ mExpiry = mCtime + ANQP_HOLDOFF_TIME;
+ }
+ else if (network.getAnqpDomainID() == 0) {
+ mExpiry = mCtime + ANQP_UNQUALIFIED_CACHE_TIMEOUT;
+ }
+ else {
+ mExpiry = mCtime + ANQP_QUALIFIED_CACHE_TIMEOUT;
+ }
+ }
+
+ public ANQPData(NetworkDetail network, ANQPData existing) {
+ mNetwork = network;
+ mANQPElements = null;
+ mCtime = System.currentTimeMillis();
+ if (existing == null) {
+ mRetry = 0;
+ mExpiry = mCtime + ANQP_HOLDOFF_TIME;
+ }
+ else {
+ mRetry = Math.max(existing.getRetry() + 1, MAX_RETRY);
+ mExpiry = ANQP_HOLDOFF_TIME * (1<<mRetry);
+ }
+ }
+
+ public Map<Constants.ANQPElementType, ANQPElement> getANQPElements() {
+ return Collections.unmodifiableMap(mANQPElements);
+ }
+
+ public NetworkDetail getNetwork() {
+ return mNetwork;
+ }
+
+ public boolean expired() {
+ return expired(System.currentTimeMillis());
+ }
+
+ public boolean expired(long at) {
+ return mExpiry <= at;
+ }
+
+ protected boolean isValid(NetworkDetail nwk) {
+ return mANQPElements != null &&
+ nwk.getAnqpDomainID() == mNetwork.getAnqpDomainID() &&
+ mExpiry > System.currentTimeMillis();
+ }
+
+ private int getRetry() {
+ return mRetry;
+ }
+
+ public String toString(boolean brief) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(mNetwork.toKeyString()).append(", domid ").append(mNetwork.getAnqpDomainID());
+ if (mANQPElements == null) {
+ sb.append(", unresolved, ");
+ }
+ else {
+ sb.append(", ").append(mANQPElements.size()).append(" elements, ");
+ }
+ long now = System.currentTimeMillis();
+ sb.append(Utils.toHMS(now-mCtime)).append(" old, expires in ").
+ append(Utils.toHMS(mExpiry-now)).append(' ');
+ if (brief) {
+ sb.append(expired(now) ? 'x' : '-');
+ sb.append(mANQPElements == null ? 'u' : '-');
+ }
+ else if (mANQPElements != null) {
+ sb.append(" data=").append(mANQPElements);
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public String toString() {
+ return toString(true);
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/AlarmHandler.java b/service/java/com/android/server/wifi/hotspot2/AlarmHandler.java
new file mode 100644
index 0000000..5fae519
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/AlarmHandler.java
@@ -0,0 +1,5 @@
+package com.android.server.wifi.hotspot2;
+
+public interface AlarmHandler {
+ public void wake(Object token);
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/AnqpCache.java b/service/java/com/android/server/wifi/hotspot2/AnqpCache.java
new file mode 100644
index 0000000..5bc60ae
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/AnqpCache.java
@@ -0,0 +1,190 @@
+package com.android.server.wifi.hotspot2;
+
+import android.util.Log;
+
+import com.android.server.wifi.anqp.ANQPElement;
+import com.android.server.wifi.anqp.Constants;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class AnqpCache {
+ private static final long CACHE_RECHECK = 60000L;
+ private static final boolean STANDARD_ESS = true; // Regular AP keying; see CacheKey below.
+ private long mLastSweep;
+
+ private final HashMap<CacheKey, ANQPData> mANQPCache;
+
+ public AnqpCache() {
+ mANQPCache = new HashMap<>();
+ mLastSweep = System.currentTimeMillis();
+ }
+
+ private static class CacheKey {
+ private final String mSSID;
+ private final long mBSSID;
+ private final long mHESSID;
+
+ private CacheKey(String ssid, long bssid, long hessid) {
+ mSSID = ssid;
+ mBSSID = bssid;
+ mHESSID = hessid;
+ }
+
+ /**
+ * Build an ANQP cache key suitable for the granularity of the key space as follows:
+ *
+ * HESSID domainID standardESS Key content Rationale
+ * -------- ----------- --------------- ----------- --------------------
+ * n/a zero n/a SSID/BSSID Domain ID indicates unique AP info
+ * not set set false SSID/BSSID Strict per AP keying override
+ * not set set true SSID Standard definition of an ESS
+ * set set n/a HESSID The ESS is defined by the HESSID
+ *
+ * @param network The network to build the key for.
+ * @param standardESS If this parameter is set the "standard" paradigm for an ESS is used
+ * for the cache, i.e. all APs with identical SSID is considered an ESS,
+ * otherwise caching is performed per AP.
+ * @return A CacheKey.
+ */
+ private static CacheKey buildKey(NetworkDetail network, boolean standardESS) {
+ String ssid;
+ long bssid;
+ long hessid;
+ if (network.getAnqpDomainID() == 0L || (network.getHESSID() == 0L && !standardESS)) {
+ ssid = network.getSSID();
+ bssid = network.getBSSID();
+ hessid = 0L;
+ }
+ else if (network.getHESSID() != 0L && network.getAnqpDomainID() > 0) {
+ ssid = null;
+ bssid = 0L;
+ hessid = network.getHESSID();
+ }
+ else {
+ ssid = network.getSSID();
+ bssid = 0L;
+ hessid = 0L;
+ }
+
+ return new CacheKey(ssid, bssid, hessid);
+ }
+
+ @Override
+ public int hashCode() {
+ if (mHESSID != 0) {
+ return (int)((mHESSID >>> 32) * 31 + mHESSID);
+ }
+ else if (mBSSID != 0) {
+ return (int)((mSSID.hashCode() * 31 + (mBSSID >>> 32)) * 31 + mBSSID);
+ }
+ else {
+ return mSSID.hashCode();
+ }
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (thatObject == this) {
+ return true;
+ }
+ else if (thatObject == null || thatObject.getClass() != CacheKey.class) {
+ return false;
+ }
+ CacheKey that = (CacheKey) thatObject;
+ return Utils.compare(that.mSSID, mSSID) == 0 &&
+ that.mBSSID == mBSSID &&
+ that.mHESSID == mHESSID;
+ }
+
+ @Override
+ public String toString() {
+ if (mHESSID != 0L) {
+ return "HESSID:" + NetworkDetail.toMACString(mHESSID);
+ }
+ else if (mBSSID != 0L) {
+ return NetworkDetail.toMACString(mBSSID) +
+ ":<" + Utils.toUnicodeEscapedString(mSSID) + ">";
+ }
+ else {
+ return '<' + Utils.toUnicodeEscapedString(mSSID) + '>';
+ }
+ }
+ }
+
+ public boolean initiate(NetworkDetail network) {
+ CacheKey key = CacheKey.buildKey(network, STANDARD_ESS);
+
+ synchronized (mANQPCache) {
+ ANQPData data = mANQPCache.get(key);
+ if (data == null || data.expired()) {
+ mANQPCache.put(key, new ANQPData(network, data));
+ return true;
+ }
+ else {
+ Log.d(Utils.hs2LogTag(getClass()),
+ String.format("BSSID %012x already in cache: %s", network.getBSSID(), data));
+ return false;
+ }
+ }
+ }
+
+ public void update(NetworkDetail network,
+ Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
+
+ CacheKey key = CacheKey.buildKey(network, STANDARD_ESS);
+
+ // Networks with a 0 ANQP Domain ID are still cached, but with a very short expiry, just
+ // long enough to prevent excessive re-querying.
+ synchronized (mANQPCache) {
+ ANQPData data = new ANQPData(network, anqpElements);
+ mANQPCache.put(key, data);
+ }
+ }
+
+ public ANQPData getEntry(NetworkDetail network) {
+ ANQPData data;
+
+ CacheKey key = CacheKey.buildKey(network, STANDARD_ESS);
+ synchronized (mANQPCache) {
+ data = mANQPCache.get(key);
+ }
+
+ return data != null && data.isValid(network) ? data : null;
+ }
+
+ public void clear(boolean all, boolean debug) {
+ long now = System.currentTimeMillis();
+ synchronized (mANQPCache) {
+ if (all) {
+ mANQPCache.clear();
+ mLastSweep = now;
+ }
+ else if (now > mLastSweep + CACHE_RECHECK) {
+ List<CacheKey> retirees = new ArrayList<>();
+ for (Map.Entry<CacheKey, ANQPData> entry : mANQPCache.entrySet()) {
+ if (entry.getValue().expired(now)) {
+ retirees.add(entry.getKey());
+ }
+ }
+ for (CacheKey key : retirees) {
+ mANQPCache.remove(key);
+ if (debug) {
+ Log.d(Utils.hs2LogTag(getClass()), "Retired " + key);
+ }
+ }
+ mLastSweep = now;
+ }
+ }
+ }
+
+ public void dump(PrintWriter out) {
+ out.println("Last sweep " + Utils.toHMS(System.currentTimeMillis() - mLastSweep) + " ago.");
+ for (ANQPData anqpData : mANQPCache.values()) {
+ out.println(anqpData.toString(false));
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/AuthMatch.java b/service/java/com/android/server/wifi/hotspot2/AuthMatch.java
new file mode 100644
index 0000000..cd988b5
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/AuthMatch.java
@@ -0,0 +1,40 @@
+package com.android.server.wifi.hotspot2;
+
+/**
+ * Match score for EAP credentials:
+ * None means that there is a distinct mismatch, i.e. realm, method or parameter is defined
+ * and mismatches that of the credential.
+ * Indeterminate means that there is no ANQP information to match against.
+ * Note: The numeric values given to the constants are used for preference comparison and
+ * must be maintained accordingly.
+ */
+public abstract class AuthMatch {
+ public static final int None = -1;
+ public static final int Indeterminate = 0;
+ public static final int Realm = 0x04;
+ public static final int Method = 0x02;
+ public static final int Param = 0x01;
+ public static final int MethodParam = Method | Param;
+ public static final int Exact = Realm | Method | Param;
+
+ public static String toString(int match) {
+ if (match < 0) {
+ return "None";
+ }
+ else if (match == 0) {
+ return "Indeterminate";
+ }
+
+ StringBuilder sb = new StringBuilder();
+ if ((match & Realm) != 0) {
+ sb.append("Realm");
+ }
+ if ((match & Method) != 0) {
+ sb.append("Method");
+ }
+ if ((match & Param) != 0) {
+ sb.append("Param");
+ }
+ return sb.toString();
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/Chronograph.java b/service/java/com/android/server/wifi/hotspot2/Chronograph.java
new file mode 100644
index 0000000..92bc95f
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/Chronograph.java
@@ -0,0 +1,154 @@
+package com.android.server.wifi.hotspot2;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+public class Chronograph extends Thread {
+ private final Map<Long, Set<AlarmEntry>> mAlarmEntryMap = new TreeMap<Long, Set<AlarmEntry>>();
+ private boolean mRecalculate;
+
+ private static class AlarmEntry {
+ private final long mAt;
+ private final AlarmHandler mAlarmHandler;
+ private final Object mToken;
+
+ private AlarmEntry(long at, AlarmHandler alarmHandler, Object token) {
+ mAt = at;
+ mAlarmHandler = alarmHandler;
+ mToken = token;
+ }
+
+ private void callout() {
+ mAlarmHandler.wake(mToken);
+ }
+ }
+
+ public Chronograph()
+ {
+ setName("Chronograph");
+ setDaemon(true);
+ }
+
+ public Object addAlarm(long interval, AlarmHandler handler, Object token) {
+ long at = System.currentTimeMillis() + interval;
+ synchronized (mAlarmEntryMap) {
+ AlarmEntry alarmEntry = new AlarmEntry(at, handler, token);
+ Set<AlarmEntry> entries = mAlarmEntryMap.get(at);
+ if (entries == null) {
+ entries = new HashSet<AlarmEntry>(1);
+ mAlarmEntryMap.put(at, entries);
+ }
+ entries.add(alarmEntry);
+ mRecalculate = true;
+ mAlarmEntryMap.notifyAll();
+ return alarmEntry;
+ }
+ }
+
+ public boolean cancelAlarm(Object key) {
+ if (key == null || key.getClass() != AlarmEntry.class) {
+ throw new IllegalArgumentException("Not an alarm key");
+ }
+
+ AlarmEntry alarmEntry = (AlarmEntry)key;
+
+ synchronized (mAlarmEntryMap) {
+ Set<AlarmEntry> entries = mAlarmEntryMap.get(alarmEntry.mAt);
+ if (entries == null) {
+ return false;
+ }
+ if (entries.remove(alarmEntry)) {
+ mRecalculate = true;
+ mAlarmEntryMap.notifyAll();
+ return true;
+ }
+ return false;
+ }
+ }
+
+ @Override
+ public void run() {
+
+ for(;;) {
+
+ long now = System.currentTimeMillis();
+ List<Set<AlarmEntry>> pending = new ArrayList<Set<AlarmEntry>>();
+
+ long nextExpiration = 0;
+
+ synchronized (mAlarmEntryMap) {
+
+ Iterator<Map.Entry<Long,Set<AlarmEntry>>> entries =
+ mAlarmEntryMap.entrySet().iterator();
+
+ while (entries.hasNext()) {
+ Map.Entry<Long,Set<AlarmEntry>> entry = entries.next();
+ if (entry.getKey() <= now) {
+ pending.add(entry.getValue());
+ entries.remove();
+ }
+ else {
+ nextExpiration = entry.getKey();
+ break;
+ }
+ }
+ }
+
+ for (Set<AlarmEntry> alarmEntries : pending) {
+ for (AlarmEntry alarmEntry : alarmEntries) {
+ alarmEntry.callout();
+ }
+ }
+
+ now = System.currentTimeMillis();
+
+ synchronized (mAlarmEntryMap) {
+ long sleep = nextExpiration - now;
+ while (sleep > 0 && !mRecalculate) {
+ try {
+ mAlarmEntryMap.wait(sleep);
+ }
+ catch (InterruptedException ie) {
+ /**/
+ }
+ sleep = nextExpiration - System.currentTimeMillis();
+ }
+ }
+ }
+ }
+
+ public static void main(String[] args) throws InterruptedException{
+ Chronograph chronograph = new Chronograph();
+ chronograph.start();
+
+ chronograph.addAlarm(3000L, new AlarmHandler() {
+ @Override
+ public void wake(Object token) {
+ System.out.println("3: " + token);
+ }
+ }, "3s" );
+
+ Object key = chronograph.addAlarm(7500L, new AlarmHandler() {
+ @Override
+ public void wake(Object token) {
+ System.out.println("7: " + token);
+ }
+ }, "7.5s" );
+
+ chronograph.addAlarm(10000L, new AlarmHandler() {
+ @Override
+ public void wake(Object token) {
+ System.out.println("10: " + token);
+ }
+ }, "10.00s" );
+
+ System.out.println(chronograph.cancelAlarm(key));
+
+ chronograph.join();
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
new file mode 100644
index 0000000..900cbeb
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
@@ -0,0 +1,635 @@
+package com.android.server.wifi.hotspot2;
+
+import android.net.wifi.ScanResult;
+import android.util.Log;
+
+import com.android.server.wifi.anqp.ANQPElement;
+import com.android.server.wifi.anqp.Constants;
+import com.android.server.wifi.anqp.VenueNameElement;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+
+import static com.android.server.wifi.anqp.Constants.BYTES_IN_EUI48;
+import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
+import static com.android.server.wifi.anqp.Constants.getInteger;
+
+public class NetworkDetail {
+
+ private static final int EID_SSID = 0;
+ private static final int EID_BSSLoad = 11;
+ private static final int EID_HT_OPERATION = 61;
+ private static final int EID_VHT_OPERATION = 192;
+ private static final int EID_Interworking = 107;
+ private static final int EID_RoamingConsortium = 111;
+ private static final int EID_ExtendedCaps = 127;
+ private static final int EID_VSA = 221;
+
+ private static final int ANQP_DOMID_BIT = 0x04;
+ private static final int RTT_RESP_ENABLE_BIT = 70;
+
+ private static final long SSID_UTF8_BIT = 0x0001000000000000L;
+ //turn off when SHIP
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
+
+ private static final String TAG = "NetworkDetail:";
+
+ public enum Ant {
+ Private,
+ PrivateWithGuest,
+ ChargeablePublic,
+ FreePublic,
+ Personal,
+ EmergencyOnly,
+ Resvd6,
+ Resvd7,
+ Resvd8,
+ Resvd9,
+ Resvd10,
+ Resvd11,
+ Resvd12,
+ Resvd13,
+ TestOrExperimental,
+ Wildcard
+ }
+
+ public enum HSRelease {
+ R1,
+ R2,
+ Unknown
+ }
+
+ // General identifiers:
+ private final String mSSID;
+ private final long mHESSID;
+ private final long mBSSID;
+
+ // BSS Load element:
+ private final int mStationCount;
+ private final int mChannelUtilization;
+ private final int mCapacity;
+
+ //channel detailed information
+ /*
+ * 0 -- 20 MHz
+ * 1 -- 40 MHz
+ * 2 -- 80 MHz
+ * 3 -- 160 MHz
+ * 4 -- 80 + 80 MHz
+ */
+ private final int mChannelWidth;
+ private final int mPrimaryFreq;
+ private final int mCenterfreq0;
+ private final int mCenterfreq1;
+ private final boolean m80211McRTTResponder;
+ /*
+ * From Interworking element:
+ * mAnt non null indicates the presence of Interworking, i.e. 802.11u
+ * mVenueGroup and mVenueType may be null if not present in the Interworking element.
+ */
+ private final Ant mAnt;
+ private final boolean mInternet;
+ private final VenueNameElement.VenueGroup mVenueGroup;
+ private final VenueNameElement.VenueType mVenueType;
+
+ /*
+ * From HS20 Indication element:
+ * mHSRelease is null only if the HS20 Indication element was not present.
+ * mAnqpDomainID is set to -1 if not present in the element.
+ */
+ private final HSRelease mHSRelease;
+ private final int mAnqpDomainID;
+
+ /*
+ * From beacon:
+ * mAnqpOICount is how many additional OIs are available through ANQP.
+ * mRoamingConsortiums is either null, if the element was not present, or is an array of
+ * 1, 2 or 3 longs in which the roaming consortium values occupy the LSBs.
+ */
+ private final int mAnqpOICount;
+ private final long[] mRoamingConsortiums;
+
+ private final Long mExtendedCapabilities;
+
+ private final Map<Constants.ANQPElementType, ANQPElement> mANQPElements;
+
+ public NetworkDetail(String bssid, String infoElements, List<String> anqpLines, int freq) {
+
+ if (infoElements == null) {
+ throw new IllegalArgumentException("Null information element string");
+ }
+ int separator = infoElements.indexOf('=');
+ if (separator<0) {
+ throw new IllegalArgumentException("No element separator");
+ }
+
+ mBSSID = Utils.parseMac(bssid);
+
+ ByteBuffer data = ByteBuffer.wrap(Utils.hexToBytes(infoElements.substring(separator + 1)))
+ .order(ByteOrder.LITTLE_ENDIAN);
+
+ String ssid = null;
+ byte[] ssidOctets = null;
+ int stationCount = 0;
+ int channelUtilization = 0;
+ int capacity = 0;
+
+ Ant ant = null;
+ boolean internet = false;
+ VenueNameElement.VenueGroup venueGroup = null;
+ VenueNameElement.VenueType venueType = null;
+ long hessid = 0L;
+
+ int anqpOICount = 0;
+ long[] roamingConsortiums = null;
+
+ HSRelease hsRelease = null;
+ int anqpDomainID = 0; // No domain ID treated the same as a 0; unique info per AP.
+
+ Long extendedCapabilities = null;
+
+ int secondChanelOffset = 0;
+ int channelMode = 0;
+ int centerFreqIndex1 = 0;
+ int centerFreqIndex2 = 0;
+ boolean RTTResponder = false;
+
+ RuntimeException exception = null;
+
+ try {
+ while (data.remaining() > 1) {
+ int eid = data.get() & Constants.BYTE_MASK;
+ int elementLength = data.get() & Constants.BYTE_MASK;
+
+ if (elementLength > data.remaining()) {
+ throw new IllegalArgumentException("Element length " + elementLength +
+ " exceeds payload length " + data.remaining() +
+ " @ " + data.position());
+ }
+ if (eid == 0 && elementLength == 0 && ssidOctets != null) {
+ // Don't overwrite SSID (eid 0) with trailing zero garbage
+ continue;
+ }
+
+ ByteBuffer element;
+
+ switch (eid) {
+ case EID_SSID:
+ ssidOctets = new byte[elementLength];
+ data.get(ssidOctets);
+ break;
+ case EID_BSSLoad:
+ if (elementLength != 5) {
+ throw new IllegalArgumentException("BSS Load element length is not 5: " +
+ elementLength);
+ }
+ stationCount = data.getShort() & Constants.SHORT_MASK;
+ channelUtilization = data.get() & Constants.BYTE_MASK;
+ capacity = data.getShort() & Constants.SHORT_MASK;
+ break;
+ case EID_HT_OPERATION:
+ element = getAndAdvancePayload(data, elementLength);
+ int primary_channel = element.get();
+ secondChanelOffset = element.get() & 0x3;
+ break;
+ case EID_VHT_OPERATION:
+ element = getAndAdvancePayload(data, elementLength);
+ channelMode = element.get() & Constants.BYTE_MASK;
+ centerFreqIndex1 = element.get() & Constants.BYTE_MASK;
+ centerFreqIndex2 = element.get() & Constants.BYTE_MASK;
+ break;
+ case EID_Interworking:
+ int anOptions = data.get() & Constants.BYTE_MASK;
+ ant = Ant.values()[anOptions & 0x0f];
+ internet = (anOptions & 0x10) != 0;
+ // Len 1 none, 3 venue-info, 7 HESSID, 9 venue-info & HESSID
+ if (elementLength == 3 || elementLength == 9) {
+ try {
+ ByteBuffer vinfo = data.duplicate();
+ vinfo.limit(vinfo.position() + 2);
+ VenueNameElement vne =
+ new VenueNameElement(Constants.ANQPElementType.ANQPVenueName,
+ vinfo);
+ venueGroup = vne.getGroup();
+ venueType = vne.getType();
+ data.getShort();
+ } catch (ProtocolException pe) {
+ /*Cannot happen*/
+ }
+ } else if (elementLength != 1 && elementLength != 7) {
+ throw new IllegalArgumentException("Bad Interworking element length: " +
+ elementLength);
+ }
+ if (elementLength == 7 || elementLength == 9) {
+ hessid = getInteger(data, ByteOrder.BIG_ENDIAN, 6);
+ }
+ break;
+ case EID_RoamingConsortium:
+ anqpOICount = data.get() & Constants.BYTE_MASK;
+
+ int oi12Length = data.get() & Constants.BYTE_MASK;
+ int oi1Length = oi12Length & Constants.NIBBLE_MASK;
+ int oi2Length = (oi12Length >>> 4) & Constants.NIBBLE_MASK;
+ int oi3Length = elementLength - 2 - oi1Length - oi2Length;
+ int oiCount = 0;
+ if (oi1Length > 0) {
+ oiCount++;
+ if (oi2Length > 0) {
+ oiCount++;
+ if (oi3Length > 0) {
+ oiCount++;
+ }
+ }
+ }
+ roamingConsortiums = new long[oiCount];
+ if (oi1Length > 0 && roamingConsortiums.length > 0) {
+ roamingConsortiums[0] =
+ getInteger(data, ByteOrder.BIG_ENDIAN, oi1Length);
+ }
+ if (oi2Length > 0 && roamingConsortiums.length > 1) {
+ roamingConsortiums[1] =
+ getInteger(data, ByteOrder.BIG_ENDIAN, oi2Length);
+ }
+ if (oi3Length > 0 && roamingConsortiums.length > 2) {
+ roamingConsortiums[2] =
+ getInteger(data, ByteOrder.BIG_ENDIAN, oi3Length);
+ }
+ break;
+ case EID_VSA:
+ element = getAndAdvancePayload(data, elementLength);
+ if (elementLength >= 5 && element.getInt() == Constants.HS20_FRAME_PREFIX) {
+ int hsConf = element.get() & Constants.BYTE_MASK;
+ switch ((hsConf >> 4) & Constants.NIBBLE_MASK) {
+ case 0:
+ hsRelease = HSRelease.R1;
+ break;
+ case 1:
+ hsRelease = HSRelease.R2;
+ break;
+ default:
+ hsRelease = HSRelease.Unknown;
+ break;
+ }
+ if ((hsConf & ANQP_DOMID_BIT) != 0) {
+ if (elementLength < 7) {
+ throw new IllegalArgumentException(
+ "HS20 indication element too short: " + elementLength);
+ }
+ anqpDomainID = element.getShort() & Constants.SHORT_MASK;
+ }
+ }
+ break;
+ case EID_ExtendedCaps:
+ element = data.duplicate();
+ extendedCapabilities =
+ Constants.getInteger(data, ByteOrder.LITTLE_ENDIAN, elementLength);
+
+ int index = RTT_RESP_ENABLE_BIT / 8;
+ byte offset = RTT_RESP_ENABLE_BIT % 8;
+
+ if (elementLength < index + 1) {
+ RTTResponder = false;
+ element.position(element.position() + elementLength);
+ break;
+ }
+
+ element.position(element.position() + index);
+
+ RTTResponder = (element.get() & (0x1 << offset)) != 0;
+ break;
+ default:
+ data.position(data.position() + elementLength);
+ break;
+ }
+ }
+ }
+ catch (IllegalArgumentException | BufferUnderflowException | ArrayIndexOutOfBoundsException e) {
+ Log.d(Utils.hs2LogTag(getClass()), "Caught " + e);
+ if (ssidOctets == null) {
+ throw new IllegalArgumentException("Malformed IE string (no SSID)", e);
+ }
+ exception = e;
+ }
+
+ if (ssidOctets != null) {
+ boolean strictUTF8 = extendedCapabilities != null &&
+ ( extendedCapabilities & SSID_UTF8_BIT ) != 0;
+
+ /*
+ * Strict use of the "UTF-8 SSID" bit by APs appears to be spotty at best even if the
+ * encoding truly is in UTF-8. An unconditional attempt to decode the SSID as UTF-8 is
+ * therefore always made with a fall back to 8859-1 under normal circumstances.
+ * If, however, a previous exception was detected and the UTF-8 bit is set, failure to
+ * decode the SSID will be used as an indication that the whole frame is malformed and
+ * an exception will be triggered.
+ */
+ CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
+ try {
+ CharBuffer decoded = decoder.decode(ByteBuffer.wrap(ssidOctets));
+ ssid = decoded.toString();
+ }
+ catch (CharacterCodingException cce) {
+ ssid = null;
+ }
+
+ if (ssid == null) {
+ if (strictUTF8 && exception != null) {
+ throw new IllegalArgumentException("Failed to decode SSID in dubious IE string");
+ }
+ else {
+ ssid = new String(ssidOctets, StandardCharsets.ISO_8859_1);
+ }
+ }
+ }
+
+ mSSID = ssid;
+ mHESSID = hessid;
+ mStationCount = stationCount;
+ mChannelUtilization = channelUtilization;
+ mCapacity = capacity;
+ mAnt = ant;
+ mInternet = internet;
+ mVenueGroup = venueGroup;
+ mVenueType = venueType;
+ mHSRelease = hsRelease;
+ mAnqpDomainID = anqpDomainID;
+ mAnqpOICount = anqpOICount;
+ mRoamingConsortiums = roamingConsortiums;
+ mExtendedCapabilities = extendedCapabilities;
+ mANQPElements = SupplicantBridge.parseANQPLines(anqpLines);
+ //set up channel info
+ mPrimaryFreq = freq;
+
+ if (channelMode != 0) {
+ // 80 or 160 MHz
+ mChannelWidth = channelMode + 1;
+ mCenterfreq0 = (centerFreqIndex1 - 36) * 5 + 5180;
+ if(channelMode > 1) { //160MHz
+ mCenterfreq1 = (centerFreqIndex2 - 36) * 5 + 5180;
+ } else {
+ mCenterfreq1 = 0;
+ }
+ } else {
+ //20 or 40 MHz
+ if (secondChanelOffset != 0) {//40MHz
+ mChannelWidth = 1;
+ if (secondChanelOffset == 1) {
+ mCenterfreq0 = mPrimaryFreq + 20;
+ } else if (secondChanelOffset == 3) {
+ mCenterfreq0 = mPrimaryFreq - 20;
+ } else {
+ mCenterfreq0 = 0;
+ Log.e(TAG,"Error on secondChanelOffset");
+ }
+ } else {
+ mCenterfreq0 = 0;
+ mChannelWidth = 0;
+ }
+ mCenterfreq1 = 0;
+ }
+ m80211McRTTResponder = RTTResponder;
+ if (VDBG) {
+ Log.d(TAG, mSSID + "ChannelWidth is: " + mChannelWidth + " PrimaryFreq: " + mPrimaryFreq +
+ " mCenterfreq0: " + mCenterfreq0 + " mCenterfreq1: " + mCenterfreq1 +
+ (m80211McRTTResponder ? "Support RTT reponder" : "Do not support RTT responder"));
+ }
+ }
+
+ private static ByteBuffer getAndAdvancePayload(ByteBuffer data, int plLength) {
+ ByteBuffer payload = data.duplicate().order(data.order());
+ payload.limit(payload.position() + plLength);
+ data.position(data.position() + plLength);
+ return payload;
+ }
+
+ private NetworkDetail(NetworkDetail base, Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
+ mSSID = base.mSSID;
+ mBSSID = base.mBSSID;
+ mHESSID = base.mHESSID;
+ mStationCount = base.mStationCount;
+ mChannelUtilization = base.mChannelUtilization;
+ mCapacity = base.mCapacity;
+ mAnt = base.mAnt;
+ mInternet = base.mInternet;
+ mVenueGroup = base.mVenueGroup;
+ mVenueType = base.mVenueType;
+ mHSRelease = base.mHSRelease;
+ mAnqpDomainID = base.mAnqpDomainID;
+ mAnqpOICount = base.mAnqpOICount;
+ mRoamingConsortiums = base.mRoamingConsortiums;
+ mExtendedCapabilities = base.mExtendedCapabilities;
+ mANQPElements = anqpElements;
+ mChannelWidth = base.mChannelWidth;
+ mPrimaryFreq = base.mPrimaryFreq;
+ mCenterfreq0 = base.mCenterfreq0;
+ mCenterfreq1 = base.mCenterfreq1;
+ m80211McRTTResponder = base.m80211McRTTResponder;
+ }
+
+ public NetworkDetail complete(Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
+ return new NetworkDetail(this, anqpElements);
+ }
+
+ private static long parseMac(String s) {
+
+ long mac = 0;
+ int count = 0;
+ for (int n = 0; n < s.length(); n++) {
+ int nibble = Utils.fromHex(s.charAt(n), true);
+ if (nibble >= 0) {
+ mac = (mac << 4) | nibble;
+ count++;
+ }
+ }
+ if (count < 12 || (count&1) == 1) {
+ throw new IllegalArgumentException("Bad MAC address: '" + s + "'");
+ }
+ return mac;
+ }
+
+ public boolean has80211uInfo() {
+ return mAnt != null || mRoamingConsortiums != null || mHSRelease != null;
+ }
+
+ public boolean hasInterworking() {
+ return mAnt != null;
+ }
+
+ public String getSSID() {
+ return mSSID;
+ }
+
+ public String getTrimmedSSID() {
+ for (int n = 0; n < mSSID.length(); n++) {
+ if (mSSID.charAt(n) != 0) {
+ return mSSID;
+ }
+ }
+ return "";
+ }
+
+ public long getHESSID() {
+ return mHESSID;
+ }
+
+ public long getBSSID() {
+ return mBSSID;
+ }
+
+ public int getStationCount() {
+ return mStationCount;
+ }
+
+ public int getChannelUtilization() {
+ return mChannelUtilization;
+ }
+
+ public int getCapacity() {
+ return mCapacity;
+ }
+
+ public boolean isInterworking() {
+ return mAnt != null;
+ }
+
+ public Ant getAnt() {
+ return mAnt;
+ }
+
+ public boolean isInternet() {
+ return mInternet;
+ }
+
+ public VenueNameElement.VenueGroup getVenueGroup() {
+ return mVenueGroup;
+ }
+
+ public VenueNameElement.VenueType getVenueType() {
+ return mVenueType;
+ }
+
+ public HSRelease getHSRelease() {
+ return mHSRelease;
+ }
+
+ public int getAnqpDomainID() {
+ return mAnqpDomainID;
+ }
+
+ public int getAnqpOICount() {
+ return mAnqpOICount;
+ }
+
+ public long[] getRoamingConsortiums() {
+ return mRoamingConsortiums;
+ }
+
+ public Long getExtendedCapabilities() {
+ return mExtendedCapabilities;
+ }
+
+ public Map<Constants.ANQPElementType, ANQPElement> getANQPElements() {
+ return mANQPElements;
+ }
+
+ public int getChannelWidth() {
+ return mChannelWidth;
+ }
+
+ public int getCenterfreq0() {
+ return mCenterfreq0;
+ }
+
+ public int getCenterfreq1() {
+ return mCenterfreq1;
+ }
+
+ public boolean is80211McResponderSupport() {
+ return m80211McRTTResponder;
+ }
+
+ public boolean isSSID_UTF8() {
+ return mExtendedCapabilities != null && (mExtendedCapabilities & SSID_UTF8_BIT) != 0;
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (thatObject == null || getClass() != thatObject.getClass()) {
+ return false;
+ }
+
+ NetworkDetail that = (NetworkDetail)thatObject;
+
+ return getSSID().equals(that.getSSID()) && getBSSID() == that.getBSSID();
+ }
+
+ @Override
+ public int hashCode() {
+ return ((mSSID.hashCode() * 31) + (int)(mBSSID >>> 32)) * 31 + (int)mBSSID;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("NetworkInfo{mSSID='%s', mHESSID=%x, mBSSID=%x, mStationCount=%d, " +
+ "mChannelUtilization=%d, mCapacity=%d, mAnt=%s, mInternet=%s, " +
+ "mVenueGroup=%s, mVenueType=%s, mHSRelease=%s, mAnqpDomainID=%d, " +
+ "mAnqpOICount=%d, mRoamingConsortiums=%s}",
+ mSSID, mHESSID, mBSSID, mStationCount,
+ mChannelUtilization, mCapacity, mAnt, mInternet,
+ mVenueGroup, mVenueType, mHSRelease, mAnqpDomainID,
+ mAnqpOICount, Utils.roamingConsortiumsToString(mRoamingConsortiums));
+ }
+
+ public String toKeyString() {
+ return mHESSID != 0 ?
+ String.format("'%s':%012x (%012x)", mSSID, mBSSID, mHESSID) :
+ String.format("'%s':%012x", mSSID, mBSSID);
+ }
+
+ public String getBSSIDString() {
+ return toMACString(mBSSID);
+ }
+
+ public static String toMACString(long mac) {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (int n = BYTES_IN_EUI48 - 1; n >= 0; n--) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(':');
+ }
+ sb.append(String.format("%02x", (mac >>> (n * Byte.SIZE)) & BYTE_MASK));
+ }
+ return sb.toString();
+ }
+
+ private static final String IE = "ie=" +
+ "000477696e67" + // SSID wing
+ "0b052a00cf611e" + // BSS Load 42:207:7777
+ "6b091e0a01610408621205" + // internet:Experimental:Vehicular:Auto:hessid
+ "6f0a0e530111112222222229" + // 14:111111:2222222229
+ "dd07506f9a10143a01"; // r2:314
+
+ private static final String IE2 = "ie=000f4578616d706c65204e6574776f726b010882848b960c1218240301012a010432043048606c30140100000fac040100000fac040100000fac0100007f04000000806b091e07010203040506076c027f006f1001531122331020304050010203040506dd05506f9a1000";
+
+ public static void main(String[] args) {
+ ScanResult scanResult = new ScanResult();
+ scanResult.SSID = "wing";
+ scanResult.BSSID = "610408";
+ NetworkDetail nwkDetail = new NetworkDetail(scanResult.BSSID, IE2, null, 0);
+ System.out.println(nwkDetail);
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointMatch.java b/service/java/com/android/server/wifi/hotspot2/PasspointMatch.java
new file mode 100644
index 0000000..8825dad
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointMatch.java
@@ -0,0 +1,9 @@
+package com.android.server.wifi.hotspot2;
+
+public enum PasspointMatch {
+ HomeProvider,
+ RoamingProvider,
+ Incomplete,
+ None,
+ Declined
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointMatchInfo.java b/service/java/com/android/server/wifi/hotspot2/PasspointMatchInfo.java
new file mode 100644
index 0000000..deb60ed
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointMatchInfo.java
@@ -0,0 +1,246 @@
+package com.android.server.wifi.hotspot2;
+
+import com.android.server.wifi.ScanDetail;
+import com.android.server.wifi.anqp.ANQPElement;
+import com.android.server.wifi.anqp.HSConnectionCapabilityElement;
+import com.android.server.wifi.anqp.HSWanMetricsElement;
+import com.android.server.wifi.anqp.IPAddressTypeAvailabilityElement;
+import com.android.server.wifi.hotspot2.pps.HomeSP;
+
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.android.server.wifi.anqp.Constants.ANQPElementType;
+import static com.android.server.wifi.anqp.IPAddressTypeAvailabilityElement.IPv4Availability;
+import static com.android.server.wifi.anqp.IPAddressTypeAvailabilityElement.IPv6Availability;
+
+public class PasspointMatchInfo implements Comparable<PasspointMatchInfo> {
+ private final PasspointMatch mPasspointMatch;
+ private final ScanDetail mScanDetail;
+ private final HomeSP mHomeSP;
+ private final int mScore;
+
+ private static final Map<IPv4Availability, Integer> sIP4Scores =
+ new EnumMap<>(IPv4Availability.class);
+ private static final Map<IPv6Availability, Integer> sIP6Scores =
+ new EnumMap<>(IPv6Availability.class);
+
+ private static final Map<Integer, Map<Integer, Integer>> sPortScores = new HashMap<>();
+
+ private static final int IPPROTO_ICMP = 1;
+ private static final int IPPROTO_TCP = 6;
+ private static final int IPPROTO_UDP = 17;
+ private static final int IPPROTO_ESP = 50;
+ private static final Map<NetworkDetail.Ant, Integer> sAntScores = new HashMap<>();
+
+ static {
+ // These are all arbitrarily chosen scores, subject to tuning.
+
+ sAntScores.put(NetworkDetail.Ant.FreePublic, 4);
+ sAntScores.put(NetworkDetail.Ant.ChargeablePublic, 4);
+ sAntScores.put(NetworkDetail.Ant.PrivateWithGuest, 4);
+ sAntScores.put(NetworkDetail.Ant.Private, 4);
+ sAntScores.put(NetworkDetail.Ant.Personal, 2);
+ sAntScores.put(NetworkDetail.Ant.EmergencyOnly, 2);
+ sAntScores.put(NetworkDetail.Ant.Wildcard, 1);
+ sAntScores.put(NetworkDetail.Ant.TestOrExperimental, 0);
+
+ sIP4Scores.put(IPv4Availability.NotAvailable, 0);
+ sIP4Scores.put(IPv4Availability.PortRestricted, 1);
+ sIP4Scores.put(IPv4Availability.PortRestrictedAndSingleNAT, 1);
+ sIP4Scores.put(IPv4Availability.PortRestrictedAndDoubleNAT, 1);
+ sIP4Scores.put(IPv4Availability.Unknown, 1);
+ sIP4Scores.put(IPv4Availability.Public, 2);
+ sIP4Scores.put(IPv4Availability.SingleNAT, 2);
+ sIP4Scores.put(IPv4Availability.DoubleNAT, 2);
+
+ sIP6Scores.put(IPv6Availability.NotAvailable, 0);
+ sIP6Scores.put(IPv6Availability.Reserved, 1);
+ sIP6Scores.put(IPv6Availability.Unknown, 1);
+ sIP6Scores.put(IPv6Availability.Available, 2);
+
+ Map<Integer, Integer> tcpMap = new HashMap<>();
+ tcpMap.put(20, 1);
+ tcpMap.put(21, 1);
+ tcpMap.put(22, 3);
+ tcpMap.put(23, 2);
+ tcpMap.put(25, 8);
+ tcpMap.put(26, 8);
+ tcpMap.put(53, 3);
+ tcpMap.put(80, 10);
+ tcpMap.put(110, 6);
+ tcpMap.put(143, 6);
+ tcpMap.put(443, 10);
+ tcpMap.put(993, 6);
+ tcpMap.put(1723, 7);
+
+ Map<Integer, Integer> udpMap = new HashMap<>();
+ udpMap.put(53, 10);
+ udpMap.put(500, 7);
+ udpMap.put(5060, 10);
+ udpMap.put(4500, 4);
+
+ sPortScores.put(IPPROTO_TCP, tcpMap);
+ sPortScores.put(IPPROTO_UDP, udpMap);
+ }
+
+
+ public PasspointMatchInfo(PasspointMatch passpointMatch,
+ ScanDetail scanDetail, HomeSP homeSP) {
+ mPasspointMatch = passpointMatch;
+ mScanDetail = scanDetail;
+ mHomeSP = homeSP;
+
+ int score;
+ if (passpointMatch == PasspointMatch.HomeProvider) {
+ score = 100;
+ }
+ else if (passpointMatch == PasspointMatch.RoamingProvider) {
+ score = 0;
+ }
+ else {
+ score = -1000; // Don't expect to see anything not home or roaming.
+ }
+
+ if (getNetworkDetail().getHSRelease() != null) {
+ score += getNetworkDetail().getHSRelease() != NetworkDetail.HSRelease.Unknown ? 50 : 0;
+ }
+
+ if (getNetworkDetail().hasInterworking()) {
+ score += getNetworkDetail().isInternet() ? 20 : -20;
+ }
+
+ score += (Math.max(200-getNetworkDetail().getStationCount(), 0) *
+ (255-getNetworkDetail().getChannelUtilization()) *
+ getNetworkDetail().getCapacity()) >>> 26;
+ // Gives a value of 23 max capped at 200 stations and max cap 31250
+
+ if (getNetworkDetail().hasInterworking()) {
+ score += sAntScores.get(getNetworkDetail().getAnt());
+ }
+
+ Map<ANQPElementType, ANQPElement> anqp = getNetworkDetail().getANQPElements();
+
+ if (anqp != null) {
+ HSWanMetricsElement wm = (HSWanMetricsElement) anqp.get(ANQPElementType.HSWANMetrics);
+
+ if (wm != null) {
+ if (wm.getStatus() != HSWanMetricsElement.LinkStatus.Up || wm.isCapped()) {
+ score -= 1000;
+ } else {
+ long scaledSpeed =
+ wm.getDlSpeed() * (255 - wm.getDlLoad()) * 8 +
+ wm.getUlSpeed() * (255 - wm.getUlLoad()) * 2;
+ score += Math.min(scaledSpeed, 255000000L) >>> 23;
+ // Max value is 30 capped at 100Mb/s
+ }
+ }
+
+ IPAddressTypeAvailabilityElement ipa =
+ (IPAddressTypeAvailabilityElement) anqp.get(ANQPElementType.ANQPIPAddrAvailability);
+
+ if (ipa != null) {
+ Integer as14 = sIP4Scores.get(ipa.getV4Availability());
+ Integer as16 = sIP6Scores.get(ipa.getV6Availability());
+ as14 = as14 != null ? as14 : 1;
+ as16 = as16 != null ? as16 : 1;
+ // Is IPv4 twice as important as IPv6???
+ score += as14 * 2 + as16;
+ }
+
+ HSConnectionCapabilityElement cce =
+ (HSConnectionCapabilityElement) anqp.get(ANQPElementType.HSConnCapability);
+
+ if (cce != null) {
+ score = Math.min(Math.max(protoScore(cce) >> 3, -10), 10);
+ }
+ }
+
+ mScore = score;
+ }
+
+ public PasspointMatch getPasspointMatch() {
+ return mPasspointMatch;
+ }
+
+ public ScanDetail getScanDetail() {
+ return mScanDetail;
+ }
+
+ public NetworkDetail getNetworkDetail() {
+ return mScanDetail.getNetworkDetail();
+ }
+
+
+ public HomeSP getHomeSP() {
+ return mHomeSP;
+ }
+
+ public int getScore() {
+ return mScore;
+ }
+
+ @Override
+ public int compareTo(PasspointMatchInfo that) {
+ return getScore() - that.getScore();
+ }
+
+ private static int protoScore(HSConnectionCapabilityElement cce) {
+ int score = 0;
+ for (HSConnectionCapabilityElement.ProtocolTuple tuple : cce.getStatusList()) {
+ int sign = tuple.getStatus() == HSConnectionCapabilityElement.ProtoStatus.Open ?
+ 1 : -1;
+
+ int elementScore = 1;
+ if (tuple.getProtocol() == IPPROTO_ICMP) {
+ elementScore = 1;
+ }
+ else if (tuple.getProtocol() == IPPROTO_ESP) {
+ elementScore = 5;
+ }
+ else {
+ Map<Integer, Integer> protoMap = sPortScores.get(tuple.getProtocol());
+ if (protoMap != null) {
+ Integer portScore = protoMap.get(tuple.getPort());
+ elementScore = portScore != null ? portScore : 0;
+ }
+ }
+ score += elementScore * sign;
+ }
+ return score;
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (thatObject == null || getClass() != thatObject.getClass()) {
+ return false;
+ }
+
+ PasspointMatchInfo that = (PasspointMatchInfo)thatObject;
+
+ return getNetworkDetail().equals(that.getNetworkDetail()) &&
+ getHomeSP().equals(that.getHomeSP()) &&
+ getPasspointMatch().equals(that.getPasspointMatch());
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mPasspointMatch != null ? mPasspointMatch.hashCode() : 0;
+ result = 31 * result + getNetworkDetail().hashCode();
+ result = 31 * result + (mHomeSP != null ? mHomeSP.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "PasspointMatchInfo{" +
+ ", mPasspointMatch=" + mPasspointMatch +
+ ", mNetworkInfo=" + getNetworkDetail().getSSID() +
+ ", mHomeSP=" + mHomeSP.getFQDN() +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/SupplicantBridge.java b/service/java/com/android/server/wifi/hotspot2/SupplicantBridge.java
new file mode 100644
index 0000000..40b6223
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/SupplicantBridge.java
@@ -0,0 +1,457 @@
+package com.android.server.wifi.hotspot2;
+
+import android.util.Log;
+
+import com.android.server.wifi.ScanDetail;
+import com.android.server.wifi.WifiConfigStore;
+import com.android.server.wifi.WifiNative;
+import com.android.server.wifi.anqp.ANQPElement;
+import com.android.server.wifi.anqp.ANQPFactory;
+import com.android.server.wifi.anqp.Constants;
+import com.android.server.wifi.anqp.eap.AuthParam;
+import com.android.server.wifi.anqp.eap.EAP;
+import com.android.server.wifi.anqp.eap.EAPMethod;
+import com.android.server.wifi.hotspot2.pps.Credential;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class SupplicantBridge {
+ private final WifiNative mSupplicantHook;
+ private final WifiConfigStore mConfigStore;
+ private final Map<Long, ScanDetail> mRequestMap = new HashMap<>();
+
+ private static final Map<String, Constants.ANQPElementType> sWpsNames = new HashMap<>();
+
+ static {
+ sWpsNames.put("anqp_venue_name", Constants.ANQPElementType.ANQPVenueName);
+ sWpsNames.put("anqp_network_auth_type", Constants.ANQPElementType.ANQPNwkAuthType);
+ sWpsNames.put("anqp_roaming_consortium", Constants.ANQPElementType.ANQPRoamingConsortium);
+ sWpsNames.put("anqp_ip_addr_type_availability",
+ Constants.ANQPElementType.ANQPIPAddrAvailability);
+ sWpsNames.put("anqp_nai_realm", Constants.ANQPElementType.ANQPNAIRealm);
+ sWpsNames.put("anqp_3gpp", Constants.ANQPElementType.ANQP3GPPNetwork);
+ sWpsNames.put("anqp_domain_name", Constants.ANQPElementType.ANQPDomName);
+ sWpsNames.put("hs20_operator_friendly_name", Constants.ANQPElementType.HSFriendlyName);
+ sWpsNames.put("hs20_wan_metrics", Constants.ANQPElementType.HSWANMetrics);
+ sWpsNames.put("hs20_connection_capability", Constants.ANQPElementType.HSConnCapability);
+ sWpsNames.put("hs20_operating_class", Constants.ANQPElementType.HSOperatingclass);
+ sWpsNames.put("hs20_osu_providers_list", Constants.ANQPElementType.HSOSUProviders);
+ }
+
+ public static boolean isAnqpAttribute(String line) {
+ int split = line.indexOf('=');
+ return split >= 0 && sWpsNames.containsKey(line.substring(0, split));
+ }
+
+ public SupplicantBridge(WifiNative supplicantHook, WifiConfigStore configStore) {
+ mSupplicantHook = supplicantHook;
+ mConfigStore = configStore;
+ }
+
+ public static Map<Constants.ANQPElementType, ANQPElement> parseANQPLines(List<String> lines) {
+ if (lines == null) {
+ return null;
+ }
+ Map<Constants.ANQPElementType, ANQPElement> elements = new HashMap<>(lines.size());
+ for (String line : lines) {
+ try {
+ ANQPElement element = buildElement(line);
+ if (element != null) {
+ elements.put(element.getID(), element);
+ }
+ }
+ catch (ProtocolException pe) {
+ Log.e(Utils.hs2LogTag(SupplicantBridge.class), "Failed to parse ANQP: " + pe);
+ }
+ }
+ return elements;
+ }
+
+ public void startANQP(ScanDetail scanDetail) {
+ String anqpGet = buildWPSQueryRequest(scanDetail.getNetworkDetail());
+ synchronized (mRequestMap) {
+ mRequestMap.put(scanDetail.getNetworkDetail().getBSSID(), scanDetail);
+ }
+ String result = mSupplicantHook.doCustomCommand(anqpGet);
+ if (result != null && result.startsWith("OK")) {
+ Log.d(Utils.hs2LogTag(getClass()), "ANQP initiated on " + scanDetail);
+ }
+ else {
+ Log.d(Utils.hs2LogTag(getClass()), "ANQP failed on " +
+ scanDetail + ": " + result);
+ }
+ }
+
+ public void notifyANQPDone(Long bssid, boolean success) {
+ ScanDetail scanDetail;
+ synchronized (mRequestMap) {
+ scanDetail = mRequestMap.remove(bssid);
+ }
+ if (scanDetail == null) {
+ Log.d(Utils.hs2LogTag(getClass()), String.format("Spurious %s ANQP response for %012x",
+ success ? "successful" : "failed", bssid));
+ return;
+ }
+
+ String bssData = mSupplicantHook.scanResult(scanDetail.getBSSIDString());
+ try {
+ Map<Constants.ANQPElementType, ANQPElement> elements = parseWPSData(bssData);
+ Log.d(Utils.hs2LogTag(getClass()), String.format("%s ANQP response for %012x: %s",
+ success ? "successful" : "failed", bssid, elements));
+ mConfigStore.notifyANQPResponse(scanDetail, success ? elements : null);
+ }
+ catch (IOException ioe) {
+ Log.e(Utils.hs2LogTag(getClass()), "Failed to parse ANQP: " +
+ ioe.toString() + ": " + bssData);
+ }
+ catch (RuntimeException rte) {
+ Log.e(Utils.hs2LogTag(getClass()), "Failed to parse ANQP: " +
+ rte.toString() + ": " + bssData, rte);
+ }
+ mConfigStore.notifyANQPResponse(scanDetail, null);
+ }
+
+ /*
+ public boolean addCredential(HomeSP homeSP, NetworkDetail networkDetail) {
+ Credential credential = homeSP.getCredential();
+ if (credential == null)
+ return false;
+
+ String nwkID = null;
+ if (mLastSSID != null) {
+ String nwkList = mSupplicantHook.doCustomCommand("LIST_NETWORKS");
+
+ BufferedReader reader = new BufferedReader(new StringReader(nwkList));
+ String line;
+ try {
+ while ((line = reader.readLine()) != null) {
+ String[] tokens = line.split("\\t");
+ if (tokens.length < 2 || ! Utils.isDecimal(tokens[0])) {
+ continue;
+ }
+ if (unescapeSSID(tokens[1]).equals(mLastSSID)) {
+ nwkID = tokens[0];
+ Log.d("HS2J", "Network " + tokens[0] +
+ " matches last SSID '" + mLastSSID + "'");
+ break;
+ }
+ }
+ }
+ catch (IOException ioe) {
+ //
+ }
+ }
+
+ if (nwkID == null) {
+ nwkID = mSupplicantHook.doCustomCommand("ADD_NETWORK");
+ Log.d("HS2J", "add_network: '" + nwkID + "'");
+ if (! Utils.isDecimal(nwkID)) {
+ return false;
+ }
+ }
+
+ List<String> credCommand = getWPSNetCommands(nwkID, networkDetail, credential);
+ for (String command : credCommand) {
+ String status = mSupplicantHook.doCustomCommand(command);
+ Log.d("HS2J", "Status of '" + command + "': '" + status + "'");
+ }
+
+ if (! networkDetail.getSSID().equals(mLastSSID)) {
+ mLastSSID = networkDetail.getSSID();
+ PrintWriter out = null;
+ try {
+ out = new PrintWriter(new OutputStreamWriter(
+ new FileOutputStream(mLastSSIDFile, false), StandardCharsets.UTF_8));
+ out.println(mLastSSID);
+ } catch (IOException ioe) {
+ //
+ } finally {
+ if (out != null) {
+ out.close();
+ }
+ }
+ }
+
+ return true;
+ }
+ */
+
+ private static String escapeSSID(NetworkDetail networkDetail) {
+ return escapeString(networkDetail.getSSID(), networkDetail.isSSID_UTF8());
+ }
+
+ private static String escapeString(String s, boolean utf8) {
+ boolean asciiOnly = true;
+ for (int n = 0; n < s.length(); n++) {
+ char ch = s.charAt(n);
+ if (ch > 127) {
+ asciiOnly = false;
+ break;
+ }
+ }
+
+ if (asciiOnly) {
+ return '"' + s + '"';
+ }
+ else {
+ byte[] octets = s.getBytes(utf8 ? StandardCharsets.UTF_8 : StandardCharsets.ISO_8859_1);
+
+ StringBuilder sb = new StringBuilder();
+ for (byte octet : octets) {
+ sb.append(String.format("%02x", octet & Constants.BYTE_MASK));
+ }
+ return sb.toString();
+ }
+ }
+
+ private static String buildWPSQueryRequest(NetworkDetail networkDetail) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("ANQP_GET ").append(networkDetail.getBSSIDString()).append(' ');
+
+ boolean first = true;
+ for (Constants.ANQPElementType elementType : ANQPFactory.getBaseANQPSet()) {
+ if (networkDetail.getAnqpOICount() == 0 &&
+ elementType == Constants.ANQPElementType.ANQPRoamingConsortium) {
+ continue;
+ }
+ if (first) {
+ first = false;
+ }
+ else {
+ sb.append(',');
+ }
+ sb.append(Constants.getANQPElementID(elementType));
+ }
+ if (networkDetail.getHSRelease() != null) {
+ for (Constants.ANQPElementType elementType : ANQPFactory.getHS20ANQPSet()) {
+ sb.append(",hs20:").append(Constants.getHS20ElementID(elementType));
+ }
+ }
+ return sb.toString();
+ }
+
+ private static List<String> getWPSNetCommands(String netID, NetworkDetail networkDetail,
+ Credential credential) {
+
+ List<String> commands = new ArrayList<String>();
+
+ EAPMethod eapMethod = credential.getEAPMethod();
+ commands.add(String.format("SET_NETWORK %s key_mgmt WPA-EAP", netID));
+ commands.add(String.format("SET_NETWORK %s ssid %s", netID, escapeSSID(networkDetail)));
+ commands.add(String.format("SET_NETWORK %s bssid %s",
+ netID, networkDetail.getBSSIDString()));
+ commands.add(String.format("SET_NETWORK %s eap %s",
+ netID, mapEAPMethodName(eapMethod.getEAPMethodID())));
+
+ AuthParam authParam = credential.getEAPMethod().getAuthParam();
+ if (authParam == null) {
+ return null; // TLS or SIM/AKA
+ }
+ switch (authParam.getAuthInfoID()) {
+ case NonEAPInnerAuthType:
+ case InnerAuthEAPMethodType:
+ commands.add(String.format("SET_NETWORK %s identity %s",
+ netID, escapeString(credential.getUserName(), true)));
+ commands.add(String.format("SET_NETWORK %s password %s",
+ netID, escapeString(credential.getPassword(), true)));
+ commands.add(String.format("SET_NETWORK %s anonymous_identity \"anonymous\"",
+ netID));
+ break;
+ default: // !!! Needs work.
+ return null;
+ }
+ commands.add(String.format("SET_NETWORK %s priority 0", netID));
+ commands.add(String.format("ENABLE_NETWORK %s", netID));
+ commands.add(String.format("SAVE_CONFIG"));
+ return commands;
+ }
+
+ private static Map<Constants.ANQPElementType, ANQPElement> parseWPSData(String bssInfo)
+ throws IOException {
+ Map<Constants.ANQPElementType, ANQPElement> elements = new HashMap<>();
+ if (bssInfo == null) {
+ return elements;
+ }
+ BufferedReader lineReader = new BufferedReader(new StringReader(bssInfo));
+ String line;
+ while ((line=lineReader.readLine()) != null) {
+ ANQPElement element = buildElement(line);
+ if (element != null) {
+ elements.put(element.getID(), element);
+ }
+ }
+ return elements;
+ }
+
+ private static ANQPElement buildElement(String text) throws ProtocolException {
+ int separator = text.indexOf('=');
+ if (separator < 0) {
+ return null;
+ }
+
+ String elementName = text.substring(0, separator);
+ Constants.ANQPElementType elementType = sWpsNames.get(elementName);
+ if (elementType == null) {
+ return null;
+ }
+
+ byte[] payload;
+ try {
+ payload = Utils.hexToBytes(text.substring(separator + 1));
+ }
+ catch (NumberFormatException nfe) {
+ Log.e(Utils.hs2LogTag(SupplicantBridge.class), "Failed to parse hex string");
+ return null;
+ }
+ return Constants.getANQPElementID(elementType) != null ?
+ ANQPFactory.buildElement(ByteBuffer.wrap(payload), elementType, payload.length) :
+ ANQPFactory.buildHS20Element(elementType,
+ ByteBuffer.wrap(payload).order(ByteOrder.LITTLE_ENDIAN));
+ }
+
+ private static String mapEAPMethodName(EAP.EAPMethodID eapMethodID) {
+ switch (eapMethodID) {
+ case EAP_AKA:
+ return "AKA";
+ case EAP_AKAPrim:
+ return "AKA'"; // eap.c:1514
+ case EAP_SIM:
+ return "SIM";
+ case EAP_TLS:
+ return "TLS";
+ case EAP_TTLS:
+ return "TTLS";
+ default:
+ throw new IllegalArgumentException("No mapping for " + eapMethodID);
+ }
+ }
+
+ private static final Map<Character,Integer> sMappings = new HashMap<Character, Integer>();
+
+ static {
+ sMappings.put('\\', (int)'\\');
+ sMappings.put('"', (int)'"');
+ sMappings.put('e', 0x1b);
+ sMappings.put('n', (int)'\n');
+ sMappings.put('r', (int)'\n');
+ sMappings.put('t', (int)'\t');
+ }
+
+ public static String unescapeSSID(String ssid) {
+
+ CharIterator chars = new CharIterator(ssid);
+ byte[] octets = new byte[ssid.length()];
+ int bo = 0;
+
+ while (chars.hasNext()) {
+ char ch = chars.next();
+ if (ch != '\\' || ! chars.hasNext()) {
+ octets[bo++] = (byte)ch;
+ }
+ else {
+ char suffix = chars.next();
+ Integer mapped = sMappings.get(suffix);
+ if (mapped != null) {
+ octets[bo++] = mapped.byteValue();
+ }
+ else if (suffix == 'x' && chars.hasDoubleHex()) {
+ octets[bo++] = (byte)chars.nextDoubleHex();
+ }
+ else {
+ octets[bo++] = '\\';
+ octets[bo++] = (byte)suffix;
+ }
+ }
+ }
+
+ boolean asciiOnly = true;
+ for (byte b : octets) {
+ if ((b&0x80) != 0) {
+ asciiOnly = false;
+ break;
+ }
+ }
+ if (asciiOnly) {
+ return new String(octets, 0, bo, StandardCharsets.UTF_8);
+ } else {
+ try {
+ // If UTF-8 decoding is successful it is almost certainly UTF-8
+ CharBuffer cb = StandardCharsets.UTF_8.newDecoder().decode(
+ ByteBuffer.wrap(octets, 0, bo));
+ return cb.toString();
+ } catch (CharacterCodingException cce) {
+ return new String(octets, 0, bo, StandardCharsets.ISO_8859_1);
+ }
+ }
+ }
+
+ private static class CharIterator {
+ private final String mString;
+ private int mPosition;
+ private int mHex;
+
+ private CharIterator(String s) {
+ mString = s;
+ }
+
+ private boolean hasNext() {
+ return mPosition < mString.length();
+ }
+
+ private char next() {
+ return mString.charAt(mPosition++);
+ }
+
+ private boolean hasDoubleHex() {
+ if (mString.length() - mPosition < 2) {
+ return false;
+ }
+ int nh = Utils.fromHex(mString.charAt(mPosition), true);
+ if (nh < 0) {
+ return false;
+ }
+ int nl = Utils.fromHex(mString.charAt(mPosition + 1), true);
+ if (nl < 0) {
+ return false;
+ }
+ mPosition += 2;
+ mHex = (nh << 4) | nl;
+ return true;
+ }
+
+ private int nextDoubleHex() {
+ return mHex;
+ }
+ }
+
+ private static final String[] TestStrings = {
+ "test-ssid",
+ "test\\nss\\tid",
+ "test\\x2d\\x5f\\nss\\tid",
+ "test\\x2d\\x5f\\nss\\tid\\\\",
+ "test\\x2d\\x5f\\nss\\tid\\n",
+ "test\\x2d\\x5f\\nss\\tid\\x4a",
+ "another\\",
+ "an\\other",
+ "another\\x2"
+ };
+
+ public static void main(String[] args) {
+ for (String string : TestStrings) {
+ System.out.println(unescapeSSID(string));
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/Utils.java b/service/java/com/android/server/wifi/hotspot2/Utils.java
new file mode 100644
index 0000000..aa84853
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/Utils.java
@@ -0,0 +1,267 @@
+package com.android.server.wifi.hotspot2;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.TimeZone;
+
+import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
+import static com.android.server.wifi.anqp.Constants.NIBBLE_MASK;
+
+public abstract class Utils {
+
+ public static final long UNSET_TIME = -1;
+
+ private static final String[] PLMNText = {"org", "3gppnetwork", "mcc*", "mnc*", "wlan" };
+
+ public static String hs2LogTag(Class c) {
+ return "HS20";
+ }
+
+ public static List<String> splitDomain(String domain) {
+
+ if (domain.endsWith("."))
+ domain = domain.substring(0, domain.length() - 1);
+ int at = domain.indexOf('@');
+ if (at >= 0)
+ domain = domain.substring(at + 1);
+
+ String[] labels = domain.toLowerCase().split("\\.");
+ LinkedList<String> labelList = new LinkedList<String>();
+ for (String label : labels) {
+ labelList.addFirst(label);
+ }
+
+ return labelList;
+ }
+
+ public static long parseMac(String s) {
+
+ long mac = 0;
+ int count = 0;
+ for (int n = 0; n < s.length(); n++) {
+ int nibble = Utils.fromHex(s.charAt(n), true); // Set lenient to not blow up on ':'
+ if (nibble >= 0) { // ... and use only legit hex.
+ mac = (mac << 4) | nibble;
+ count++;
+ }
+ }
+ if (count < 12 || (count&1) == 1) {
+ throw new IllegalArgumentException("Bad MAC address: '" + s + "'");
+ }
+ return mac;
+ }
+
+ public static String getMccMnc(List<String> domain) {
+ if (domain.size() != PLMNText.length) {
+ return null;
+ }
+
+ for (int n = 0; n < PLMNText.length; n++ ) {
+ String expect = PLMNText[n];
+ int len = expect.endsWith("*") ? expect.length() - 1 : expect.length();
+ if (!domain.get(n).regionMatches(0, expect, 0, len)) {
+ return null;
+ }
+ }
+
+ String prefix = domain.get(2).substring(3) + domain.get(3).substring(3);
+ for (int n = 0; n < prefix.length(); n++) {
+ char ch = prefix.charAt(n);
+ if (ch < '0' || ch > '9') {
+ return null;
+ }
+ }
+ return prefix;
+ }
+
+ public static String roamingConsortiumsToString(long[] ois) {
+ if (ois == null) {
+ return "null";
+ }
+ List<Long> list = new ArrayList<Long>(ois.length);
+ for (long oi : ois) {
+ list.add(oi);
+ }
+ return roamingConsortiumsToString(list);
+ }
+
+ public static String roamingConsortiumsToString(Collection<Long> ois) {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (long oi : ois) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(", ");
+ }
+ if (Long.numberOfLeadingZeros(oi) > 40) {
+ sb.append(String.format("%06x", oi));
+ } else {
+ sb.append(String.format("%010x", oi));
+ }
+ }
+ return sb.toString();
+ }
+
+ public static String toUnicodeEscapedString(String s) {
+ StringBuilder sb = new StringBuilder(s.length());
+ for (int n = 0; n < s.length(); n++) {
+ char ch = s.charAt(n);
+ if (ch>= ' ' && ch < 127) {
+ sb.append(ch);
+ }
+ else {
+ sb.append("\\u").append(String.format("%04x", (int)ch));
+ }
+ }
+ return sb.toString();
+ }
+
+ public static String toHexString(byte[] data) {
+ if (data == null) {
+ return "null";
+ }
+ StringBuilder sb = new StringBuilder(data.length * 3);
+
+ boolean first = true;
+ for (byte b : data) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(' ');
+ }
+ sb.append(String.format("%02x", b & BYTE_MASK));
+ }
+ return sb.toString();
+ }
+
+ public static String toHex(byte[] octets) {
+ StringBuilder sb = new StringBuilder(octets.length * 2);
+ for (byte o : octets) {
+ sb.append(String.format("%02x", o & BYTE_MASK));
+ }
+ return sb.toString();
+ }
+
+ public static byte[] hexToBytes(String text) {
+ if ((text.length() & 1) == 1) {
+ throw new NumberFormatException("Odd length hex string: " + text.length());
+ }
+ byte[] data = new byte[text.length() >> 1];
+ int position = 0;
+ for (int n = 0; n < text.length(); n += 2) {
+ data[position] =
+ (byte) (((fromHex(text.charAt(n), false) & NIBBLE_MASK) << 4) |
+ (fromHex(text.charAt(n + 1), false) & NIBBLE_MASK));
+ position++;
+ }
+ return data;
+ }
+
+ public static int fromHex(char ch, boolean lenient) throws NumberFormatException {
+ if (ch <= '9' && ch >= '0') {
+ return ch - '0';
+ } else if (ch >= 'a' && ch <= 'f') {
+ return ch + 10 - 'a';
+ } else if (ch <= 'F' && ch >= 'A') {
+ return ch + 10 - 'A';
+ } else if (lenient) {
+ return -1;
+ } else {
+ throw new NumberFormatException("Bad hex-character: " + ch);
+ }
+ }
+
+ private static char toAscii(int b) {
+ return b >= ' ' && b < 0x7f ? (char) b : '.';
+ }
+
+ static boolean isDecimal(String s) {
+ for (int n = 0; n < s.length(); n++) {
+ char ch = s.charAt(n);
+ if (ch < '0' || ch > '9') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static <T extends Comparable> int compare(Comparable<T> c1, T c2) {
+ if (c1 == null) {
+ return c2 == null ? 0 : -1;
+ }
+ else if (c2 == null) {
+ return 1;
+ }
+ else {
+ return c1.compareTo(c2);
+ }
+ }
+
+ public static String bytesToBingoCard(ByteBuffer data, int len) {
+ ByteBuffer dup = data.duplicate();
+ dup.limit(dup.position() + len);
+ return bytesToBingoCard(dup);
+ }
+
+ public static String bytesToBingoCard(ByteBuffer data) {
+ ByteBuffer dup = data.duplicate();
+ StringBuilder sbx = new StringBuilder();
+ while (dup.hasRemaining()) {
+ sbx.append(String.format("%02x ", dup.get() & BYTE_MASK));
+ }
+ dup = data.duplicate();
+ sbx.append(' ');
+ while (dup.hasRemaining()) {
+ sbx.append(String.format("%c", toAscii(dup.get() & BYTE_MASK)));
+ }
+ return sbx.toString();
+ }
+
+ public static String toHMS(long millis) {
+ long time = millis >= 0 ? millis : -millis;
+ long tmp = time / 1000L;
+ long ms = time - tmp * 1000L;
+
+ time = tmp;
+ tmp /= 60L;
+ long s = time - tmp * 60L;
+
+ time = tmp;
+ tmp /= 60L;
+ long m = time - tmp * 60L;
+
+ return String.format("%s%d:%02d:%02d.%03d", millis < 0 ? "-" : "", tmp, m, s, ms);
+ }
+
+ public static String toUTCString(long ms) {
+ if (ms < 0) {
+ return "unset";
+ }
+ Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ c.setTimeInMillis(ms);
+ return String.format("%4d/%02d/%02d %2d:%02d:%02dZ",
+ c.get(Calendar.YEAR),
+ c.get(Calendar.MONTH) + 1,
+ c.get(Calendar.DAY_OF_MONTH),
+ c.get(Calendar.HOUR_OF_DAY),
+ c.get(Calendar.MINUTE),
+ c.get(Calendar.SECOND));
+ }
+
+ public static String unquote(String s) {
+ if (s == null) {
+ return null;
+ }
+ else if (s.startsWith("\"") && s.endsWith("\"")) {
+ return s.substring(1, s.length()-1);
+ }
+ else {
+ return s;
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/MOManager.java b/service/java/com/android/server/wifi/hotspot2/omadm/MOManager.java
new file mode 100644
index 0000000..0ea75ae
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/omadm/MOManager.java
@@ -0,0 +1,711 @@
+package com.android.server.wifi.hotspot2.omadm;
+
+import android.util.Base64;
+import android.util.Log;
+
+import com.android.server.wifi.IMSIParameter;
+import com.android.server.wifi.anqp.eap.EAP;
+import com.android.server.wifi.anqp.eap.EAPMethod;
+import com.android.server.wifi.anqp.eap.ExpandedEAPMethod;
+import com.android.server.wifi.anqp.eap.InnerAuthEAP;
+import com.android.server.wifi.anqp.eap.NonEAPInnerAuth;
+import com.android.server.wifi.hotspot2.Utils;
+import com.android.server.wifi.hotspot2.pps.Credential;
+import com.android.server.wifi.hotspot2.pps.HomeSP;
+
+import org.xml.sax.SAXException;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TimeZone;
+
+/**
+ * Handles provisioning of PerProviderSubscription data.
+ */
+public class MOManager {
+
+ public static final String TAG_AAAServerTrustRoot = "AAAServerTrustRoot";
+ public static final String TAG_AbleToShare = "AbleToShare";
+ public static final String TAG_CertificateType = "CertificateType";
+ public static final String TAG_CertSHA256Fingerprint = "CertSHA256Fingerprint";
+ public static final String TAG_CertURL = "CertURL";
+ public static final String TAG_CheckAAAServerCertStatus = "CheckAAAServerCertStatus";
+ public static final String TAG_Country = "Country";
+ public static final String TAG_CreationDate = "CreationDate";
+ public static final String TAG_Credential = "Credential";
+ public static final String TAG_CredentialPriority = "CredentialPriority";
+ public static final String TAG_DataLimit = "DataLimit";
+ public static final String TAG_DigitalCertificate = "DigitalCertificate";
+ public static final String TAG_DLBandwidth = "DLBandwidth";
+ public static final String TAG_EAPMethod = "EAPMethod";
+ public static final String TAG_EAPType = "EAPType";
+ public static final String TAG_ExpirationDate = "ExpirationDate";
+ public static final String TAG_Extension = "Extension";
+ public static final String TAG_FQDN = "FQDN";
+ public static final String TAG_FQDN_Match = "FQDN_Match";
+ public static final String TAG_FriendlyName = "FriendlyName";
+ public static final String TAG_HESSID = "HESSID";
+ public static final String TAG_HomeOI = "HomeOI";
+ public static final String TAG_HomeOIList = "HomeOIList";
+ public static final String TAG_HomeOIRequired = "HomeOIRequired";
+ public static final String TAG_HomeSP = "HomeSP";
+ public static final String TAG_IconURL = "IconURL";
+ public static final String TAG_IMSI = "IMSI";
+ public static final String TAG_InnerEAPType = "InnerEAPType";
+ public static final String TAG_InnerMethod = "InnerMethod";
+ public static final String TAG_InnerVendorID = "InnerVendorID";
+ public static final String TAG_InnerVendorType = "InnerVendorType";
+ public static final String TAG_IPProtocol = "IPProtocol";
+ public static final String TAG_MachineManaged = "MachineManaged";
+ public static final String TAG_MaximumBSSLoadValue = "MaximumBSSLoadValue";
+ public static final String TAG_MinBackhaulThreshold = "MinBackhaulThreshold";
+ public static final String TAG_NetworkID = "NetworkID";
+ public static final String TAG_NetworkType = "NetworkType";
+ public static final String TAG_Other = "Other";
+ public static final String TAG_OtherHomePartners = "OtherHomePartners";
+ public static final String TAG_Password = "Password";
+ public static final String TAG_PerProviderSubscription = "PerProviderSubscription";
+ public static final String TAG_Policy = "Policy";
+ public static final String TAG_PolicyUpdate = "PolicyUpdate";
+ public static final String TAG_PortNumber = "PortNumber";
+ public static final String TAG_PreferredRoamingPartnerList = "PreferredRoamingPartnerList";
+ public static final String TAG_Priority = "Priority";
+ public static final String TAG_Realm = "Realm";
+ public static final String TAG_RequiredProtoPortTuple = "RequiredProtoPortTuple";
+ public static final String TAG_Restriction = "Restriction";
+ public static final String TAG_RoamingConsortiumOI = "RoamingConsortiumOI";
+ public static final String TAG_SIM = "SIM";
+ public static final String TAG_SoftTokenApp = "SoftTokenApp";
+ public static final String TAG_SPExclusionList = "SPExclusionList";
+ public static final String TAG_SSID = "SSID";
+ public static final String TAG_StartDate = "StartDate";
+ public static final String TAG_SubscriptionParameters = "SubscriptionParameters";
+ public static final String TAG_SubscriptionUpdate = "SubscriptionUpdate";
+ public static final String TAG_TimeLimit = "TimeLimit";
+ public static final String TAG_TrustRoot = "TrustRoot";
+ public static final String TAG_TypeOfSubscription = "TypeOfSubscription";
+ public static final String TAG_ULBandwidth = "ULBandwidth";
+ public static final String TAG_UpdateIdentifier = "UpdateIdentifier";
+ public static final String TAG_UpdateInterval = "UpdateInterval";
+ public static final String TAG_UpdateMethod = "UpdateMethod";
+ public static final String TAG_URI = "URI";
+ public static final String TAG_UsageLimits = "UsageLimits";
+ public static final String TAG_UsageTimePeriod = "UsageTimePeriod";
+ public static final String TAG_Username = "Username";
+ public static final String TAG_UsernamePassword = "UsernamePassword";
+ public static final String TAG_VendorId = "VendorId";
+ public static final String TAG_VendorType = "VendorType";
+
+ private static final DateFormat DTFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+
+ static {
+ DTFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+ private final File mPpsFile;
+ private final boolean mEnabled;
+ private final Map<String, HomeSP> mSPs;
+
+ public MOManager(File ppsFile, boolean hs2enabled) {
+ mPpsFile = ppsFile;
+ mEnabled = hs2enabled;
+ mSPs = new HashMap<>();
+ }
+
+ public File getPpsFile() {
+ return mPpsFile;
+ }
+
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ public boolean isConfigured() {
+ return mEnabled && !mSPs.isEmpty();
+ }
+
+ public Map<String, HomeSP> getLoadedSPs() {
+ return Collections.unmodifiableMap(mSPs);
+ }
+
+ public List<HomeSP> loadAllSPs() throws IOException {
+
+ if (!mEnabled || !mPpsFile.exists()) {
+ return Collections.emptyList();
+ }
+
+ try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(mPpsFile))) {
+ MOTree moTree = MOTree.unmarshal(in);
+ mSPs.clear();
+ if (moTree == null) {
+ return Collections.emptyList(); // Empty file
+ }
+
+ List<HomeSP> sps = buildSPs(moTree);
+ if (sps != null) {
+ for (HomeSP sp : sps) {
+ if (mSPs.put(sp.getFQDN(), sp) != null) {
+ throw new OMAException("Multiple SPs for FQDN '" + sp.getFQDN() + "'");
+ } else {
+ Log.d(Utils.hs2LogTag(getClass()), "retrieved " + sp.getFQDN() + " from PPS");
+ }
+ }
+ return sps;
+
+ } else {
+ throw new OMAException("Failed to build HomeSP");
+ }
+ }
+ }
+
+ public static HomeSP buildSP(String xml) throws IOException, SAXException {
+ OMAParser omaParser = new OMAParser();
+ MOTree tree = omaParser.parse(xml, OMAConstants.LOC_PPS + ":1.0");
+ List<HomeSP> spList = buildSPs(tree);
+ if (spList.size() != 1) {
+ throw new OMAException("Expected exactly one HomeSP, got " + spList.size());
+ }
+ return spList.iterator().next();
+ }
+
+ public HomeSP addSP(String xml) throws IOException, SAXException {
+ OMAParser omaParser = new OMAParser();
+ MOTree tree = omaParser.parse(xml, OMAConstants.LOC_PPS + ":1.0");
+ List<HomeSP> spList = buildSPs(tree);
+ if (spList.size() != 1) {
+ throw new OMAException("Expected exactly one HomeSP, got " + spList.size());
+ }
+ HomeSP sp = spList.iterator().next();
+ String fqdn = sp.getFQDN();
+ if (mSPs.put(fqdn, sp) != null) {
+ throw new OMAException("SP " + fqdn + " already exists");
+ }
+
+ BufferedOutputStream out = null;
+ try {
+ out = new BufferedOutputStream(new FileOutputStream(mPpsFile, true));
+ tree.marshal(out);
+ out.flush();
+ } finally {
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException ioe) {
+ /**/
+ }
+ }
+ }
+
+ return sp;
+ }
+
+ public HomeSP getHomeSP(String fqdn) {
+ return mSPs.get(fqdn);
+ }
+
+ public void addSP(HomeSP homeSP) throws IOException {
+ if (!mEnabled) {
+ throw new IOException("HS2.0 not enabled on this device");
+ }
+ if (mSPs.containsKey(homeSP.getFQDN())) {
+ Log.d(Utils.hs2LogTag(getClass()), "HS20 profile for " +
+ homeSP.getFQDN() + " already exists");
+ return;
+ }
+ Log.d(Utils.hs2LogTag(getClass()), "Adding new HS20 profile for " + homeSP.getFQDN());
+ mSPs.put(homeSP.getFQDN(), homeSP);
+ writeMO(mSPs.values(), mPpsFile);
+ }
+
+ public void removeSP(String fqdn) throws IOException {
+ if (mSPs.remove(fqdn) == null) {
+ Log.d(Utils.hs2LogTag(getClass()), "No HS20 profile to delete for " + fqdn);
+ return;
+ }
+ Log.d(Utils.hs2LogTag(getClass()), "Deleting HS20 profile for " + fqdn);
+ writeMO(mSPs.values(), mPpsFile);
+ }
+
+ public void updateAndSaveAllSps(Collection<HomeSP> homeSPs) throws IOException {
+
+ boolean dirty = false;
+ List<HomeSP> newSet = new ArrayList<>(homeSPs.size());
+
+ Map<String, HomeSP> spClone = new HashMap<>(mSPs);
+ for (HomeSP homeSP : homeSPs) {
+ Log.d(Utils.hs2LogTag(getClass()), "Passed HomeSP: " + homeSP);
+ HomeSP existing = spClone.remove(homeSP.getFQDN());
+ if (existing == null) {
+ dirty = true;
+ newSet.add(homeSP);
+ Log.d(Utils.hs2LogTag(getClass()), "New HomeSP");
+ }
+ else if (!homeSP.deepEquals(existing)) {
+ dirty = true;
+ newSet.add(homeSP.getClone(existing.getCredential().getPassword()));
+ Log.d(Utils.hs2LogTag(getClass()), "Non-equal HomeSP: " + existing);
+ }
+ else {
+ newSet.add(existing);
+ Log.d(Utils.hs2LogTag(getClass()), "Keeping HomeSP: " + existing);
+ }
+ }
+
+ Log.d(Utils.hs2LogTag(getClass()),
+ String.format("Saving all SPs (%s): current %s (%d), new %s (%d)",
+ dirty ? "dirty" : "clean",
+ fqdnList(mSPs.values()), mSPs.size(),
+ fqdnList(newSet), newSet.size()));
+
+ if (!dirty && spClone.isEmpty()) {
+ Log.d(Utils.hs2LogTag(getClass()), "Not persisting");
+ return;
+ }
+
+ rewriteMO(newSet, mSPs, mPpsFile);
+ }
+
+ private static void rewriteMO(Collection<HomeSP> homeSPs, Map<String, HomeSP> current, File f)
+ throws IOException {
+
+ current.clear();
+
+ OMAConstructed ppsNode = new OMAConstructed(null, TAG_PerProviderSubscription, null);
+ int instance = 0;
+ for (HomeSP homeSP : homeSPs) {
+ buildHomeSPTree(homeSP, ppsNode, instance++);
+ current.put(homeSP.getFQDN(), homeSP);
+ }
+
+ MOTree tree = new MOTree(OMAConstants.LOC_PPS + ":1.0", "1.2", ppsNode);
+ try (BufferedOutputStream out =
+ new BufferedOutputStream(new FileOutputStream(f, false))) {
+ tree.marshal(out);
+ out.flush();
+ }
+ }
+
+ private static void writeMO(Collection<HomeSP> homeSPs, File f) throws IOException {
+
+ OMAConstructed ppsNode = new OMAConstructed(null, TAG_PerProviderSubscription, null);
+ int instance = 0;
+ for (HomeSP homeSP : homeSPs) {
+ buildHomeSPTree(homeSP, ppsNode, instance++);
+ }
+
+ MOTree tree = new MOTree(OMAConstants.LOC_PPS + ":1.0", "1.2", ppsNode);
+ try (BufferedOutputStream out =
+ new BufferedOutputStream(new FileOutputStream(f, false))) {
+ tree.marshal(out);
+ out.flush();
+ }
+ }
+
+ private static String fqdnList(Collection<HomeSP> sps) {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (HomeSP sp : sps) {
+ if (first) {
+ first = false;
+ }
+ else {
+ sb.append(", ");
+ }
+ sb.append(sp.getFQDN());
+ }
+ return sb.toString();
+ }
+
+ private static void buildHomeSPTree(HomeSP homeSP, OMAConstructed root, int spInstance)
+ throws IOException {
+ OMANode providerSubNode = root.addChild(getInstanceString(spInstance), null, null, null);
+
+ // The HomeSP:
+ OMANode homeSpNode = providerSubNode.addChild(TAG_HomeSP, null, null, null);
+ if (!homeSP.getSSIDs().isEmpty()) {
+ OMAConstructed nwkIDNode =
+ (OMAConstructed) homeSpNode.addChild(TAG_NetworkID, null, null, null);
+ int instance = 0;
+ for (Map.Entry<String, Long> entry : homeSP.getSSIDs().entrySet()) {
+ OMAConstructed inode =
+ (OMAConstructed) nwkIDNode.addChild(getInstanceString(instance++), null, null, null);
+ inode.addChild(TAG_SSID, null, entry.getKey(), null);
+ if (entry.getValue() != null) {
+ inode.addChild(TAG_HESSID, null, String.format("%012x", entry.getValue()), null);
+ }
+ }
+ }
+
+ homeSpNode.addChild(TAG_FriendlyName, null, homeSP.getFriendlyName(), null);
+
+ if (homeSP.getIconURL() != null) {
+ homeSpNode.addChild(TAG_IconURL, null, homeSP.getIconURL(), null);
+ }
+
+ homeSpNode.addChild(TAG_FQDN, null, homeSP.getFQDN(), null);
+
+ if (!homeSP.getMatchAllOIs().isEmpty() || !homeSP.getMatchAnyOIs().isEmpty()) {
+ OMAConstructed homeOIList =
+ (OMAConstructed) homeSpNode.addChild(TAG_HomeOIList, null, null, null);
+
+ int instance = 0;
+ for (Long oi : homeSP.getMatchAllOIs()) {
+ OMAConstructed inode =
+ (OMAConstructed) homeOIList.addChild(getInstanceString(instance++),
+ null, null, null);
+ inode.addChild(TAG_HomeOI, null, String.format("%x", oi), null);
+ inode.addChild(TAG_HomeOIRequired, null, "TRUE", null);
+ }
+ for (Long oi : homeSP.getMatchAnyOIs()) {
+ OMAConstructed inode =
+ (OMAConstructed) homeOIList.addChild(getInstanceString(instance++),
+ null, null, null);
+ inode.addChild(TAG_HomeOI, null, String.format("%x", oi), null);
+ inode.addChild(TAG_HomeOIRequired, null, "FALSE", null);
+ }
+ }
+
+ if (!homeSP.getOtherHomePartners().isEmpty()) {
+ OMAConstructed otherPartners =
+ (OMAConstructed) homeSpNode.addChild(TAG_OtherHomePartners, null, null, null);
+ int instance = 0;
+ for (String fqdn : homeSP.getOtherHomePartners()) {
+ OMAConstructed inode =
+ (OMAConstructed) otherPartners.addChild(getInstanceString(instance++),
+ null, null, null);
+ inode.addChild(TAG_FQDN, null, fqdn, null);
+ }
+ }
+
+ if (!homeSP.getRoamingConsortiums().isEmpty()) {
+ homeSpNode.addChild(TAG_RoamingConsortiumOI, null, getRCList(homeSP.getRoamingConsortiums()), null);
+ }
+
+ // The Credential:
+ OMANode credentialNode = providerSubNode.addChild(TAG_Credential, null, null, null);
+ Credential cred = homeSP.getCredential();
+ EAPMethod method = cred.getEAPMethod();
+
+ if (cred.getCtime() > 0) {
+ credentialNode.addChild(TAG_CreationDate,
+ null, DTFormat.format(new Date(cred.getCtime())), null);
+ }
+ if (cred.getExpTime() > 0) {
+ credentialNode.addChild(TAG_ExpirationDate,
+ null, DTFormat.format(new Date(cred.getExpTime())), null);
+ }
+
+ if (method.getEAPMethodID() == EAP.EAPMethodID.EAP_SIM
+ || method.getEAPMethodID() == EAP.EAPMethodID.EAP_AKA
+ || method.getEAPMethodID() == EAP.EAPMethodID.EAP_AKAPrim) {
+
+ OMANode simNode = credentialNode.addChild(TAG_SIM, null, null, null);
+ simNode.addChild(TAG_IMSI, null, cred.getImsi().toString(), null);
+ simNode.addChild(TAG_EAPType, null,
+ Integer.toString(EAP.mapEAPMethod(method.getEAPMethodID())), null);
+
+ } else if (method.getEAPMethodID() == EAP.EAPMethodID.EAP_TTLS) {
+
+ OMANode unpNode = credentialNode.addChild(TAG_UsernamePassword, null, null, null);
+ unpNode.addChild(TAG_Username, null, cred.getUserName(), null);
+ unpNode.addChild(TAG_Password, null,
+ Base64.encodeToString(cred.getPassword().getBytes(StandardCharsets.UTF_8),
+ Base64.DEFAULT), null);
+ OMANode eapNode = unpNode.addChild(TAG_EAPMethod, null, null, null);
+ eapNode.addChild(TAG_EAPType, null,
+ Integer.toString(EAP.mapEAPMethod(method.getEAPMethodID())), null);
+ eapNode.addChild(TAG_InnerMethod, null,
+ ((NonEAPInnerAuth) method.getAuthParam()).getOMAtype(), null);
+
+ } else if (method.getEAPMethodID() == EAP.EAPMethodID.EAP_TLS) {
+
+ OMANode certNode = credentialNode.addChild(TAG_DigitalCertificate, null, null, null);
+ certNode.addChild(TAG_CertificateType, null, Credential.CertTypeX509, null);
+ certNode.addChild(TAG_CertSHA256Fingerprint, null,
+ Utils.toHex(cred.getFingerPrint()), null);
+
+ } else {
+ throw new OMAException("Invalid credential on " + homeSP.getFQDN());
+ }
+
+ credentialNode.addChild(TAG_Realm, null, cred.getRealm(), null);
+
+ // !!! Note: This node defines CRL checking through OSCP, I suspect we won't be able
+ // to do that so it is commented out:
+ //credentialNode.addChild(TAG_CheckAAAServerCertStatus, null, "TRUE", null);
+ }
+
+ private static String getInstanceString(int instance) {
+ return "i" + instance;
+ }
+
+ private static String getRCList(Collection<Long> rcs) {
+ StringBuilder builder = new StringBuilder();
+ boolean first = true;
+ for (Long roamingConsortium : rcs) {
+ if (first) {
+ first = false;
+ }
+ else {
+ builder.append(',');
+ }
+ builder.append(String.format("%x", roamingConsortium));
+ }
+ return builder.toString();
+ }
+
+ private static List<HomeSP> buildSPs(MOTree moTree) throws OMAException {
+ OMAConstructed spList;
+ if (moTree.getRoot().getName().equals(TAG_PerProviderSubscription)) {
+ // The PPS file is rooted at PPS instead of MgmtTree to conserve space
+ spList = moTree.getRoot();
+ }
+ else {
+ List<String> spPath = Arrays.asList(TAG_PerProviderSubscription);
+ spList = moTree.getRoot().getListValue(spPath.iterator());
+ }
+
+ List<HomeSP> homeSPs = new ArrayList<>();
+
+ if (spList == null) {
+ return homeSPs;
+ }
+ for (OMANode spRoot : spList.getChildren()) {
+ homeSPs.add(buildHomeSP(spRoot));
+ }
+
+ return homeSPs;
+ }
+
+ private static HomeSP buildHomeSP(OMANode ppsRoot) throws OMAException {
+ OMANode spRoot = ppsRoot.getChild(TAG_HomeSP);
+
+ String fqdn = spRoot.getScalarValue(Arrays.asList(TAG_FQDN).iterator());
+ String friendlyName = spRoot.getScalarValue(Arrays.asList(TAG_FriendlyName).iterator());
+ String iconURL = spRoot.getScalarValue(Arrays.asList(TAG_IconURL).iterator());
+
+ HashSet<Long> roamingConsortiums = new HashSet<>();
+ String oiString = spRoot.getScalarValue(Arrays.asList(TAG_RoamingConsortiumOI).iterator());
+ if (oiString != null) {
+ for (String oi : oiString.split(",")) {
+ roamingConsortiums.add(Long.parseLong(oi.trim(), 16));
+ }
+ }
+
+ Map<String, Long> ssids = new HashMap<>();
+
+ OMANode ssidListNode = spRoot.getListValue(Arrays.asList(TAG_NetworkID).iterator());
+ if (ssidListNode != null) {
+ for (OMANode ssidRoot : ssidListNode.getChildren()) {
+ OMANode hessidNode = ssidRoot.getChild(TAG_HESSID);
+ ssids.put(ssidRoot.getChild(TAG_SSID).getValue(), getMac(hessidNode));
+ }
+ }
+
+ Set<Long> matchAnyOIs = new HashSet<>();
+ List<Long> matchAllOIs = new ArrayList<>();
+ OMANode homeOIListNode = spRoot.getListValue(Arrays.asList(TAG_HomeOIList).iterator());
+ if (homeOIListNode != null) {
+ for (OMANode homeOIRoot : homeOIListNode.getChildren()) {
+ String homeOI = homeOIRoot.getChild(TAG_HomeOI).getValue();
+ if (Boolean.parseBoolean(homeOIRoot.getChild(TAG_HomeOIRequired).getValue())) {
+ matchAllOIs.add(Long.parseLong(homeOI, 16));
+ } else {
+ matchAnyOIs.add(Long.parseLong(homeOI, 16));
+ }
+ }
+ }
+
+ Set<String> otherHomePartners = new HashSet<>();
+ OMANode otherListNode =
+ spRoot.getListValue(Arrays.asList(TAG_OtherHomePartners).iterator());
+ if (otherListNode != null) {
+ for (OMANode fqdnNode : otherListNode.getChildren()) {
+ otherHomePartners.add(fqdnNode.getChild(TAG_FQDN).getValue());
+ }
+ }
+
+ Credential credential = buildCredential(ppsRoot.getChild(TAG_Credential));
+
+ return new HomeSP(ssids, fqdn, roamingConsortiums, otherHomePartners,
+ matchAnyOIs, matchAllOIs, friendlyName, iconURL, credential);
+ }
+
+ private static Credential buildCredential(OMANode credNode) throws OMAException {
+ long ctime = getTime(credNode.getChild(TAG_CreationDate));
+ long expTime = getTime(credNode.getChild(TAG_ExpirationDate));
+ String realm = getString(credNode.getChild(TAG_Realm));
+ boolean checkAAACert = getBoolean(credNode.getChild(TAG_CheckAAAServerCertStatus));
+
+ OMANode unNode = credNode.getChild(TAG_UsernamePassword);
+ OMANode certNode = credNode.getChild(TAG_DigitalCertificate);
+ OMANode simNode = credNode.getChild(TAG_SIM);
+
+ int alternatives = 0;
+ alternatives += unNode != null ? 1 : 0;
+ alternatives += certNode != null ? 1 : 0;
+ alternatives += simNode != null ? 1 : 0;
+ if (alternatives != 1) {
+ throw new OMAException("Expected exactly one credential type, got " + alternatives);
+ }
+
+ if (unNode != null) {
+ String userName = getString(unNode.getChild(TAG_Username));
+ String password = getString(unNode.getChild(TAG_Password));
+ boolean machineManaged = getBoolean(unNode.getChild(TAG_MachineManaged));
+ String softTokenApp = getString(unNode.getChild(TAG_SoftTokenApp));
+ boolean ableToShare = getBoolean(unNode.getChild(TAG_AbleToShare));
+
+ OMANode eapMethodNode = unNode.getChild(TAG_EAPMethod);
+ int eapID = getInteger(eapMethodNode.getChild(TAG_EAPType));
+
+ EAP.EAPMethodID eapMethodID = EAP.mapEAPMethod(eapID);
+ if (eapMethodID == null) {
+ throw new OMAException("Unknown EAP method: " + eapID);
+ }
+
+ Long vid = getOptionalInteger(eapMethodNode.getChild(TAG_VendorId));
+ Long vtype = getOptionalInteger(eapMethodNode.getChild(TAG_VendorType));
+ Long innerEAPType = getOptionalInteger(eapMethodNode.getChild(TAG_InnerEAPType));
+ EAP.EAPMethodID innerEAPMethod = null;
+ if (innerEAPType != null) {
+ innerEAPMethod = EAP.mapEAPMethod(innerEAPType.intValue());
+ if (innerEAPMethod == null) {
+ throw new OMAException("Bad inner EAP method: " + innerEAPType);
+ }
+ }
+
+ Long innerVid = getOptionalInteger(eapMethodNode.getChild(TAG_InnerVendorID));
+ Long innerVtype = getOptionalInteger(eapMethodNode.getChild(TAG_InnerVendorType));
+ String innerNonEAPMethod = getString(eapMethodNode.getChild(TAG_InnerMethod));
+
+ EAPMethod eapMethod;
+ if (innerEAPMethod != null) {
+ eapMethod = new EAPMethod(eapMethodID, new InnerAuthEAP(innerEAPMethod));
+ } else if (vid != null) {
+ eapMethod = new EAPMethod(eapMethodID,
+ new ExpandedEAPMethod(EAP.AuthInfoID.ExpandedEAPMethod,
+ vid.intValue(), vtype));
+ } else if (innerVid != null) {
+ eapMethod =
+ new EAPMethod(eapMethodID, new ExpandedEAPMethod(EAP.AuthInfoID
+ .ExpandedInnerEAPMethod, innerVid.intValue(), innerVtype));
+ } else if (innerNonEAPMethod != null) {
+ eapMethod = new EAPMethod(eapMethodID, new NonEAPInnerAuth(innerNonEAPMethod));
+ } else {
+ throw new OMAException("Incomplete set of EAP parameters");
+ }
+
+ return new Credential(ctime, expTime, realm, checkAAACert, eapMethod, userName,
+ password, machineManaged, softTokenApp, ableToShare);
+ }
+ if (certNode != null) {
+ try {
+ String certTypeString = getString(certNode.getChild(TAG_CertificateType));
+ byte[] fingerPrint = getOctets(certNode.getChild(TAG_CertSHA256Fingerprint));
+
+ EAPMethod eapMethod = new EAPMethod(EAP.EAPMethodID.EAP_TLS, null);
+
+ return new Credential(ctime, expTime, realm, checkAAACert, eapMethod,
+ Credential.mapCertType(certTypeString), fingerPrint);
+ }
+ catch (NumberFormatException nfe) {
+ throw new OMAException("Bad hex string: " + nfe.toString());
+ }
+ }
+ if (simNode != null) {
+ try {
+ IMSIParameter imsi = new IMSIParameter(getString(simNode.getChild(TAG_IMSI)));
+
+ EAPMethod eapMethod =
+ new EAPMethod(EAP.mapEAPMethod(getInteger(simNode.getChild(TAG_EAPType))),
+ null);
+
+ return new Credential(ctime, expTime, realm, checkAAACert, eapMethod, imsi);
+ }
+ catch (IOException ioe) {
+ throw new OMAException("Failed to parse IMSI: " + ioe);
+ }
+ }
+ throw new OMAException("Missing credential parameters");
+ }
+
+ private static boolean getBoolean(OMANode boolNode) {
+ return boolNode != null && Boolean.parseBoolean(boolNode.getValue());
+ }
+
+ private static String getString(OMANode stringNode) {
+ return stringNode != null ? stringNode.getValue() : null;
+ }
+
+ private static int getInteger(OMANode intNode) throws OMAException {
+ if (intNode == null) {
+ throw new OMAException("Missing integer value");
+ }
+ try {
+ return Integer.parseInt(intNode.getValue());
+ } catch (NumberFormatException nfe) {
+ throw new OMAException("Invalid integer: " + intNode.getValue());
+ }
+ }
+
+ private static Long getMac(OMANode macNode) throws OMAException {
+ if (macNode == null) {
+ return null;
+ }
+ try {
+ return Long.parseLong(macNode.getValue(), 16);
+ } catch (NumberFormatException nfe) {
+ throw new OMAException("Invalid MAC: " + macNode.getValue());
+ }
+ }
+
+ private static Long getOptionalInteger(OMANode intNode) throws OMAException {
+ if (intNode == null) {
+ return null;
+ }
+ try {
+ return Long.parseLong(intNode.getValue());
+ } catch (NumberFormatException nfe) {
+ throw new OMAException("Invalid integer: " + intNode.getValue());
+ }
+ }
+
+ private static long getTime(OMANode timeNode) throws OMAException {
+ if (timeNode == null) {
+ return Utils.UNSET_TIME;
+ }
+ String timeText = timeNode.getValue();
+ try {
+ Date date = DTFormat.parse(timeText);
+ return date.getTime();
+ } catch (ParseException pe) {
+ throw new OMAException("Badly formatted time: " + timeText);
+ }
+ }
+
+ private static byte[] getOctets(OMANode octetNode) throws OMAException {
+ if (octetNode == null) {
+ throw new OMAException("Missing byte value");
+ }
+ return Utils.hexToBytes(octetNode.getValue());
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/MOTree.java b/service/java/com/android/server/wifi/hotspot2/omadm/MOTree.java
new file mode 100644
index 0000000..96df05f
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/omadm/MOTree.java
@@ -0,0 +1,224 @@
+package com.android.server.wifi.hotspot2.omadm;
+
+import android.util.Log;
+
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+
+public class MOTree {
+ public static final String MgmtTreeTag = "MgmtTree";
+
+ private static final String NodeTag = "Node";
+ private static final String NodeNameTag = "NodeName";
+ private static final String PathTag = "Path";
+ private static final String ValueTag = "Value";
+ private static final String RTPropTag = "RTProperties";
+ private static final String TypeTag = "Type";
+ private static final String DDFNameTag = "DDFName";
+
+ private final String mUrn;
+ private final String mDtdRev;
+ private final OMAConstructed mRoot;
+
+ public MOTree(XMLNode node, String urn) throws IOException, SAXException {
+ Iterator<XMLNode> children = node.getChildren().iterator();
+
+ String dtdRev = null;
+
+ while (children.hasNext()) {
+ XMLNode child = children.next();
+ if (child.getTag().equals(OMAConstants.SyncMLVersionTag)) {
+ dtdRev = child.getText();
+ children.remove();
+ break;
+ }
+ }
+
+ mUrn = urn;
+ mDtdRev = dtdRev;
+
+ mRoot = new OMAConstructed(null, MgmtTreeTag, null);
+
+ for (XMLNode child : node.getChildren()) {
+ buildNode(mRoot, child);
+ }
+ }
+
+ public MOTree(String urn, String rev, OMAConstructed root) {
+ mUrn = urn;
+ mDtdRev = rev;
+ mRoot = root;
+ }
+
+ private static class NodeData {
+ private final String mName;
+ private String mPath;
+ private String mValue;
+
+ private NodeData(String name) {
+ mName = name;
+ }
+
+ private void setPath(String path) {
+ mPath = path;
+ }
+
+ private void setValue(String value) {
+ mValue = value;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getPath() {
+ return mPath;
+ }
+
+ public String getValue() {
+ return mValue;
+ }
+ }
+
+ private static void buildNode(OMANode parent, XMLNode node) throws IOException {
+ if (!node.getTag().equals(NodeTag))
+ throw new IOException("Node is a '" + node.getTag() + "' instead of a 'Node'");
+
+ Map<String, XMLNode> checkMap = new HashMap<String, XMLNode>(3);
+ String context = null;
+ List<NodeData> values = new ArrayList<NodeData>();
+ List<XMLNode> children = new ArrayList<XMLNode>();
+
+ NodeData curValue = null;
+
+ for (XMLNode child : node.getChildren()) {
+ XMLNode old = checkMap.put(child.getTag(), child);
+
+ if (child.getTag().equals(NodeNameTag)) {
+ if (curValue != null)
+ throw new IOException(NodeNameTag + " not expected");
+ curValue = new NodeData(child.getText());
+
+ } else if (child.getTag().equals(PathTag)) {
+ if (curValue == null || curValue.getPath() != null)
+ throw new IOException(PathTag + " not expected");
+ curValue.setPath(child.getText());
+
+ } else if (child.getTag().equals(ValueTag)) {
+ if (!children.isEmpty())
+ throw new IOException(ValueTag + " in constructed node");
+ if (curValue == null || curValue.getValue() != null)
+ throw new IOException(ValueTag + " not expected");
+ curValue.setValue(child.getText());
+ values.add(curValue);
+ curValue = null;
+
+ } else if (child.getTag().equals(RTPropTag)) {
+ if (old != null)
+ throw new IOException("Duplicate " + RTPropTag);
+ XMLNode typeNode = getNextNode(child, TypeTag);
+ XMLNode ddfName = getNextNode(typeNode, DDFNameTag);
+ context = ddfName.getText();
+ if (context == null)
+ throw new IOException("No text in " + DDFNameTag);
+
+ } else if (child.getTag().equals(NodeTag)) {
+ if (!values.isEmpty())
+ throw new IOException("Scalar node " + node.getText() + " has Node child");
+ children.add(child);
+
+ }
+ }
+
+ if (values.isEmpty()) {
+ if (curValue == null)
+ throw new IOException("Missing name");
+
+ OMANode subNode = parent.addChild(curValue.getName(),
+ context, null, curValue.getPath());
+
+ for (XMLNode child : children) {
+ buildNode(subNode, child);
+ }
+ } else {
+ if (!children.isEmpty())
+ throw new IOException("Got both sub nodes and value(s)");
+
+ for (NodeData nodeData : values) {
+ parent.addChild(nodeData.getName(), context,
+ nodeData.getValue(), nodeData.getPath());
+ }
+ }
+ }
+
+ private static XMLNode getNextNode(XMLNode node, String tag) throws IOException {
+ if (node == null)
+ throw new IOException("No node for " + tag);
+ if (node.getChildren().size() != 1)
+ throw new IOException("Expected " + node.getTag() + " to have exactly one child");
+ XMLNode child = node.getChildren().iterator().next();
+ if (!child.getTag().equals(tag))
+ throw new IOException("Expected " + node.getTag() + " to have child '" + tag +
+ "' instead of '" + child.getTag() + "'");
+ return child;
+ }
+
+ public String getUrn() {
+ return mUrn;
+ }
+
+ public String getDtdRev() {
+ return mDtdRev;
+ }
+
+ public OMAConstructed getRoot() {
+ return mRoot;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("MO Tree v").append(mDtdRev).append(", urn ").append(mUrn).append(")\n");
+ sb.append(mRoot);
+
+ return sb.toString();
+ }
+
+ public void marshal(OutputStream out) throws IOException {
+ out.write("tree ".getBytes(StandardCharsets.UTF_8));
+ OMAConstants.serializeString(mDtdRev, out);
+ out.write(String.format("(%s)\n", mUrn).getBytes(StandardCharsets.UTF_8));
+ mRoot.marshal(out, 0);
+ }
+
+ public static MOTree unmarshal(InputStream in) throws IOException {
+ boolean strip = true;
+ StringBuilder tree = new StringBuilder();
+ for (; ; ) {
+ int octet = in.read();
+ if (octet < 0) {
+ return null;
+ } else if (octet > ' ') {
+ tree.append((char) octet);
+ strip = false;
+ } else if (!strip) {
+ break;
+ }
+ }
+ if (!tree.toString().equals("tree")) {
+ throw new IOException("Not a tree: " + tree);
+ }
+
+ String version = OMAConstants.deserializeString(in);
+ String urn = OMAConstants.readURN(in);
+
+ OMAConstructed root = OMANode.unmarshal(in);
+
+ return new MOTree(urn, version, root);
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/NodeAttribute.java b/service/java/com/android/server/wifi/hotspot2/omadm/NodeAttribute.java
new file mode 100644
index 0000000..1fc1adf
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/omadm/NodeAttribute.java
@@ -0,0 +1,30 @@
+package com.android.server.wifi.hotspot2.omadm;
+
+public class NodeAttribute {
+ private final String mName;
+ private final String mType;
+ private final String mValue;
+
+ public NodeAttribute(String name, String type, String value) {
+ mName = name;
+ mType = type;
+ mValue = value;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getValue() {
+ return mValue;
+ }
+
+ public String getType() {
+ return mType;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s (%s) = '%s'", mName, mType, mValue);
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/OMAConstants.java b/service/java/com/android/server/wifi/hotspot2/omadm/OMAConstants.java
new file mode 100644
index 0000000..1a42341
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/omadm/OMAConstants.java
@@ -0,0 +1,96 @@
+package com.android.server.wifi.hotspot2.omadm;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+public class OMAConstants {
+ private OMAConstants() {
+ }
+
+ public static final String TAG_PostDevData = "spp:sppPostDevData";
+ public static final String TAG_SupportedVersions = "spp:supportedSPPVersions";
+ public static final String TAG_SupportedMOs = "spp:supportedMOList";
+
+ public static final String TAG_MO_Add = "spp:addMO";
+ public static final String TAG_MO_Container = "spp:moContainer";
+
+ public static final String ATTR_URN = "spp:moURN";
+
+ // Following strings excludes the trailing version number (e.g. :1.0)
+ public static final String LOC_PPS = "urn:wfa:mo:hotspot2dot0-perprovidersubscription";
+ public static final String LOC_DEVINFO =
+ "urn:oma:mo:oma-dm-devinfo:1.0 urn:oma:mo:oma-dm-devdetail";
+ public static final String LOC_DEVDETAIL = "urn:wfa:mo-ext:hotspot2dot0-devdetail-ext";
+
+ public static final String SyncMLVersionTag = "VerDTD";
+ public static final String RequiredSyncMLVersion = "1.2";
+
+ private static final Set<String> sMOContainers = new HashSet<String>();
+
+ static {
+ sMOContainers.add(TAG_MO_Add);
+ sMOContainers.add(TAG_MO_Container);
+ }
+
+ public static boolean isMOContainer(String tag) {
+ return sMOContainers.contains(tag);
+ }
+
+ private static final byte[] INDENT = new byte[1024];
+
+ static {
+ Arrays.fill(INDENT, (byte) ' ');
+ }
+
+ public static void serializeString(String s, OutputStream out) throws IOException {
+ byte[] octets = s.getBytes(StandardCharsets.UTF_8);
+ byte[] prefix = String.format("%x:", octets.length).getBytes(StandardCharsets.UTF_8);
+ out.write(prefix);
+ out.write(octets);
+ }
+
+ public static void indent(int level, OutputStream out) throws IOException {
+ out.write(INDENT, 0, level);
+ }
+
+ public static String deserializeString(InputStream in) throws IOException {
+ StringBuilder prefix = new StringBuilder();
+ for (; ; ) {
+ byte b = (byte) in.read();
+ if (b == '.')
+ return null;
+ else if (b == ':')
+ break;
+ else if (b > ' ')
+ prefix.append((char) b);
+ }
+ int length = Integer.parseInt(prefix.toString(), 16);
+ byte[] octets = new byte[length];
+ int offset = 0;
+ while (offset < octets.length) {
+ int amount = in.read(octets, offset, octets.length - offset);
+ if (amount <= 0)
+ throw new EOFException();
+ offset += amount;
+ }
+ return new String(octets, StandardCharsets.UTF_8);
+ }
+
+ public static String readURN(InputStream in) throws IOException {
+ StringBuilder urn = new StringBuilder();
+
+ for (; ; ) {
+ byte b = (byte) in.read();
+ if (b == ')')
+ break;
+ urn.append((char) b);
+ }
+ return urn.toString();
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/OMAConstructed.java b/service/java/com/android/server/wifi/hotspot2/omadm/OMAConstructed.java
new file mode 100644
index 0000000..c0fee8f
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/omadm/OMAConstructed.java
@@ -0,0 +1,118 @@
+package com.android.server.wifi.hotspot2.omadm;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+public class OMAConstructed extends OMANode {
+ private final Map<String, OMANode> mChildren;
+
+ public OMAConstructed(OMANode parent, String name, String context) {
+ super(parent, name, context);
+ mChildren = new HashMap<>();
+ }
+
+ @Override
+ public OMANode addChild(String name, String context, String value, String pathString) throws IOException {
+ if (pathString == null) {
+ OMANode child = value != null ?
+ new OMAScalar(this, name, context, value) :
+ new OMAConstructed(this, name, context);
+ mChildren.put(name.toLowerCase(), child);
+ return child;
+ } else {
+ OMANode target = this;
+ while (target.getParent() != null)
+ target = target.getParent();
+
+ for (String element : pathString.split("/")) {
+ target = target.getChild(element);
+ if (target == null)
+ throw new IOException("No child node '" + element + "' in " + getPathString());
+ else if (target.isLeaf())
+ throw new IOException("Cannot add child to leaf node: " + getPathString());
+ }
+ return target.addChild(name, context, value, null);
+ }
+ }
+
+ public String getScalarValue(Iterator<String> path) throws OMAException {
+ if (!path.hasNext()) {
+ throw new OMAException("Path too short for " + getPathString());
+ }
+ String tag = path.next();
+ OMANode child = mChildren.get(tag.toLowerCase());
+ if (child != null) {
+ return child.getScalarValue(path);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public OMAConstructed getListValue(Iterator<String> path) throws OMAException {
+ if (!path.hasNext()) {
+ return this;
+ }
+ String tag = path.next();
+ OMANode child = mChildren.get(tag.toLowerCase());
+ if (child != null) {
+ return child.getListValue(path);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return false;
+ }
+
+ @Override
+ public Collection<OMANode> getChildren() {
+ return Collections.unmodifiableCollection(mChildren.values());
+ }
+
+ public OMANode getChild(String name) {
+ return mChildren.get(name.toLowerCase());
+ }
+
+ @Override
+ public String getValue() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void toString(StringBuilder sb, int level) {
+ sb.append(getPathString());
+ if (getContext() != null) {
+ sb.append(" (").append(getContext()).append(')');
+ }
+ sb.append('\n');
+
+ for (OMANode node : mChildren.values()) {
+ node.toString(sb, level + 1);
+ }
+ }
+
+ @Override
+ public void marshal(OutputStream out, int level) throws IOException {
+ OMAConstants.indent(level, out);
+ OMAConstants.serializeString(getName(), out);
+ if (getContext() != null) {
+ out.write(String.format("(%s)", getContext()).getBytes(StandardCharsets.UTF_8));
+ }
+ out.write(new byte[] { '+', '\n' });
+
+ for (OMANode child : mChildren.values()) {
+ child.marshal(out, level + 1);
+ }
+ OMAConstants.indent(level, out);
+ out.write(".\n".getBytes(StandardCharsets.UTF_8));
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/OMAException.java b/service/java/com/android/server/wifi/hotspot2/omadm/OMAException.java
new file mode 100644
index 0000000..5f8cd11
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/omadm/OMAException.java
@@ -0,0 +1,9 @@
+package com.android.server.wifi.hotspot2.omadm;
+
+import java.io.IOException;
+
+public class OMAException extends IOException {
+ public OMAException(String message) {
+ super(message);
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/OMANode.java b/service/java/com/android/server/wifi/hotspot2/omadm/OMANode.java
new file mode 100644
index 0000000..5eab73d
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/omadm/OMANode.java
@@ -0,0 +1,124 @@
+package com.android.server.wifi.hotspot2.omadm;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+public abstract class OMANode {
+ private final OMANode mParent;
+ private final String mName;
+ private final String mContext;
+
+ protected OMANode(OMANode parent, String name, String context) {
+ mParent = parent;
+ mName = name;
+ mContext = context;
+ }
+
+ public OMANode getParent() {
+ return mParent;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getContext() {
+ return mContext;
+ }
+
+ public List<String> getPath() {
+ LinkedList<String> path = new LinkedList<>();
+ for (OMANode node = this; node != null; node = node.getParent()) {
+ path.addFirst(node.getName());
+ }
+ return path;
+ }
+
+ public String getPathString() {
+ StringBuilder sb = new StringBuilder();
+ for (String element : getPath()) {
+ sb.append('/').append(element);
+ }
+ return sb.toString();
+ }
+
+ public abstract String getScalarValue(Iterator<String> path) throws OMAException;
+
+ public abstract OMAConstructed getListValue(Iterator<String> path) throws OMAException;
+
+ public abstract boolean isLeaf();
+
+ public abstract Collection<OMANode> getChildren();
+
+ public abstract OMANode getChild(String name);
+
+ public abstract String getValue();
+
+ public abstract OMANode addChild(String name, String context, String value, String path)
+ throws IOException;
+
+ public abstract void marshal(OutputStream out, int level) throws IOException;
+
+ public abstract void toString(StringBuilder sb, int level);
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ toString(sb, 0);
+ return sb.toString();
+ }
+
+ public static OMAConstructed unmarshal(InputStream in) throws IOException {
+ OMANode node = buildNode(in, null);
+ if (node == null || node.isLeaf()) {
+ throw new IOException("Bad OMA tree");
+ }
+ unmarshal(in, (OMAConstructed) node);
+ return (OMAConstructed) node;
+ }
+
+ private static void unmarshal(InputStream in, OMAConstructed parent) throws IOException {
+ for (; ; ) {
+ OMANode node = buildNode(in, parent);
+ if (node == null) {
+ return;
+ }
+ else if (!node.isLeaf()) {
+ unmarshal(in, (OMAConstructed) node);
+ }
+ }
+ }
+
+ private static OMANode buildNode(InputStream in, OMAConstructed parent) throws IOException {
+ String name = OMAConstants.deserializeString(in);
+ if (name == null) {
+ return null;
+ }
+
+ String urn = null;
+ int next = in.read();
+ if (next == '(') {
+ urn = OMAConstants.readURN(in);
+ next = in.read();
+ }
+
+ if (next == '=') {
+ String value = OMAConstants.deserializeString(in);
+ return parent.addChild(name, urn, value, null);
+ } else if (next == '+') {
+ if (parent != null) {
+ return parent.addChild(name, urn, null, null);
+ } else {
+ return new OMAConstructed(null, name, urn);
+ }
+ }
+ else {
+ throw new IOException("Parse error: expected = or + after node name");
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/OMAParser.java b/service/java/com/android/server/wifi/hotspot2/omadm/OMAParser.java
new file mode 100644
index 0000000..99bcbb9
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/omadm/OMAParser.java
@@ -0,0 +1,63 @@
+package com.android.server.wifi.hotspot2.omadm;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import java.io.*;
+
+/**
+ * Parses an OMA-DM XML tree.
+ */
+public class OMAParser extends DefaultHandler {
+ private XMLNode mRoot;
+ private XMLNode mCurrent;
+
+ public MOTree parse(String text, String urn) throws IOException, SAXException {
+ try {
+ SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
+ parser.parse(new InputSource(new StringReader(text)), this);
+ return new MOTree(mRoot, urn);
+ } catch (ParserConfigurationException pce) {
+ throw new SAXException(pce);
+ }
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes)
+ throws SAXException {
+ XMLNode parent = mCurrent;
+
+ mCurrent = new XMLNode(mCurrent, qName, attributes);
+
+ if (mRoot == null)
+ mRoot = mCurrent;
+ else
+ parent.addChild(mCurrent);
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (!qName.equals(mCurrent.getTag()))
+ throw new SAXException("End tag '" + qName + "' doesn't match current node: " +
+ mCurrent);
+
+ try {
+ mCurrent.close();
+ } catch (IOException ioe) {
+ throw new SAXException("Failed to close element", ioe);
+ }
+
+ mCurrent = mCurrent.getParent();
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ mCurrent.addText(ch, start, length);
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/OMAScalar.java b/service/java/com/android/server/wifi/hotspot2/omadm/OMAScalar.java
new file mode 100644
index 0000000..fa93478
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/omadm/OMAScalar.java
@@ -0,0 +1,70 @@
+package com.android.server.wifi.hotspot2.omadm;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.Iterator;
+
+public class OMAScalar extends OMANode {
+ private final String mValue;
+
+ public OMAScalar(OMANode parent, String name, String context, String value) {
+ super(parent, name, context);
+ mValue = value;
+ }
+
+ public String getScalarValue(Iterator<String> path) throws OMAException {
+ return mValue;
+ }
+
+ @Override
+ public OMAConstructed getListValue(Iterator<String> path) throws OMAException {
+ throw new OMAException("Scalar encountered in list path: " + getPathString());
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return true;
+ }
+
+ @Override
+ public Collection<OMANode> getChildren() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getValue() {
+ return mValue;
+ }
+
+ @Override
+ public OMANode getChild(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public OMANode addChild(String name, String context, String value, String path)
+ throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void toString(StringBuilder sb, int level) {
+ sb.append(getPathString()).append('=').append(mValue);
+ if (getContext() != null) {
+ sb.append(" (").append(getContext()).append(')');
+ }
+ sb.append('\n');
+ }
+
+ @Override
+ public void marshal(OutputStream out, int level) throws IOException {
+ OMAConstants.indent(level, out);
+ OMAConstants.serializeString(getName(), out);
+ out.write((byte) '=');
+ OMAConstants.serializeString(getValue(), out);
+ out.write((byte) '\n');
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/RequestDetail.java b/service/java/com/android/server/wifi/hotspot2/omadm/RequestDetail.java
new file mode 100644
index 0000000..11d5c5b
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/omadm/RequestDetail.java
@@ -0,0 +1,60 @@
+package com.android.server.wifi.hotspot2.omadm;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+
+public class RequestDetail {
+ private final String mSppversion;
+ private final String mRedirectURI;
+ private final String mRequestReason;
+ private final String mSessionID;
+ private final String[] mSupportedVersions;
+ private final String[] mSupportedMOs;
+ private final Collection<MOTree> m_MOs;
+
+ public enum RequestFields {
+ SPPVersion,
+ RedirectURI,
+ RequestReason,
+ SessionID,
+ SupportedVersions,
+ SupportedMOs
+ }
+
+ public RequestDetail(Map<RequestFields, String> values, Collection<MOTree> mos) {
+ mSppversion = values.get(RequestFields.SPPVersion);
+ mRedirectURI = values.get(RequestFields.RedirectURI);
+ mRequestReason = values.get(RequestFields.RequestReason);
+ mSessionID = values.get(RequestFields.SessionID);
+ mSupportedVersions = split(values.get(RequestFields.SupportedVersions));
+ mSupportedMOs = split(values.get(RequestFields.SupportedMOs));
+ m_MOs = mos;
+ }
+
+ public Collection<MOTree> getMOs() {
+ return m_MOs;
+ }
+
+ private static String[] split(String list) {
+ return list != null ? list.split("[ \n\r]+") : null;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("SPPVersion").append(" = '").append(mSppversion).append("'\n");
+ sb.append("RedirectURI").append(" = '").append(mRedirectURI).append("'\n");
+ sb.append("RequestReason").append(" = '").append(mRequestReason).append("'\n");
+ sb.append("SessionID").append(" = '").append(mSessionID).append("'\n");
+ sb.append("SupportedVersions").append(" = ").append(Arrays.toString(mSupportedVersions))
+ .append('\n');
+ sb.append("SupportedMOs").append(" = ").append(Arrays.toString(mSupportedMOs)).append('\n');
+ sb.append("MOs:\n");
+ for (MOTree mo : m_MOs)
+ sb.append(mo);
+
+ return sb.toString();
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/SOAPParser.java b/service/java/com/android/server/wifi/hotspot2/omadm/SOAPParser.java
new file mode 100644
index 0000000..f13c851
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/omadm/SOAPParser.java
@@ -0,0 +1,153 @@
+package com.android.server.wifi.hotspot2.omadm;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import java.io.*;
+import java.util.*;
+
+import static com.android.server.wifi.hotspot2.omadm.RequestDetail.RequestFields.*;
+
+/**
+ * This is an incomplete SOAP-XML parser for OSU data needing enhancements for r2.
+ */
+
+public class SOAPParser extends DefaultHandler {
+ private XMLNode mRoot;
+ private XMLNode mCurrent;
+
+ private static String[] TagOnly = new String[0];
+ private static final Map<RequestDetail.RequestFields, String> sSoapMappings =
+ new EnumMap<RequestDetail.RequestFields, String>(RequestDetail.RequestFields.class);
+ private static final Map<String, RequestDetail.RequestFields> sRevMappings =
+ new HashMap<String, RequestDetail.RequestFields>();
+ private static final Map<String, String[]> sSoapAttributes =
+ new HashMap<String, String[]>();
+
+ static {
+ sSoapMappings.put(SPPVersion, "spp:sppVersion");
+ sSoapMappings.put(RedirectURI, "redirectURI");
+ sSoapMappings.put(RequestReason, "requestReason");
+ sSoapMappings.put(SessionID, "spp:sessionID");
+ sSoapMappings.put(SupportedVersions, "spp:supportedSPPVersions");
+ sSoapMappings.put(SupportedMOs, "spp:supportedMOList");
+
+ for (Map.Entry<RequestDetail.RequestFields, String> entry : sSoapMappings.entrySet()) {
+ sRevMappings.put(entry.getValue(), entry.getKey());
+ }
+
+ // !!! Really: The first element inside the body
+ sSoapAttributes.put("spp:sppPostDevDataResponse", new String[]{
+ sSoapMappings.get(SPPVersion),
+ sSoapMappings.get(RedirectURI),
+ sSoapMappings.get(RequestReason),
+ sSoapMappings.get(SessionID)});
+
+ sSoapAttributes.put(sSoapMappings.get(SupportedVersions), TagOnly);
+ sSoapAttributes.put(sSoapMappings.get(SupportedMOs), TagOnly);
+ }
+
+ public XMLNode parse(File file) throws IOException, ParserConfigurationException, SAXException {
+ SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
+
+ BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
+ try {
+ parser.parse(in, this);
+ } finally {
+ in.close();
+ }
+ return mRoot;
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes)
+ throws SAXException {
+ XMLNode parent = mCurrent;
+
+ mCurrent = new XMLNode(mCurrent, qName, attributes);
+ System.out.println("Added " + mCurrent.getTag() + ", atts " + mCurrent.getAttributes());
+
+ if (mRoot == null)
+ mRoot = mCurrent;
+ else
+ parent.addChild(mCurrent);
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (!qName.equals(mCurrent.getTag()))
+ throw new SAXException("End tag '" + qName + "' doesn't match current node: " +
+ mCurrent);
+
+ try {
+ mCurrent.close();
+ } catch (IOException ioe) {
+ throw new SAXException("Failed to close element", ioe);
+ }
+
+ mCurrent = mCurrent.getParent();
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ mCurrent.addText(ch, start, length);
+ }
+
+ public RequestDetail getRequestDetail() {
+ Map<RequestDetail.RequestFields, String> values =
+ new EnumMap<RequestDetail.RequestFields, String>(RequestDetail.RequestFields.class);
+ List<MOTree> mos = new ArrayList<MOTree>();
+ extractFields(mRoot, values, mos);
+ return new RequestDetail(values, mos);
+ }
+
+ private static void extractFields(XMLNode node, Map<RequestDetail.RequestFields,
+ String> values, Collection<MOTree> mos) {
+ String[] attributes = sSoapAttributes.get(node.getTag());
+
+ if (attributes != null) {
+ if (attributes.length == 0) {
+ RequestDetail.RequestFields field = sRevMappings.get(node.getTag());
+ values.put(field, node.getText());
+ } else {
+ for (String attribute : attributes) {
+ RequestDetail.RequestFields field = sRevMappings.get(attribute);
+ if (field != null) {
+ String value = node.getAttributeValue(attribute);
+
+ if (value != null)
+ values.put(field, value);
+ }
+ }
+ }
+ }
+
+ if (node.getMOTree() != null)
+ mos.add(node.getMOTree());
+
+ for (XMLNode child : node.getChildren()) {
+ extractFields(child, values, mos);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ SOAPParser soapParser = new SOAPParser();
+ XMLNode root = soapParser.parse(new File(args[0]));
+ //System.out.println( root );
+ System.out.println(soapParser.getRequestDetail());
+ System.out.println("Marshalled: ");
+ for (MOTree mo : soapParser.getRequestDetail().getMOs()) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ mo.marshal(out);
+ System.out.println(out.toString());
+ MOTree back = MOTree.unmarshal(new ByteArrayInputStream(out.toByteArray()));
+ System.out.println(back);
+ }
+ System.out.println("---");
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/XMLNode.java b/service/java/com/android/server/wifi/hotspot2/omadm/XMLNode.java
new file mode 100644
index 0000000..d400e2f
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/omadm/XMLNode.java
@@ -0,0 +1,128 @@
+package com.android.server.wifi.hotspot2.omadm;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class XMLNode {
+ private final String mTag;
+ private final Map<String, NodeAttribute> mAttributes;
+ private final List<XMLNode> mChildren;
+ private final XMLNode mParent;
+ private MOTree mMO;
+ private StringBuilder mTextBuilder;
+ private String mText;
+
+ public XMLNode(XMLNode parent, String tag, Attributes attributes) throws SAXException {
+ mTag = tag;
+
+ mAttributes = new HashMap<String, NodeAttribute>();
+
+ if (attributes.getLength() > 0) {
+ for (int n = 0; n < attributes.getLength(); n++)
+ mAttributes.put(attributes.getQName(n), new NodeAttribute(attributes.getQName(n),
+ attributes.getType(n), attributes.getValue(n)));
+ }
+
+ mParent = parent;
+ mChildren = new ArrayList<XMLNode>();
+
+ mTextBuilder = new StringBuilder();
+ }
+
+ public void addText(char[] chs, int start, int length) {
+ String s = new String(chs, start, length);
+ String trimmed = s.trim();
+ if (trimmed.isEmpty())
+ return;
+
+ if (s.charAt(0) != trimmed.charAt(0))
+ mTextBuilder.append(' ');
+ mTextBuilder.append(trimmed);
+ if (s.charAt(s.length() - 1) != trimmed.charAt(trimmed.length() - 1))
+ mTextBuilder.append(' ');
+ }
+
+ public void addChild(XMLNode child) {
+ mChildren.add(child);
+ }
+
+ public void close() throws IOException, SAXException {
+ String text = mTextBuilder.toString().trim();
+ StringBuilder filtered = new StringBuilder(text.length());
+ for (int n = 0; n < text.length(); n++) {
+ char ch = text.charAt(n);
+ if (ch >= ' ')
+ filtered.append(ch);
+ }
+
+ mText = filtered.toString();
+ mTextBuilder = null;
+
+ if (OMAConstants.isMOContainer(mTag)) {
+ NodeAttribute urn = mAttributes.get(OMAConstants.ATTR_URN);
+ OMAParser omaParser = new OMAParser();
+ mMO = omaParser.parse(mText, urn.getValue());
+ }
+ }
+
+ public String getTag() {
+ return mTag;
+ }
+
+ public XMLNode getParent() {
+ return mParent;
+ }
+
+ public String getText() {
+ return mText;
+ }
+
+ public Map<String, NodeAttribute> getAttributes() {
+ return Collections.unmodifiableMap(mAttributes);
+ }
+
+ public String getAttributeValue(String name) {
+ NodeAttribute nodeAttribute = mAttributes.get(name);
+ return nodeAttribute != null ? nodeAttribute.getValue() : null;
+ }
+
+ public List<XMLNode> getChildren() {
+ return mChildren;
+ }
+
+ public MOTree getMOTree() {
+ return mMO;
+ }
+
+ private void toString(char[] indent, StringBuilder sb) {
+ Arrays.fill(indent, ' ');
+
+ sb.append(indent).append('<').append(mTag).append("> ").append(mAttributes.values());
+
+ if (mMO != null)
+ sb.append('\n').append(mMO);
+ else if (!mText.isEmpty())
+ sb.append(", text: ").append(mText);
+
+ sb.append('\n');
+
+ char[] subIndent = Arrays.copyOf(indent, indent.length + 2);
+ for (XMLNode child : mChildren)
+ child.toString(subIndent, sb);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ toString(new char[0], sb);
+ return sb.toString();
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/pps/Credential.java b/service/java/com/android/server/wifi/hotspot2/pps/Credential.java
new file mode 100644
index 0000000..4a06021
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/pps/Credential.java
@@ -0,0 +1,357 @@
+package com.android.server.wifi.hotspot2.pps;
+
+import android.net.wifi.WifiEnterpriseConfig;
+import android.security.Credentials;
+import android.security.KeyStore;
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+
+import com.android.server.wifi.IMSIParameter;
+import com.android.server.wifi.anqp.eap.EAP;
+import com.android.server.wifi.anqp.eap.EAPMethod;
+import com.android.server.wifi.anqp.eap.NonEAPInnerAuth;
+import com.android.server.wifi.hotspot2.Utils;
+import com.android.server.wifi.hotspot2.omadm.OMAException;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.util.Arrays;
+
+public class Credential {
+ public enum CertType {IEEE, x509v3}
+
+ public static final String CertTypeX509 = "x509v3";
+ public static final String CertTypeIEEE = "802.1ar";
+
+ private final long mCtime;
+ private final long mExpTime;
+ private final String mRealm;
+ private final boolean mCheckAAACert;
+
+ private final String mUserName;
+ private final String mPassword;
+ private final boolean mDisregardPassword;
+ private final boolean mMachineManaged;
+ private final String mSTokenApp;
+ private final boolean mShare;
+ private final EAPMethod mEAPMethod;
+
+ private final CertType mCertType;
+ private final byte[] mFingerPrint;
+
+ private final IMSIParameter mImsi;
+
+ public Credential(long ctime, long expTime, String realm, boolean checkAAACert,
+ EAPMethod eapMethod, String userName, String password,
+ boolean machineManaged, String stApp, boolean share) {
+ mCtime = ctime;
+ mExpTime = expTime;
+ mRealm = realm;
+ mCheckAAACert = checkAAACert;
+ mEAPMethod = eapMethod;
+ mUserName = userName;
+
+ if (!TextUtils.isEmpty(password)) {
+ byte[] pwOctets = Base64.decode(password, Base64.DEFAULT);
+ mPassword = new String(pwOctets, StandardCharsets.UTF_8);
+ } else {
+ mPassword = null;
+ }
+ mDisregardPassword = false;
+
+ mMachineManaged = machineManaged;
+ mSTokenApp = stApp;
+ mShare = share;
+
+ mCertType = null;
+ mFingerPrint = null;
+
+ mImsi = null;
+ }
+
+ public Credential(long ctime, long expTime, String realm, boolean checkAAACert,
+ EAPMethod eapMethod, Credential.CertType certType, byte[] fingerPrint) {
+ mCtime = ctime;
+ mExpTime = expTime;
+ mRealm = realm;
+ mCheckAAACert = checkAAACert;
+ mEAPMethod = eapMethod;
+ mCertType = certType;
+ mFingerPrint = fingerPrint;
+
+ mUserName = null;
+ mPassword = null;
+ mDisregardPassword = false;
+ mMachineManaged = false;
+ mSTokenApp = null;
+ mShare = false;
+
+ mImsi = null;
+ }
+
+ public Credential(long ctime, long expTime, String realm, boolean checkAAACert,
+ EAPMethod eapMethod, IMSIParameter imsi) {
+ mCtime = ctime;
+ mExpTime = expTime;
+ mRealm = realm;
+ mCheckAAACert = checkAAACert;
+ mEAPMethod = eapMethod;
+ mImsi = imsi;
+
+ mCertType = null;
+ mFingerPrint = null;
+
+ mUserName = null;
+ mPassword = null;
+ mDisregardPassword = false;
+ mMachineManaged = false;
+ mSTokenApp = null;
+ mShare = false;
+ }
+
+ public Credential(Credential other, String password) {
+ mCtime = other.mCtime;
+ mExpTime = other.mExpTime;
+ mRealm = other.mRealm;
+ mCheckAAACert = other.mCheckAAACert;
+ mUserName = other.mUserName;
+ mPassword = password;
+ mDisregardPassword = other.mDisregardPassword;
+ mMachineManaged = other.mMachineManaged;
+ mSTokenApp = other.mSTokenApp;
+ mShare = other.mShare;
+ mEAPMethod = other.mEAPMethod;
+ mCertType = other.mCertType;
+ mFingerPrint = other.mFingerPrint;
+ mImsi = other.mImsi;
+ }
+
+ public Credential(WifiEnterpriseConfig enterpriseConfig, KeyStore keyStore, boolean update)
+ throws IOException {
+ mCtime = Utils.UNSET_TIME;
+ mExpTime = Utils.UNSET_TIME;
+ mRealm = enterpriseConfig.getRealm();
+ mCheckAAACert = false;
+ mEAPMethod = mapEapMethod(enterpriseConfig.getEapMethod(),
+ enterpriseConfig.getPhase2Method());
+ mCertType = mEAPMethod.getEAPMethodID() == EAP.EAPMethodID.EAP_TLS ? CertType.x509v3 : null;
+ byte[] fingerPrint;
+
+ if (enterpriseConfig.getClientCertificate() != null) {
+ // !!! Not sure this will be true in any practical instances:
+ try {
+ MessageDigest digester = MessageDigest.getInstance("SHA-256");
+ fingerPrint = digester.digest(enterpriseConfig.getClientCertificate().getEncoded());
+ } catch (GeneralSecurityException gse) {
+ Log.e(Utils.hs2LogTag(getClass()),
+ "Failed to generate certificate fingerprint: " + gse);
+ fingerPrint = null;
+ }
+ } else if (enterpriseConfig.getClientCertificateAlias() != null) {
+ String alias = enterpriseConfig.getClientCertificateAlias();
+ byte[] octets = keyStore.get(Credentials.USER_CERTIFICATE + alias);
+ if (octets != null) {
+ try {
+ MessageDigest digester = MessageDigest.getInstance("SHA-256");
+ fingerPrint = digester.digest(octets);
+ } catch (GeneralSecurityException gse) {
+ Log.e(Utils.hs2LogTag(getClass()), "Failed to construct digest: " + gse);
+ fingerPrint = null;
+ }
+ } else // !!! The current alias is *not* derived from the fingerprint...
+ {
+ try {
+ fingerPrint = Base64.decode(enterpriseConfig.getClientCertificateAlias(),
+ Base64.DEFAULT);
+ } catch (IllegalArgumentException ie) {
+ Log.e(Utils.hs2LogTag(getClass()), "Bad base 64 alias");
+ fingerPrint = null;
+ }
+ }
+ } else {
+ fingerPrint = null;
+ }
+ mFingerPrint = fingerPrint;
+ String imsi = enterpriseConfig.getPlmn();
+ mImsi = imsi == null || imsi.length() == 0 ? null : new IMSIParameter(imsi);
+ mUserName = enterpriseConfig.getIdentity();
+ mPassword = enterpriseConfig.getPassword();
+ mDisregardPassword = update && mPassword.length() < 2;
+ mMachineManaged = false;
+ mSTokenApp = null;
+ mShare = false;
+ }
+
+ public static CertType mapCertType(String certType) throws OMAException {
+ if (certType.equalsIgnoreCase(CertTypeX509)) {
+ return CertType.x509v3;
+ } else if (certType.equalsIgnoreCase(CertTypeIEEE)) {
+ return CertType.IEEE;
+ } else {
+ throw new OMAException("Invalid cert type: '" + certType + "'");
+ }
+ }
+
+ private static EAPMethod mapEapMethod(int eapMethod, int phase2Method) throws IOException {
+ switch (eapMethod) {
+ case WifiEnterpriseConfig.Eap.TLS:
+ return new EAPMethod(EAP.EAPMethodID.EAP_TLS, null);
+ case WifiEnterpriseConfig.Eap.TTLS:
+ /* keep this table in sync with WifiEnterpriseConfig.Phase2 enum */
+ NonEAPInnerAuth inner;
+ switch (phase2Method) {
+ case WifiEnterpriseConfig.Phase2.PAP:
+ inner = new NonEAPInnerAuth(NonEAPInnerAuth.NonEAPType.PAP);
+ break;
+ case WifiEnterpriseConfig.Phase2.MSCHAP:
+ inner = new NonEAPInnerAuth(NonEAPInnerAuth.NonEAPType.MSCHAP);
+ break;
+ case WifiEnterpriseConfig.Phase2.MSCHAPV2:
+ inner = new NonEAPInnerAuth(NonEAPInnerAuth.NonEAPType.MSCHAPv2);
+ break;
+ default:
+ throw new IOException("TTLS phase2 method " +
+ phase2Method + " not valid for Passpoint");
+ }
+ return new EAPMethod(EAP.EAPMethodID.EAP_TTLS, inner);
+ case WifiEnterpriseConfig.Eap.SIM:
+ return new EAPMethod(EAP.EAPMethodID.EAP_SIM, null);
+ case WifiEnterpriseConfig.Eap.AKA:
+ return new EAPMethod(EAP.EAPMethodID.EAP_AKA, null);
+ case WifiEnterpriseConfig.Eap.AKA_PRIME:
+ return new EAPMethod(EAP.EAPMethodID.EAP_AKAPrim, null);
+ default:
+ String methodName;
+ if (eapMethod >= 0 && eapMethod < WifiEnterpriseConfig.Eap.strings.length) {
+ methodName = WifiEnterpriseConfig.Eap.strings[eapMethod];
+ } else {
+ methodName = Integer.toString(eapMethod);
+ }
+ throw new IOException("EAP method id " + methodName + " is not valid for Passpoint");
+ }
+ }
+
+ public EAPMethod getEAPMethod() {
+ return mEAPMethod;
+ }
+
+ public String getRealm() {
+ return mRealm;
+ }
+
+ public IMSIParameter getImsi() {
+ return mImsi;
+ }
+
+ public String getUserName() {
+ return mUserName;
+ }
+
+ public String getPassword() {
+ return mPassword;
+ }
+
+ public boolean hasDisregardPassword() {
+ return mDisregardPassword;
+ }
+
+ public CertType getCertType() {
+ return mCertType;
+ }
+
+ public byte[] getFingerPrint() {
+ return mFingerPrint;
+ }
+
+ public long getCtime() {
+ return mCtime;
+ }
+
+ public long getExpTime() {
+ return mExpTime;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Credential that = (Credential) o;
+
+ if (mCheckAAACert != that.mCheckAAACert) return false;
+ if (mCtime != that.mCtime) return false;
+ if (mExpTime != that.mExpTime) return false;
+ if (mMachineManaged != that.mMachineManaged) return false;
+ if (mShare != that.mShare) return false;
+ if (mCertType != that.mCertType) return false;
+ if (!mEAPMethod.equals(that.mEAPMethod)) return false;
+ if (!Arrays.equals(mFingerPrint, that.mFingerPrint)) return false;
+ if (!safeEquals(mImsi, that.mImsi)) {
+ return false;
+ }
+
+ if (!mDisregardPassword && !safeEquals(mPassword, that.mPassword)) {
+ return false;
+ }
+
+ if (!mRealm.equals(that.mRealm)) return false;
+ if (!safeEquals(mSTokenApp, that.mSTokenApp)) {
+ return false;
+ }
+ if (!safeEquals(mUserName, that.mUserName)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private static boolean safeEquals(Object s1, Object s2) {
+ if (s1 == null) {
+ return s2 == null;
+ }
+ else {
+ return s2 != null && s1.equals(s2);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) (mCtime ^ (mCtime >>> 32));
+ result = 31 * result + (int) (mExpTime ^ (mExpTime >>> 32));
+ result = 31 * result + mRealm.hashCode();
+ result = 31 * result + (mCheckAAACert ? 1 : 0);
+ result = 31 * result + (mUserName != null ? mUserName.hashCode() : 0);
+ result = 31 * result + (mPassword != null ? mPassword.hashCode() : 0);
+ result = 31 * result + (mMachineManaged ? 1 : 0);
+ result = 31 * result + (mSTokenApp != null ? mSTokenApp.hashCode() : 0);
+ result = 31 * result + (mShare ? 1 : 0);
+ result = 31 * result + mEAPMethod.hashCode();
+ result = 31 * result + (mCertType != null ? mCertType.hashCode() : 0);
+ result = 31 * result + (mFingerPrint != null ? Arrays.hashCode(mFingerPrint) : 0);
+ result = 31 * result + (mImsi != null ? mImsi.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Credential{" +
+ "mCtime=" + Utils.toUTCString(mCtime) +
+ ", mExpTime=" + Utils.toUTCString(mExpTime) +
+ ", mRealm='" + mRealm + '\'' +
+ ", mCheckAAACert=" + mCheckAAACert +
+ ", mUserName='" + mUserName + '\'' +
+ ", mPassword='" + mPassword + '\'' +
+ ", mDisregardPassword=" + mDisregardPassword +
+ ", mMachineManaged=" + mMachineManaged +
+ ", mSTokenApp='" + mSTokenApp + '\'' +
+ ", mShare=" + mShare +
+ ", mEAPMethod=" + mEAPMethod +
+ ", mCertType=" + mCertType +
+ ", mFingerPrint=" + Utils.toHexString(mFingerPrint) +
+ ", mImsi='" + mImsi + '\'' +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/pps/DomainMatcher.java b/service/java/com/android/server/wifi/hotspot2/pps/DomainMatcher.java
new file mode 100644
index 0000000..3f6e22a
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/pps/DomainMatcher.java
@@ -0,0 +1,151 @@
+package com.android.server.wifi.hotspot2.pps;
+
+import android.util.Log;
+
+import com.android.server.wifi.hotspot2.Utils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+public class DomainMatcher {
+
+ public enum Match {None, Primary, Secondary}
+
+ private final Label mRoot;
+
+ private static class Label {
+ private final Map<String, Label> mSubDomains;
+ private final Match mMatch;
+
+ private Label(Match match) {
+ mMatch = match;
+ mSubDomains = match == Match.None ? new HashMap<String, Label>() : null;
+ }
+
+ private void addDomain(Iterator<String> labels, Match match) {
+ String labelName = labels.next();
+ if (labels.hasNext()) {
+ Label subLabel = new Label(Match.None);
+ mSubDomains.put(labelName, subLabel);
+ subLabel.addDomain(labels, match);
+ } else {
+ mSubDomains.put(labelName, new Label(match));
+ }
+ }
+
+ private Label getSubLabel(String labelString) {
+ return mSubDomains.get(labelString);
+ }
+
+ public Match getMatch() {
+ return mMatch;
+ }
+
+ private void toString(StringBuilder sb) {
+ if (mSubDomains != null) {
+ sb.append(".{");
+ for (Map.Entry<String, Label> entry : mSubDomains.entrySet()) {
+ sb.append(entry.getKey());
+ entry.getValue().toString(sb);
+ }
+ sb.append('}');
+ } else {
+ sb.append('=').append(mMatch);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ toString(sb);
+ return sb.toString();
+ }
+ }
+
+ public DomainMatcher(List<String> primary, List<List<String>> secondary) {
+ mRoot = new Label(Match.None);
+ for (List<String> secondaryLabel : secondary) {
+ mRoot.addDomain(secondaryLabel.iterator(), Match.Secondary);
+ }
+ // Primary overwrites secondary.
+ mRoot.addDomain(primary.iterator(), Match.Primary);
+ }
+
+ /**
+ * Check if domain is either a the same or a sub-domain of any of the domains in the domain tree
+ * in this matcher, i.e. all or or a sub-set of the labels in domain matches a path in the tree.
+ * @param domain Domain to be checked.
+ * @return None if domain is not a sub-domain, Primary if it matched one of the primary domains
+ * or Secondary if it matched on of the secondary domains.
+ */
+ public Match isSubDomain(List<String> domain) {
+
+ Label label = mRoot;
+ for (String labelString : domain) {
+ label = label.getSubLabel(labelString);
+ if (label == null) {
+ return Match.None;
+ } else if (label.getMatch() != Match.None) {
+ return label.getMatch();
+ }
+ }
+ return Match.None; // Domain is a super domain
+ }
+
+ public static boolean arg2SubdomainOfArg1(List<String> arg1, List<String> arg2) {
+ if (arg2.size() < arg1.size()) {
+ return false;
+ }
+
+ Iterator<String> l1 = arg1.iterator();
+ Iterator<String> l2 = arg2.iterator();
+
+ while(l1.hasNext()) {
+ if (!l1.next().equals(l2.next())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "Domain matcher " + mRoot;
+ }
+
+ private static final String[] TestDomains = {
+ "garbage.apple.com",
+ "apple.com",
+ "com",
+ "jan.android.google.com.",
+ "jan.android.google.com",
+ "android.google.com",
+ "google.com",
+ "jan.android.google.net.",
+ "jan.android.google.net",
+ "android.google.net",
+ "google.net",
+ "net.",
+ "."
+ };
+
+ public static void main(String[] args) {
+ DomainMatcher dm1 = new DomainMatcher(Utils.splitDomain("android.google.com"),
+ Collections.<List<String>>emptyList());
+ for (String domain : TestDomains) {
+ System.out.println(domain + ": " + dm1.isSubDomain(Utils.splitDomain(domain)));
+ }
+ List<List<String>> secondaries = new ArrayList<List<String>>();
+ secondaries.add(Utils.splitDomain("apple.com"));
+ secondaries.add(Utils.splitDomain("net"));
+ DomainMatcher dm2 = new DomainMatcher(Utils.splitDomain("android.google.com"), secondaries);
+ for (String domain : TestDomains) {
+ System.out.println(domain + ": " + dm2.isSubDomain(Utils.splitDomain(domain)));
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/pps/HomeSP.java b/service/java/com/android/server/wifi/hotspot2/pps/HomeSP.java
new file mode 100644
index 0000000..c8a3da2
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/pps/HomeSP.java
@@ -0,0 +1,313 @@
+package com.android.server.wifi.hotspot2.pps;
+
+import android.util.Log;
+
+import com.android.server.wifi.SIMAccessor;
+import com.android.server.wifi.anqp.ANQPElement;
+import com.android.server.wifi.anqp.CellularNetwork;
+import com.android.server.wifi.anqp.DomainNameElement;
+import com.android.server.wifi.anqp.NAIRealmElement;
+import com.android.server.wifi.anqp.RoamingConsortiumElement;
+import com.android.server.wifi.anqp.ThreeGPPNetworkElement;
+import com.android.server.wifi.hotspot2.AuthMatch;
+import com.android.server.wifi.hotspot2.NetworkDetail;
+import com.android.server.wifi.hotspot2.PasspointMatch;
+import com.android.server.wifi.hotspot2.Utils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static com.android.server.wifi.anqp.Constants.ANQPElementType;
+
+public class HomeSP {
+ private final Map<String, Long> mSSIDs; // SSID, HESSID, [0,N]
+ private final String mFQDN;
+ private final DomainMatcher mDomainMatcher;
+ private final Set<String> mOtherHomePartners;
+ private final HashSet<Long> mRoamingConsortiums; // [0,N]
+ private final Set<Long> mMatchAnyOIs; // [0,N]
+ private final List<Long> mMatchAllOIs; // [0,N]
+
+ private final Credential mCredential;
+
+ // Informational:
+ private final String mFriendlyName; // [1]
+ private final String mIconURL; // [0,1]
+
+ public HomeSP(Map<String, Long> ssidMap,
+ /*@NotNull*/ String fqdn,
+ /*@NotNull*/ HashSet<Long> roamingConsortiums,
+ /*@NotNull*/ Set<String> otherHomePartners,
+ /*@NotNull*/ Set<Long> matchAnyOIs,
+ /*@NotNull*/ List<Long> matchAllOIs,
+ String friendlyName,
+ String iconURL,
+ Credential credential) {
+
+ mSSIDs = ssidMap;
+ List<List<String>> otherPartners = new ArrayList<>(otherHomePartners.size());
+ for (String otherPartner : otherHomePartners) {
+ otherPartners.add(Utils.splitDomain(otherPartner));
+ }
+ mOtherHomePartners = otherHomePartners;
+ mFQDN = fqdn;
+ mDomainMatcher = new DomainMatcher(Utils.splitDomain(fqdn), otherPartners);
+ mRoamingConsortiums = roamingConsortiums;
+ mMatchAnyOIs = matchAnyOIs;
+ mMatchAllOIs = matchAllOIs;
+ mFriendlyName = friendlyName;
+ mIconURL = iconURL;
+ mCredential = credential;
+ }
+
+ public HomeSP getClone(String password) {
+ if (getCredential().hasDisregardPassword()) {
+ return new HomeSP(mSSIDs,
+ mFQDN,
+ mRoamingConsortiums,
+ mOtherHomePartners,
+ mMatchAnyOIs,
+ mMatchAllOIs,
+ mFriendlyName,
+ mIconURL,
+ new Credential(mCredential, password));
+ }
+ else {
+ return this;
+ }
+ }
+
+ public PasspointMatch match(NetworkDetail networkDetail,
+ Map<ANQPElementType, ANQPElement> anqpElementMap,
+ SIMAccessor simAccessor) {
+
+ List<String> imsis = simAccessor.getMatchingImsis(mCredential.getImsi());
+
+ PasspointMatch spMatch = matchSP(networkDetail, anqpElementMap, imsis);
+
+ if (spMatch == PasspointMatch.Incomplete || spMatch == PasspointMatch.Declined) {
+ return spMatch;
+ }
+
+ if (imsiMatch(imsis, (ThreeGPPNetworkElement)
+ anqpElementMap.get(ANQPElementType.ANQP3GPPNetwork)) != null) {
+ // PLMN match, promote sp match to roaming if necessary.
+ return spMatch == PasspointMatch.None ? PasspointMatch.RoamingProvider : spMatch;
+ }
+
+ NAIRealmElement naiRealmElement =
+ (NAIRealmElement) anqpElementMap.get(ANQPElementType.ANQPNAIRealm);
+
+ int authMatch = naiRealmElement != null ?
+ naiRealmElement.match(mCredential) :
+ AuthMatch.Indeterminate;
+
+ Log.d(Utils.hs2LogTag(getClass()), networkDetail.toKeyString() + " match on " + mFQDN +
+ ": " + spMatch + ", auth " + AuthMatch.toString(authMatch));
+
+ if (authMatch == AuthMatch.None) {
+ // Distinct auth mismatch, demote authentication.
+ return PasspointMatch.None;
+ }
+ else if ((authMatch & AuthMatch.Realm) == 0) {
+ // No realm match, return sp match as is.
+ return spMatch;
+ }
+ else {
+ // Realm match, promote sp match to roaming if necessary.
+ return spMatch == PasspointMatch.None ? PasspointMatch.RoamingProvider : spMatch;
+ }
+ }
+
+ public PasspointMatch matchSP(NetworkDetail networkDetail,
+ Map<ANQPElementType, ANQPElement> anqpElementMap,
+ List<String> imsis) {
+
+ if (mSSIDs.containsKey(networkDetail.getSSID())) {
+ Long hessid = mSSIDs.get(networkDetail.getSSID());
+ if (hessid == null || networkDetail.getHESSID() == hessid) {
+ Log.d(Utils.hs2LogTag(getClass()), "match SSID");
+ return PasspointMatch.HomeProvider;
+ }
+ }
+
+ Set<Long> anOIs = new HashSet<>();
+
+ if (networkDetail.getRoamingConsortiums() != null) {
+ for (long oi : networkDetail.getRoamingConsortiums()) {
+ anOIs.add(oi);
+ }
+ }
+ RoamingConsortiumElement rcElement = anqpElementMap != null ?
+ (RoamingConsortiumElement) anqpElementMap.get(ANQPElementType.ANQPRoamingConsortium)
+ : null;
+ if (rcElement != null) {
+ anOIs.addAll(rcElement.getOIs());
+ }
+
+ // It may seem reasonable to check for home provider match prior to checking for roaming
+ // relationship, but it is possible to avoid an ANQP query if it turns out that the
+ // "match all" rule fails based only on beacon info only.
+ boolean roamingMatch = false;
+
+ if (!mMatchAllOIs.isEmpty()) {
+ boolean matchesAll = true;
+
+ for (long spOI : mMatchAllOIs) {
+ if (!anOIs.contains(spOI)) {
+ matchesAll = false;
+ break;
+ }
+ }
+ if (matchesAll) {
+ roamingMatch = true;
+ }
+ else {
+ if (anqpElementMap != null || networkDetail.getAnqpOICount() == 0) {
+ return PasspointMatch.Declined;
+ }
+ else {
+ return PasspointMatch.Incomplete;
+ }
+ }
+ }
+
+ if (!roamingMatch &&
+ (!Collections.disjoint(mMatchAnyOIs, anOIs) ||
+ !Collections.disjoint(mRoamingConsortiums, anOIs))) {
+ roamingMatch = true;
+ }
+
+ if (anqpElementMap == null) {
+ return PasspointMatch.Incomplete;
+ }
+
+ DomainNameElement domainNameElement =
+ (DomainNameElement) anqpElementMap.get(ANQPElementType.ANQPDomName);
+
+ if (domainNameElement != null) {
+ for (String domain : domainNameElement.getDomains()) {
+ List<String> anLabels = Utils.splitDomain(domain);
+ DomainMatcher.Match match = mDomainMatcher.isSubDomain(anLabels);
+ if (match != DomainMatcher.Match.None) {
+ return PasspointMatch.HomeProvider;
+ }
+
+ if (imsiMatch(imsis, anLabels) != null) {
+ return PasspointMatch.HomeProvider;
+ }
+ }
+ }
+
+ return roamingMatch ? PasspointMatch.RoamingProvider : PasspointMatch.None;
+ }
+
+ private String imsiMatch(List<String> imsis, ThreeGPPNetworkElement plmnElement) {
+ if (imsis == null || plmnElement == null || plmnElement.getPlmns().isEmpty()) {
+ return null;
+ }
+ for (CellularNetwork network : plmnElement.getPlmns()) {
+ for (String mccMnc : network) {
+ String imsi = imsiMatch(imsis, mccMnc);
+ if (imsi != null) {
+ return imsi;
+ }
+ }
+ }
+ return null;
+ }
+
+ private String imsiMatch(List<String> imsis, List<String> fqdn) {
+ if (imsis == null) {
+ return null;
+ }
+ String mccMnc = Utils.getMccMnc(fqdn);
+ return mccMnc != null ? imsiMatch(imsis, mccMnc) : null;
+ }
+
+ private String imsiMatch(List<String> imsis, String mccMnc) {
+ if (mCredential.getImsi().matchesMccMnc(mccMnc)) {
+ for (String imsi : imsis) {
+ if (imsi.startsWith(mccMnc)) {
+ return imsi;
+ }
+ }
+ }
+ return null;
+ }
+
+ public String getFQDN() { return mFQDN; }
+ public String getFriendlyName() { return mFriendlyName; }
+ public HashSet<Long> getRoamingConsortiums() { return mRoamingConsortiums; }
+ public Credential getCredential() { return mCredential; }
+
+ public Map<String, Long> getSSIDs() {
+ return mSSIDs;
+ }
+
+ public Collection<String> getOtherHomePartners() {
+ return mOtherHomePartners;
+ }
+
+ public Set<Long> getMatchAnyOIs() {
+ return mMatchAnyOIs;
+ }
+
+ public List<Long> getMatchAllOIs() {
+ return mMatchAllOIs;
+ }
+
+ public String getIconURL() {
+ return mIconURL;
+ }
+
+ public boolean deepEquals(HomeSP other) {
+ return mFQDN.equals(other.mFQDN) &&
+ mSSIDs.equals(other.mSSIDs) &&
+ mOtherHomePartners.equals(other.mOtherHomePartners) &&
+ mRoamingConsortiums.equals(other.mRoamingConsortiums) &&
+ mMatchAnyOIs.equals(other.mMatchAnyOIs) &&
+ mMatchAllOIs.equals(other.mMatchAllOIs) &&
+ mFriendlyName.equals(other.mFriendlyName) &&
+ Utils.compare(mIconURL, other.mIconURL) == 0 &&
+ mCredential.equals(other.mCredential);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ } else if (thatObject == null || getClass() != thatObject.getClass()) {
+ return false;
+ }
+
+ HomeSP that = (HomeSP) thatObject;
+ return mFQDN.equals(that.mFQDN);
+ }
+
+ @Override
+ public int hashCode() {
+ return mFQDN.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "HomeSP{" +
+ "mSSIDs=" + mSSIDs +
+ ", mFQDN='" + mFQDN + '\'' +
+ ", mDomainMatcher=" + mDomainMatcher +
+ ", mRoamingConsortiums={" + Utils.roamingConsortiumsToString(mRoamingConsortiums) +
+ '}' +
+ ", mMatchAnyOIs={" + Utils.roamingConsortiumsToString(mMatchAnyOIs) + '}' +
+ ", mMatchAllOIs={" + Utils.roamingConsortiumsToString(mMatchAllOIs) + '}' +
+ ", mCredential=" + mCredential +
+ ", mFriendlyName='" + mFriendlyName + '\'' +
+ ", mIconURL='" + mIconURL + '\'' +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
index 1c04e43..9a034ad 100644
--- a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
+++ b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
@@ -1779,6 +1779,9 @@
.create();
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ WindowManager.LayoutParams attrs = dialog.getWindow().getAttributes();
+ attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ dialog.getWindow().setAttributes(attrs);
dialog.show();
mFrequencyConflictDialog = dialog;
}
@@ -2259,6 +2262,9 @@
.setPositiveButton(r.getString(R.string.ok), null)
.create();
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ WindowManager.LayoutParams attrs = dialog.getWindow().getAttributes();
+ attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ dialog.getWindow().setAttributes(attrs);
dialog.show();
}
@@ -2287,6 +2293,9 @@
.setPositiveButton(r.getString(R.string.ok), null)
.create();
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ WindowManager.LayoutParams attrs = dialog.getWindow().getAttributes();
+ attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ dialog.getWindow().setAttributes(attrs);
dialog.show();
}
@@ -2365,6 +2374,9 @@
}
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ WindowManager.LayoutParams attrs = dialog.getWindow().getAttributes();
+ attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ dialog.getWindow().setAttributes(attrs);
dialog.show();
}
diff --git a/service/jni/com_android_server_wifi_WifiNative.cpp b/service/jni/com_android_server_wifi_WifiNative.cpp
index e9e85ac..bf073f7 100644
--- a/service/jni/com_android_server_wifi_WifiNative.cpp
+++ b/service/jni/com_android_server_wifi_WifiNative.cpp
@@ -17,17 +17,21 @@
#define LOG_TAG "wifi"
#include "jni.h"
+#include "JniConstants.h"
#include <ScopedUtfChars.h>
+#include <ScopedBytes.h>
#include <utils/misc.h>
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
#include <utils/String16.h>
#include <ctype.h>
-
+#include <sys/socket.h>
+#include <linux/if.h>
#include "wifi.h"
#include "wifi_hal.h"
#include "jni_helper.h"
-
+#include "rtt.h"
+#include "wifi_hal_stub.h"
#define REPLY_BUF_SIZE 4096 // wpa_supplicant's maximum size.
#define EVENT_BUF_SIZE 2048
@@ -35,6 +39,67 @@
static jint DBG = false;
+//Please put all HAL function call here and call from the function table instead of directly call
+static wifi_hal_fn hal_fn;
+int init_wifi_hal_func_table(wifi_hal_fn *hal_fn) {
+ if (hal_fn == NULL) {
+ return -1;
+ }
+ hal_fn->wifi_initialize = wifi_initialize_stub;
+ hal_fn->wifi_cleanup = wifi_cleanup_stub;
+ hal_fn->wifi_event_loop = wifi_event_loop_stub;
+ hal_fn->wifi_get_error_info = wifi_get_error_info_stub;
+ hal_fn->wifi_get_supported_feature_set = wifi_get_supported_feature_set_stub;
+ hal_fn->wifi_get_concurrency_matrix = wifi_get_concurrency_matrix_stub;
+ hal_fn->wifi_set_scanning_mac_oui = wifi_set_scanning_mac_oui_stub;
+ hal_fn->wifi_get_supported_channels = wifi_get_supported_channels_stub;
+ hal_fn->wifi_is_epr_supported = wifi_is_epr_supported_stub;
+ hal_fn->wifi_get_ifaces = wifi_get_ifaces_stub;
+ hal_fn->wifi_get_iface_name = wifi_get_iface_name_stub;
+ hal_fn->wifi_reset_iface_event_handler = wifi_reset_iface_event_handler_stub;
+ hal_fn->wifi_start_gscan = wifi_start_gscan_stub;
+ hal_fn->wifi_stop_gscan = wifi_stop_gscan_stub;
+ hal_fn->wifi_get_cached_gscan_results = wifi_get_cached_gscan_results_stub;
+ hal_fn->wifi_set_bssid_hotlist = wifi_set_bssid_hotlist_stub;
+ hal_fn->wifi_reset_bssid_hotlist = wifi_reset_bssid_hotlist_stub;
+ hal_fn->wifi_set_significant_change_handler = wifi_set_significant_change_handler_stub;
+ hal_fn->wifi_reset_significant_change_handler = wifi_reset_significant_change_handler_stub;
+ hal_fn->wifi_get_gscan_capabilities = wifi_get_gscan_capabilities_stub;
+ hal_fn->wifi_set_link_stats = wifi_set_link_stats_stub;
+ hal_fn->wifi_get_link_stats = wifi_get_link_stats_stub;
+ hal_fn->wifi_clear_link_stats = wifi_clear_link_stats_stub;
+ hal_fn->wifi_get_valid_channels = wifi_get_valid_channels_stub;
+ hal_fn->wifi_rtt_range_request = wifi_rtt_range_request_stub;
+ hal_fn->wifi_rtt_range_cancel = wifi_rtt_range_cancel_stub;
+ hal_fn->wifi_get_rtt_capabilities = wifi_get_rtt_capabilities_stub;
+ hal_fn->wifi_start_logging = wifi_start_logging_stub;
+ hal_fn->wifi_set_epno_list = wifi_set_epno_list_stub;
+ hal_fn->wifi_set_country_code = wifi_set_country_code_stub;
+ hal_fn->wifi_enable_tdls = wifi_enable_tdls_stub;
+ hal_fn->wifi_disable_tdls = wifi_disable_tdls_stub;
+ hal_fn->wifi_get_tdls_status = wifi_get_tdls_status_stub;
+ hal_fn->wifi_get_tdls_capabilities = wifi_get_tdls_capabilities_stub;
+ hal_fn->wifi_set_nodfs_flag = wifi_set_nodfs_flag_stub;
+ hal_fn->wifi_get_firmware_memory_dump = wifi_get_firmware_memory_dump_stub;
+ hal_fn->wifi_set_log_handler = wifi_set_log_handler_stub;
+ hal_fn->wifi_reset_log_handler = wifi_reset_log_handler_stub;
+ hal_fn->wifi_set_alert_handler = wifi_set_alert_handler_stub;
+ hal_fn->wifi_reset_alert_handler = wifi_reset_alert_handler_stub;
+ hal_fn->wifi_get_firmware_version = wifi_get_firmware_version_stub;
+ hal_fn->wifi_get_ring_buffers_status = wifi_get_ring_buffers_status_stub;
+ hal_fn->wifi_get_logger_supported_feature_set = wifi_get_logger_supported_feature_set_stub;
+ hal_fn->wifi_get_ring_data = wifi_get_ring_data_stub;
+ hal_fn->wifi_get_driver_version = wifi_get_driver_version_stub;
+ hal_fn->wifi_set_ssid_white_list = wifi_set_ssid_white_list_stub;
+ hal_fn->wifi_set_gscan_roam_params = wifi_set_gscan_roam_params_stub;
+ hal_fn->wifi_set_bssid_preference = wifi_set_bssid_preference_stub;
+ hal_fn->wifi_enable_lazy_roam = wifi_enable_lazy_roam_stub;
+ hal_fn->wifi_set_bssid_blacklist = wifi_set_bssid_blacklist_stub;
+ hal_fn->wifi_start_sending_offloaded_packet = wifi_start_sending_offloaded_packet_stub;
+ hal_fn->wifi_stop_sending_offloaded_packet = wifi_stop_sending_offloaded_packet_stub;
+ return 0;
+}
+
static bool doCommand(JNIEnv* env, jstring javaCommand,
char* reply, size_t reply_len) {
ScopedUtfChars command(env, javaCommand);
@@ -152,53 +217,140 @@
static const char *WifiIfaceHandleVarName = "sWifiIfaceHandles";
static jmethodID OnScanResultsMethodID;
-static JNIEnv *getEnv() {
- JNIEnv *env = NULL;
- mVM->AttachCurrentThread(&env, NULL);
- return env;
+static wifi_handle getWifiHandle(JNIHelper &helper, jclass cls) {
+ return (wifi_handle) helper.getStaticLongField(cls, WifiHandleVarName);
}
-static wifi_handle getWifiHandle(JNIEnv *env, jclass cls) {
- return (wifi_handle) getStaticLongField(env, cls, WifiHandleVarName);
+static wifi_interface_handle getIfaceHandle(JNIHelper &helper, jclass cls, jint index) {
+ return (wifi_interface_handle) helper.getStaticLongArrayField(cls, WifiIfaceHandleVarName, index);
}
-static wifi_interface_handle getIfaceHandle(JNIEnv *env, jclass cls, jint index) {
- return (wifi_interface_handle) getStaticLongArrayField(env, cls, WifiIfaceHandleVarName, index);
-}
+jboolean setSSIDField(JNIHelper helper, jobject scanResult, const char *rawSsid) {
-static jobject createScanResult(JNIEnv *env, wifi_scan_result *result) {
+ int len = strlen(rawSsid);
+
+ if (len > 0) {
+ JNIObject<jbyteArray> ssidBytes = helper.newByteArray(len);
+ helper.setByteArrayRegion(ssidBytes, 0, len, (jbyte *) rawSsid);
+ jboolean ret = helper.callStaticMethod(mCls,
+ "setSsid", "([BLandroid/net/wifi/ScanResult;)Z", ssidBytes.get(), scanResult);
+ return ret;
+ } else {
+ //empty SSID or SSID start with \0
+ return true;
+ }
+}
+static JNIObject<jobject> createScanResult(JNIHelper &helper, wifi_scan_result *result) {
// ALOGD("creating scan result");
- jobject scanResult = createObject(env, "android/net/wifi/ScanResult");
+ JNIObject<jobject> scanResult = helper.createObject("android/net/wifi/ScanResult");
if (scanResult == NULL) {
ALOGE("Error in creating scan result");
- return NULL;
+ return JNIObject<jobject>(helper, NULL);
}
- // ALOGD("setting SSID to %s", result.ssid);
- setStringField(env, scanResult, "SSID", result->ssid);
+ ALOGV("setting SSID to %s", result->ssid);
+
+ if (!setSSIDField(helper, scanResult, result->ssid)) {
+ ALOGE("Error on set SSID");
+ return JNIObject<jobject>(helper, NULL);
+ }
char bssid[32];
sprintf(bssid, "%02x:%02x:%02x:%02x:%02x:%02x", result->bssid[0], result->bssid[1],
result->bssid[2], result->bssid[3], result->bssid[4], result->bssid[5]);
- setStringField(env, scanResult, "BSSID", bssid);
+ helper.setStringField(scanResult, "BSSID", bssid);
- setIntField(env, scanResult, "level", result->rssi);
- setIntField(env, scanResult, "frequency", result->channel);
- setLongField(env, scanResult, "timestamp", result->ts);
+ helper.setIntField(scanResult, "level", result->rssi);
+ helper.setIntField(scanResult, "frequency", result->channel);
+ helper.setLongField(scanResult, "timestamp", result->ts);
return scanResult;
}
-static jboolean android_net_wifi_startHal(JNIEnv* env, jclass cls) {
- wifi_handle halHandle = getWifiHandle(env, cls);
+int set_iface_flags(const char *ifname, int dev_up) {
+ struct ifreq ifr;
+ int ret;
+ int sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ ALOGD("Bad socket: %d\n", sock);
+ return -errno;
+ }
+ //ALOGD("setting interface %s flags (%s)\n", ifname, dev_up ? "UP" : "DOWN");
+
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+
+ //ALOGD("reading old value\n");
+
+ if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
+ ret = errno ? -errno : -999;
+ ALOGE("Could not read interface %s flags: %d\n", ifname, errno);
+ close(sock);
+ return ret;
+ } else {
+ //ALOGD("writing new value\n");
+ }
+
+ if (dev_up) {
+ if (ifr.ifr_flags & IFF_UP) {
+ // ALOGD("interface %s is already up\n", ifname);
+ close(sock);
+ return 0;
+ }
+ ifr.ifr_flags |= IFF_UP;
+ } else {
+ if (!(ifr.ifr_flags & IFF_UP)) {
+ // ALOGD("interface %s is already down\n", ifname);
+ close(sock);
+ return 0;
+ }
+ ifr.ifr_flags &= ~IFF_UP;
+ }
+
+ if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) {
+ ALOGE("Could not set interface %s flags: %d\n", ifname, errno);
+ ret = errno ? -errno : -999;
+ close(sock);
+ return ret;
+ } else {
+ ALOGD("set interface %s flags (%s)\n", ifname, dev_up ? "UP" : "DOWN");
+ }
+ close(sock);
+ return 0;
+}
+
+static jboolean android_net_wifi_toggle_interface(JNIEnv* env, jclass cls, int toggle) {
+ return(set_iface_flags("wlan0", toggle) == 0);
+}
+
+static jboolean android_net_wifi_startHal(JNIEnv* env, jclass cls) {
+ JNIHelper helper(env);
+ wifi_handle halHandle = getWifiHandle(helper, cls);
if (halHandle == NULL) {
- wifi_error res = wifi_initialize(&halHandle);
+
+ if(init_wifi_hal_func_table(&hal_fn) != 0 ) {
+ ALOGD("Can not initialize the basic function pointer table");
+ return false;
+ }
+
+ wifi_error res = init_wifi_vendor_hal_func_table(&hal_fn);
+ if (res != WIFI_SUCCESS) {
+ ALOGD("Can not initialize the vendor function pointer table");
+ return false;
+ }
+
+ int ret = set_iface_flags("wlan0", 1);
+ if(ret != 0) {
+ return false;
+ }
+
+ res = hal_fn.wifi_initialize(&halHandle);
if (res == WIFI_SUCCESS) {
- setStaticLongField(env, cls, WifiHandleVarName, (jlong)halHandle);
+ helper.setStaticLongField(cls, WifiHandleVarName, (jlong)halHandle);
ALOGD("Did set static halHandle = %p", halHandle);
}
env->GetJavaVM(&mVM);
@@ -206,61 +358,73 @@
ALOGD("halHandle = %p, mVM = %p, mCls = %p", halHandle, mVM, mCls);
return res == WIFI_SUCCESS;
} else {
- return true;
+ return (set_iface_flags("wlan0", 1) == 0);
}
}
void android_net_wifi_hal_cleaned_up_handler(wifi_handle handle) {
ALOGD("In wifi cleaned up handler");
- JNIEnv * env = getEnv();
- setStaticLongField(env, mCls, WifiHandleVarName, 0);
- env->DeleteGlobalRef(mCls);
+ JNIHelper helper(mVM);
+ helper.setStaticLongField(mCls, WifiHandleVarName, 0);
+
+ helper.deleteGlobalRef(mCls);
mCls = NULL;
mVM = NULL;
}
static void android_net_wifi_stopHal(JNIEnv* env, jclass cls) {
ALOGD("In wifi stop Hal");
- wifi_handle halHandle = getWifiHandle(env, cls);
- wifi_cleanup(halHandle, android_net_wifi_hal_cleaned_up_handler);
+
+ JNIHelper helper(env);
+ wifi_handle halHandle = getWifiHandle(helper, cls);
+ if (halHandle == NULL)
+ return;
+
+ ALOGD("halHandle = %p, mVM = %p, mCls = %p", halHandle, mVM, mCls);
+ hal_fn.wifi_cleanup(halHandle, android_net_wifi_hal_cleaned_up_handler);
}
static void android_net_wifi_waitForHalEvents(JNIEnv* env, jclass cls) {
ALOGD("waitForHalEvents called, vm = %p, obj = %p, env = %p", mVM, mCls, env);
- wifi_handle halHandle = getWifiHandle(env, cls);
- wifi_event_loop(halHandle);
+ JNIHelper helper(env);
+ wifi_handle halHandle = getWifiHandle(helper, cls);
+ hal_fn.wifi_event_loop(halHandle);
+ set_iface_flags("wlan0", 0);
}
static int android_net_wifi_getInterfaces(JNIEnv *env, jclass cls) {
int n = 0;
- wifi_handle halHandle = getWifiHandle(env, cls);
+
+ JNIHelper helper(env);
+
+ wifi_handle halHandle = getWifiHandle(helper, cls);
wifi_interface_handle *ifaceHandles = NULL;
- int result = wifi_get_ifaces(halHandle, &n, &ifaceHandles);
+ int result = hal_fn.wifi_get_ifaces(halHandle, &n, &ifaceHandles);
if (result < 0) {
return result;
}
if (n < 0) {
- THROW(env, "android_net_wifi_getInterfaces no interfaces");
+ THROW(helper,"android_net_wifi_getInterfaces no interfaces");
return 0;
}
if (ifaceHandles == NULL) {
- THROW(env, "android_net_wifi_getInterfaces null interface array");
+ THROW(helper,"android_net_wifi_getInterfaces null interface array");
return 0;
}
if (n > 8) {
- THROW(env, "Too many interfaces");
+ THROW(helper,"Too many interfaces");
return 0;
}
jlongArray array = (env)->NewLongArray(n);
if (array == NULL) {
- THROW(env, "Error in accessing array");
+ THROW(helper,"Error in accessing array");
return 0;
}
@@ -268,135 +432,133 @@
for (int i = 0; i < n; i++) {
elems[i] = reinterpret_cast<jlong>(ifaceHandles[i]);
}
- env->SetLongArrayRegion(array, 0, n, elems);
- setStaticLongArrayField(env, cls, WifiIfaceHandleVarName, array);
+
+ helper.setLongArrayRegion(array, 0, n, elems);
+ helper.setStaticLongArrayField(cls, WifiIfaceHandleVarName, array);
return (result < 0) ? result : n;
}
static jstring android_net_wifi_getInterfaceName(JNIEnv *env, jclass cls, jint i) {
+
char buf[EVENT_BUF_SIZE];
- jlong value = getStaticLongArrayField(env, cls, WifiIfaceHandleVarName, i);
+ JNIHelper helper(env);
+
+ jlong value = helper.getStaticLongArrayField(cls, WifiIfaceHandleVarName, i);
wifi_interface_handle handle = (wifi_interface_handle) value;
- int result = ::wifi_get_iface_name(handle, buf, sizeof(buf));
+ int result = hal_fn.wifi_get_iface_name(handle, buf, sizeof(buf));
if (result < 0) {
return NULL;
} else {
- return env->NewStringUTF(buf);
+ JNIObject<jstring> name = helper.newStringUTF(buf);
+ return name.detach();
}
}
static void onScanResultsAvailable(wifi_request_id id, unsigned num_results) {
- JNIEnv *env = NULL;
- mVM->AttachCurrentThread(&env, NULL);
+ JNIHelper helper(mVM);
- ALOGD("onScanResultsAvailable called, vm = %p, obj = %p, env = %p", mVM, mCls, env);
+ // ALOGD("onScanResultsAvailable called, vm = %p, obj = %p, env = %p", mVM, mCls, env);
- reportEvent(env, mCls, "onScanResultsAvailable", "(I)V", id);
+ helper.reportEvent(mCls, "onScanResultsAvailable", "(I)V", id);
}
static void onScanEvent(wifi_scan_event event, unsigned status) {
- JNIEnv *env = NULL;
- mVM->AttachCurrentThread(&env, NULL);
- ALOGD("onScanStatus called, vm = %p, obj = %p, env = %p", mVM, mCls, env);
+ JNIHelper helper(mVM);
- reportEvent(env, mCls, "onScanStatus", "(I)V", status);
+ // ALOGD("onScanStatus called, vm = %p, obj = %p, env = %p", mVM, mCls, env);
+
+ helper.reportEvent(mCls, "onScanStatus", "(I)V", event);
}
static void onFullScanResult(wifi_request_id id, wifi_scan_result *result) {
- JNIEnv *env = NULL;
- mVM->AttachCurrentThread(&env, NULL);
+ JNIHelper helper(mVM);
- ALOGD("onFullScanResult called, vm = %p, obj = %p, env = %p", mVM, mCls, env);
+ //ALOGD("onFullScanResult called, vm = %p, obj = %p, env = %p", mVM, mCls, env);
- jobject scanResult = createScanResult(env, result);
+ JNIObject<jobject> scanResult = createScanResult(helper, result);
- ALOGD("Creating a byte array of length %d", result->ie_length);
+ //ALOGD("Creating a byte array of length %d", result->ie_length);
- jbyteArray elements = env->NewByteArray(result->ie_length);
+ JNIObject<jbyteArray> elements = helper.newByteArray(result->ie_length);
if (elements == NULL) {
ALOGE("Error in allocating array");
return;
}
- ALOGE("Setting byte array");
+ // ALOGD("Setting byte array");
jbyte *bytes = (jbyte *)&(result->ie_data[0]);
- env->SetByteArrayRegion(elements, 0, result->ie_length, bytes);
+ helper.setByteArrayRegion(elements, 0, result->ie_length, bytes);
- ALOGE("Returning result");
+ // ALOGD("Returning result");
- reportEvent(env, mCls, "onFullScanResult", "(ILandroid/net/wifi/ScanResult;[B)V", id,
- scanResult, elements);
-
- env->DeleteLocalRef(scanResult);
- env->DeleteLocalRef(elements);
+ helper.reportEvent(mCls, "onFullScanResult", "(ILandroid/net/wifi/ScanResult;[B)V", id,
+ scanResult.get(), elements.get());
}
static jboolean android_net_wifi_startScan(
JNIEnv *env, jclass cls, jint iface, jint id, jobject settings) {
- wifi_interface_handle handle = getIfaceHandle(env, cls, iface);
- ALOGD("starting scan on interface[%d] = %p", iface, handle);
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, 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");
+ params.base_period = helper.getIntField(settings, "base_period_ms");
+ params.max_ap_per_scan = helper.getIntField(settings, "max_ap_per_scan");
+ params.report_threshold_percent = helper.getIntField(settings, "report_threshold_percent");
+ params.report_threshold_num_scans = helper.getIntField(settings, "report_threshold_num_scans");
- ALOGD("Initialized common fields %d, %d, %d", params.base_period,
- params.max_ap_per_scan, params.report_threshold);
+ ALOGD("Initialized common fields %d, %d, %d, %d", params.base_period, params.max_ap_per_scan,
+ params.report_threshold_percent, params.report_threshold_num_scans);
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");
+ params.num_buckets = helper.getIntField(settings, "num_buckets");
- ALOGD("Initialized num_buckets to %d", params.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);
+ JNIObject<jobject> bucket = helper.getObjectArrayField(
+ 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");
+ params.buckets[i].bucket = helper.getIntField(bucket, "bucket");
+ params.buckets[i].band = (wifi_band) helper.getIntField(bucket, "band");
+ params.buckets[i].period = helper.getIntField(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");
+ int report_events = helper.getIntField(bucket, "report_events");
params.buckets[i].report_events = report_events;
- ALOGD("Initialized report events to %d", params.buckets[i].report_events);
+ ALOGD("bucket[%d] = %d:%d:%d:%d", i, params.buckets[i].bucket,
+ params.buckets[i].band, params.buckets[i].period, 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);
+ params.buckets[i].num_channels = helper.getIntField(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);
+ JNIObject<jobject> channel = helper.getObjectArrayField(
+ 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");
+ params.buckets[i].channels[j].channel = helper.getIntField(channel, "frequency");
+ params.buckets[i].channels[j].dwellTimeMs = helper.getIntField(channel, "dwell_time_ms");
- bool passive = getBoolField(env, channel, "passive");
+ bool passive = helper.getBoolField(channel, "passive");
params.buckets[i].channels[j].passive = (passive ? 1 : 0);
- ALOGD("Initialized channel %d", params.buckets[i].channels[j].channel);
+ // ALOGD("Initialized channel %d", params.buckets[i].channels[j].channel);
}
}
- ALOGD("Initialized all fields");
+ // ALOGD("Initialized all fields");
wifi_scan_result_handler handler;
memset(&handler, 0, sizeof(handler));
@@ -404,65 +566,84 @@
handler.on_full_scan_result = &onFullScanResult;
handler.on_scan_event = &onScanEvent;
- return wifi_start_gscan(id, handle, params, handler) == WIFI_SUCCESS;
+ return hal_fn.wifi_start_gscan(id, handle, params, handler) == WIFI_SUCCESS;
}
static jboolean android_net_wifi_stopScan(JNIEnv *env, jclass cls, jint iface, jint id) {
- wifi_interface_handle handle = getIfaceHandle(env, cls, iface);
- ALOGD("stopping scan on interface[%d] = %p", iface, handle);
- return wifi_stop_gscan(id, handle) == WIFI_SUCCESS;
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+ // ALOGD("stopping scan on interface[%d] = %p", iface, handle);
+
+ return hal_fn.wifi_stop_gscan(id, handle) == WIFI_SUCCESS;
+}
+
+static int compare_scan_result_timestamp(const void *v1, const void *v2) {
+ const wifi_scan_result *result1 = static_cast<const wifi_scan_result *>(v1);
+ const wifi_scan_result *result2 = static_cast<const wifi_scan_result *>(v2);
+ return result1->ts - result2->ts;
}
static jobject android_net_wifi_getScanResults(
JNIEnv *env, jclass cls, jint iface, jboolean flush) {
-
- wifi_scan_result results[256];
- int num_results = 256;
-
- wifi_interface_handle handle = getIfaceHandle(env, cls, iface);
- ALOGD("getting scan results on interface[%d] = %p", iface, handle);
-
- int result = wifi_get_cached_gscan_results(handle, 1, num_results, results, &num_results);
+
+ JNIHelper helper(env);
+ wifi_cached_scan_results scan_data[64];
+ int num_scan_data = 64;
+
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+ // ALOGD("getting scan results on interface[%d] = %p", iface, handle);
+
+ byte b = flush ? 0xFF : 0;
+ int result = hal_fn.wifi_get_cached_gscan_results(handle, b, num_scan_data, scan_data, &num_scan_data);
if (result == WIFI_SUCCESS) {
- jclass clsScanResult = (env)->FindClass("android/net/wifi/ScanResult");
- if (clsScanResult == NULL) {
- ALOGE("Error in accessing class");
+ JNIObject<jobjectArray> scanData = helper.createObjectArray(
+ "android/net/wifi/WifiScanner$ScanData", num_scan_data);
+ if (scanData == NULL) {
+ ALOGE("Error in allocating array of scanData");
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_scan_data; i++) {
- 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");
+ JNIObject<jobject> data = helper.createObject("android/net/wifi/WifiScanner$ScanData");
+ if (data == NULL) {
+ ALOGE("Error in allocating scanData");
return NULL;
}
- setStringField(env, scanResult, "SSID", results[i].ssid);
+ helper.setIntField(data, "mId", scan_data[i].scan_id);
+ helper.setIntField(data, "mFlags", scan_data[i].flags);
- 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]);
+ /* sort all scan results by timestamp */
+ qsort(scan_data[i].results, scan_data[i].num_results,
+ sizeof(wifi_scan_result), compare_scan_result_timestamp);
- setStringField(env, scanResult, "BSSID", bssid);
+ JNIObject<jobjectArray> scanResults = helper.createObjectArray(
+ "android/net/wifi/ScanResult", scan_data[i].num_results);
+ if (scanResults == NULL) {
+ ALOGE("Error in allocating scanResult array");
+ return NULL;
+ }
- setIntField(env, scanResult, "level", results[i].rssi);
- setIntField(env, scanResult, "frequency", results[i].channel);
- setLongField(env, scanResult, "timestamp", results[i].ts);
+ wifi_scan_result *results = scan_data[i].results;
+ for (int j = 0; j < scan_data[i].num_results; j++) {
- env->SetObjectArrayElement(scanResults, i, scanResult);
- env->DeleteLocalRef(scanResult);
+ JNIObject<jobject> scanResult = createScanResult(helper, &results[j]);
+ if (scanResult == NULL) {
+ ALOGE("Error in creating scan result");
+ return NULL;
+ }
+
+ helper.setObjectArrayElement(scanResults, j, scanResult);
+ }
+
+ helper.setObjectField(data, "mResults", "[Landroid/net/wifi/ScanResult;", scanResults);
+ helper.setObjectArrayElement(scanData, i, data);
}
- return scanResults;
+ // ALOGD("retrieved %d scan data from interface[%d] = %p", num_scan_data, iface, handle);
+ return scanData.detach();
} else {
return NULL;
}
@@ -472,24 +653,25 @@
static jboolean android_net_wifi_getScanCapabilities(
JNIEnv *env, jclass cls, jint iface, jobject capabilities) {
- wifi_interface_handle handle = getIfaceHandle(env, cls, iface);
- ALOGD("getting scan capabilities on interface[%d] = %p", iface, handle);
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, 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);
+ int result = hal_fn.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",
+ helper.setIntField(capabilities, "max_scan_cache_size", c.max_scan_cache_size);
+ helper.setIntField(capabilities, "max_scan_buckets", c.max_scan_buckets);
+ helper.setIntField(capabilities, "max_ap_cache_per_scan", c.max_ap_cache_per_scan);
+ helper.setIntField(capabilities, "max_rssi_sample_size", c.max_rssi_sample_size);
+ helper.setIntField(capabilities, "max_scan_reporting_threshold", c.max_scan_reporting_threshold);
+ helper.setIntField(capabilities, "max_hotlist_bssids", c.max_hotlist_bssids);
+ helper.setIntField(capabilities, "max_significant_wifi_change_aps",
c.max_significant_wifi_change_aps);
return JNI_TRUE;
@@ -539,15 +721,15 @@
}
static bool parseMacAddress(JNIEnv *env, jobject obj, mac_addr addr) {
- jstring macAddrString = (jstring) getObjectField(
- env, obj, "bssid", "Ljava/lang/String;");
-
+ JNIHelper helper(env);
+ JNIObject<jstring> macAddrString = helper.getStringField(obj, "bssid");
if (macAddrString == NULL) {
ALOGE("Error getting bssid field");
return false;
}
- const char *bssid = env->GetStringUTFChars(macAddrString, NULL);
+ ScopedUtfChars chars(env, macAddrString);
+ const char *bssid = chars.c_str();
if (bssid == NULL) {
ALOGE("Error getting bssid");
return false;
@@ -560,19 +742,11 @@
static void onHotlistApFound(wifi_request_id id,
unsigned num_results, wifi_scan_result *results) {
- JNIEnv *env = NULL;
- mVM->AttachCurrentThread(&env, NULL);
+ JNIHelper helper(mVM);
+ ALOGD("onHotlistApFound called, vm = %p, obj = %p, num_results = %d", mVM, mCls, num_results);
- ALOGD("onHotlistApFound called, vm = %p, obj = %p, env = %p, num_results = %d",
- mVM, mCls, env, num_results);
-
- 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);
+ JNIObject<jobjectArray> scanResults = helper.newObjectArray(num_results,
+ "android/net/wifi/ScanResult", NULL);
if (scanResults == NULL) {
ALOGE("Error in allocating array");
return;
@@ -580,69 +754,91 @@
for (unsigned i = 0; i < num_results; i++) {
- jobject scanResult = createObject(env, "android/net/wifi/ScanResult");
+ JNIObject<jobject> scanResult = createScanResult(helper, &results[i]);
if (scanResult == NULL) {
ALOGE("Error in creating scan result");
return;
}
- setStringField(env, scanResult, "SSID", results[i].ssid);
+ helper.setObjectArrayElement(scanResults, i, scanResult);
- 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);
-
- ALOGD("Found AP %32s %s", results[i].ssid, bssid);
+ ALOGD("Found AP %32s", results[i].ssid);
}
- reportEvent(env, mCls, "onHotlistApFound", "(I[Landroid/net/wifi/ScanResult;)V",
- id, scanResults);
+ helper.reportEvent(mCls, "onHotlistApFound", "(I[Landroid/net/wifi/ScanResult;)V",
+ id, scanResults.get());
}
+static void onHotlistApLost(wifi_request_id id,
+ unsigned num_results, wifi_scan_result *results) {
+
+ JNIHelper helper(mVM);
+ ALOGD("onHotlistApLost called, vm = %p, obj = %p, num_results = %d", mVM, mCls, num_results);
+
+ JNIObject<jobjectArray> scanResults = helper.newObjectArray(num_results,
+ "android/net/wifi/ScanResult", NULL);
+ if (scanResults == NULL) {
+ ALOGE("Error in allocating array");
+ return;
+ }
+
+ for (unsigned i = 0; i < num_results; i++) {
+
+ JNIObject<jobject> scanResult = createScanResult(helper, &results[i]);
+ if (scanResult == NULL) {
+ ALOGE("Error in creating scan result");
+ return;
+ }
+
+ helper.setObjectArrayElement(scanResults, i, scanResult);
+
+ ALOGD("Lost AP %32s", results[i].ssid);
+ }
+
+ helper.reportEvent(mCls, "onHotlistApLost", "(I[Landroid/net/wifi/ScanResult;)V",
+ id, scanResults.get());
+}
+
+
static jboolean android_net_wifi_setHotlist(
JNIEnv *env, jclass cls, jint iface, jint id, jobject ap) {
- wifi_interface_handle handle = getIfaceHandle(env, cls, iface);
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, 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,
- "bssidInfos", "[Landroid/net/wifi/WifiScanner$BssidInfo;");
- params.num_ap = env->GetArrayLength(array);
+ params.lost_ap_sample_size = helper.getIntField(ap, "apLostThreshold");
- if (params.num_ap == 0) {
+ JNIObject<jobjectArray> array = helper.getArrayField(
+ ap, "bssidInfos", "[Landroid/net/wifi/WifiScanner$BssidInfo;");
+ params.num_bssid = helper.getArrayLength(array);
+
+ if (params.num_bssid == 0) {
ALOGE("Error in accesing array");
return false;
}
- if (params.num_ap >
+ if (params.num_bssid >
static_cast<int>(sizeof(params.ap) / sizeof(params.ap[0]))) {
ALOGE("setHotlist array length is too long");
android_errorWriteLog(0x534e4554, "31856351");
return false;
}
- for (int i = 0; i < params.num_ap; i++) {
- jobject objAp = env->GetObjectArrayElement(array, i);
+ for (int i = 0; i < params.num_bssid; i++) {
+ JNIObject<jobject> objAp = helper.getObjectArrayElement(array, i);
- jstring macAddrString = (jstring) getObjectField(
- env, objAp, "bssid", "Ljava/lang/String;");
+ JNIObject<jstring> macAddrString = helper.getStringField(objAp, "bssid");
if (macAddrString == NULL) {
ALOGE("Error getting bssid field");
return false;
}
- const char *bssid = env->GetStringUTFChars(macAddrString, NULL);
+ ScopedUtfChars chars(env, macAddrString);
+ const char *bssid = chars.c_str();
if (bssid == NULL) {
ALOGE("Error getting bssid");
return false;
@@ -658,40 +854,36 @@
ALOGD("Added bssid %s", bssidOut);
- params.ap[i].low = getIntField(env, objAp, "low");
- params.ap[i].high = getIntField(env, objAp, "high");
+ params.ap[i].low = helper.getIntField(objAp, "low");
+ params.ap[i].high = helper.getIntField(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;
+ handler.on_hotlist_ap_lost = &onHotlistApLost;
+ return hal_fn.wifi_set_bssid_hotlist(id, handle, params, handler) == WIFI_SUCCESS;
}
-static jboolean android_net_wifi_resetHotlist(
- JNIEnv *env, jclass cls, jint iface, jint id) {
+static jboolean android_net_wifi_resetHotlist(JNIEnv *env, jclass cls, jint iface, jint id) {
- wifi_interface_handle handle = getIfaceHandle(env, cls, iface);
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
ALOGD("resetting hotlist on interface[%d] = %p", iface, handle);
- return wifi_reset_bssid_hotlist(id, handle) == WIFI_SUCCESS;
+ return hal_fn.wifi_reset_bssid_hotlist(id, handle) == WIFI_SUCCESS;
}
void onSignificantWifiChange(wifi_request_id id,
unsigned num_results, wifi_significant_change_result **results) {
- JNIEnv *env = NULL;
- mVM->AttachCurrentThread(&env, NULL);
- ALOGD("onSignificantWifiChange called, vm = %p, obj = %p, env = %p", mVM, mCls, env);
+ JNIHelper helper(mVM);
- jclass clsScanResult = (env)->FindClass("android/net/wifi/ScanResult");
- if (clsScanResult == NULL) {
- ALOGE("Error in accessing class");
- return;
- }
+ ALOGD("onSignificantWifiChange called, vm = %p, obj = %p", mVM, mCls);
- jobjectArray scanResults = env->NewObjectArray(num_results, clsScanResult, NULL);
+ JNIObject<jobjectArray> scanResults = helper.newObjectArray(
+ num_results, "android/net/wifi/ScanResult", NULL);
if (scanResults == NULL) {
ALOGE("Error in allocating array");
return;
@@ -699,71 +891,72 @@
for (unsigned i = 0; i < num_results; i++) {
- wifi_significant_change_result result = *(results[i]);
+ wifi_significant_change_result &result = *(results[i]);
- jobject scanResult = createObject(env, "android/net/wifi/ScanResult");
+ JNIObject<jobject> scanResult = helper.createObject("android/net/wifi/ScanResult");
if (scanResult == NULL) {
ALOGE("Error in creating scan result");
return;
}
- // setStringField(env, scanResult, "SSID", results[i].ssid);
+ // helper.setStringField(scanResult, "SSID", results[i].ssid);
char bssid[32];
sprintf(bssid, "%02x:%02x:%02x:%02x:%02x:%02x", result.bssid[0], result.bssid[1],
result.bssid[2], result.bssid[3], result.bssid[4], result.bssid[5]);
- setStringField(env, scanResult, "BSSID", bssid);
+ helper.setStringField(scanResult, "BSSID", bssid);
- setIntField(env, scanResult, "level", result.rssi[0]);
- setIntField(env, scanResult, "frequency", result.channel);
- // setLongField(env, scanResult, "timestamp", result.ts);
+ helper.setIntField(scanResult, "level", result.rssi[0]);
+ helper.setIntField(scanResult, "frequency", result.channel);
+ // helper.setLongField(scanResult, "timestamp", result.ts);
- env->SetObjectArrayElement(scanResults, i, scanResult);
+ helper.setObjectArrayElement(scanResults, i, scanResult);
}
- reportEvent(env, mCls, "onSignificantWifiChange", "(I[Landroid/net/wifi/ScanResult;)V",
- id, scanResults);
+ helper.reportEvent(mCls, "onSignificantWifiChange", "(I[Landroid/net/wifi/ScanResult;)V",
+ id, scanResults.get());
}
static jboolean android_net_wifi_trackSignificantWifiChange(
JNIEnv *env, jclass cls, jint iface, jint id, jobject settings) {
- wifi_interface_handle handle = getIfaceHandle(env, cls, iface);
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
ALOGD("tracking significant wifi change on interface[%d] = %p", iface, handle);
wifi_significant_change_params params;
memset(¶ms, 0, sizeof(params));
- params.rssi_sample_size = getIntField(env, settings, "rssiSampleSize");
- params.lost_ap_sample_size = getIntField(env, settings, "lostApSampleSize");
- params.min_breaching = getIntField(env, settings, "minApsBreachingThreshold");
+ params.rssi_sample_size = helper.getIntField(settings, "rssiSampleSize");
+ params.lost_ap_sample_size = helper.getIntField(settings, "lostApSampleSize");
+ params.min_breaching = helper.getIntField(settings, "minApsBreachingThreshold");
const char *bssid_info_array_type = "[Landroid/net/wifi/WifiScanner$BssidInfo;";
- jobjectArray bssids = (jobjectArray)getObjectField(
- env, settings, "bssidInfos", bssid_info_array_type);
- params.num_ap = env->GetArrayLength(bssids);
+ JNIObject<jobjectArray> bssids = helper.getArrayField(
+ settings, "bssidInfos", bssid_info_array_type);
+ params.num_bssid = helper.getArrayLength(bssids);
- if (params.num_ap == 0) {
+ if (params.num_bssid == 0) {
ALOGE("Error in accessing 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_ap);
+ params.lost_ap_sample_size, params.min_breaching, params.num_bssid);
- for (int i = 0; i < params.num_ap; i++) {
- jobject objAp = env->GetObjectArrayElement(bssids, i);
+ for (int i = 0; i < params.num_bssid; i++) {
+ JNIObject<jobject> objAp = helper.getObjectArrayElement(bssids, i);
- jstring macAddrString = (jstring) getObjectField(
- env, objAp, "bssid", "Ljava/lang/String;");
+ JNIObject<jstring> macAddrString = helper.getStringField(objAp, "bssid");
if (macAddrString == NULL) {
ALOGE("Error getting bssid field");
return false;
}
- const char *bssid = env->GetStringUTFChars(macAddrString, NULL);
+ ScopedUtfChars chars(env, macAddrString.get());
+ const char *bssid = chars.c_str();
if (bssid == NULL) {
ALOGE("Error getting bssid");
return false;
@@ -777,28 +970,29 @@
sprintf(bssidOut, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1],
addr[2], addr[3], addr[4], addr[5]);
- params.ap[i].low = getIntField(env, objAp, "low");
- params.ap[i].high = getIntField(env, objAp, "high");
+ params.ap[i].low = helper.getIntField(objAp, "low");
+ params.ap[i].high = helper.getIntField(objAp, "high");
ALOGD("Added bssid %s, [%04d, %04d]", bssidOut, params.ap[i].low, params.ap[i].high);
}
- ALOGD("Added %d bssids", params.num_ap);
+ ALOGD("Added %d bssids", params.num_bssid);
wifi_significant_change_handler handler;
memset(&handler, 0, sizeof(handler));
handler.on_significant_change = &onSignificantWifiChange;
- return wifi_set_significant_change_handler(id, handle, params, handler) == WIFI_SUCCESS;
+ return hal_fn.wifi_set_significant_change_handler(id, handle, params, handler) == WIFI_SUCCESS;
}
static jboolean android_net_wifi_untrackSignificantWifiChange(
JNIEnv *env, jclass cls, jint iface, jint id) {
- wifi_interface_handle handle = getIfaceHandle(env, cls, iface);
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
ALOGD("resetting significant wifi change on interface[%d] = %p", iface, handle);
- return wifi_reset_significant_change_handler(id, handle) == WIFI_SUCCESS;
+ return hal_fn.wifi_reset_significant_change_handler(id, handle) == WIFI_SUCCESS;
}
wifi_iface_stat link_stat;
@@ -820,54 +1014,70 @@
}
}
+static void android_net_wifi_setLinkLayerStats (JNIEnv *env, jclass cls, jint iface, int enable) {
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+
+ wifi_link_layer_params params;
+ params.aggressive_statistics_gathering = enable;
+ params.mpdu_size_threshold = 128;
+
+ ALOGD("android_net_wifi_setLinkLayerStats: %u\n", enable);
+
+ hal_fn.wifi_set_link_stats(handle, params);
+}
+
static jobject android_net_wifi_getLinkLayerStats (JNIEnv *env, jclass cls, jint iface) {
+ JNIHelper helper(env);
wifi_stats_result_handler handler;
memset(&handler, 0, sizeof(handler));
handler.on_link_stats_results = &onLinkStatsResults;
- wifi_interface_handle handle = getIfaceHandle(env, cls, iface);
- int result = wifi_get_link_stats(0, handle, handler);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+ int result = hal_fn.wifi_get_link_stats(0, handle, handler);
if (result < 0) {
ALOGE("android_net_wifi_getLinkLayerStats: failed to get link statistics\n");
return NULL;
}
- jobject wifiLinkLayerStats = createObject(env, "android/net/wifi/WifiLinkLayerStats");
+ JNIObject<jobject> wifiLinkLayerStats = helper.createObject(
+ "android/net/wifi/WifiLinkLayerStats");
if (wifiLinkLayerStats == NULL) {
ALOGE("Error in allocating wifiLinkLayerStats");
return NULL;
}
- setIntField(env, wifiLinkLayerStats, "beacon_rx", link_stat.beacon_rx);
- setIntField(env, wifiLinkLayerStats, "rssi_mgmt", link_stat.rssi_mgmt);
- setLongField(env, wifiLinkLayerStats, "rxmpdu_be", link_stat.ac[WIFI_AC_BE].rx_mpdu);
- setLongField(env, wifiLinkLayerStats, "rxmpdu_bk", link_stat.ac[WIFI_AC_BK].rx_mpdu);
- setLongField(env, wifiLinkLayerStats, "rxmpdu_vi", link_stat.ac[WIFI_AC_VI].rx_mpdu);
- setLongField(env, wifiLinkLayerStats, "rxmpdu_vo", link_stat.ac[WIFI_AC_VO].rx_mpdu);
- setLongField(env, wifiLinkLayerStats, "txmpdu_be", link_stat.ac[WIFI_AC_BE].tx_mpdu);
- setLongField(env, wifiLinkLayerStats, "txmpdu_bk", link_stat.ac[WIFI_AC_BK].tx_mpdu);
- setLongField(env, wifiLinkLayerStats, "txmpdu_vi", link_stat.ac[WIFI_AC_VI].tx_mpdu);
- setLongField(env, wifiLinkLayerStats, "txmpdu_vo", link_stat.ac[WIFI_AC_VO].tx_mpdu);
- setLongField(env, wifiLinkLayerStats, "lostmpdu_be", link_stat.ac[WIFI_AC_BE].mpdu_lost);
- setLongField(env, wifiLinkLayerStats, "lostmpdu_bk", link_stat.ac[WIFI_AC_BK].mpdu_lost);
- setLongField(env, wifiLinkLayerStats, "lostmpdu_vi", link_stat.ac[WIFI_AC_VI].mpdu_lost);
- setLongField(env, wifiLinkLayerStats, "lostmpdu_vo", link_stat.ac[WIFI_AC_VO].mpdu_lost);
- setLongField(env, wifiLinkLayerStats, "retries_be", link_stat.ac[WIFI_AC_BE].retries);
- setLongField(env, wifiLinkLayerStats, "retries_bk", link_stat.ac[WIFI_AC_BK].retries);
- setLongField(env, wifiLinkLayerStats, "retries_vi", link_stat.ac[WIFI_AC_VI].retries);
- setLongField(env, wifiLinkLayerStats, "retries_vo", link_stat.ac[WIFI_AC_VO].retries);
+ helper.setIntField(wifiLinkLayerStats, "beacon_rx", link_stat.beacon_rx);
+ helper.setIntField(wifiLinkLayerStats, "rssi_mgmt", link_stat.rssi_mgmt);
+ helper.setLongField(wifiLinkLayerStats, "rxmpdu_be", link_stat.ac[WIFI_AC_BE].rx_mpdu);
+ helper.setLongField(wifiLinkLayerStats, "rxmpdu_bk", link_stat.ac[WIFI_AC_BK].rx_mpdu);
+ helper.setLongField(wifiLinkLayerStats, "rxmpdu_vi", link_stat.ac[WIFI_AC_VI].rx_mpdu);
+ helper.setLongField(wifiLinkLayerStats, "rxmpdu_vo", link_stat.ac[WIFI_AC_VO].rx_mpdu);
+ helper.setLongField(wifiLinkLayerStats, "txmpdu_be", link_stat.ac[WIFI_AC_BE].tx_mpdu);
+ helper.setLongField(wifiLinkLayerStats, "txmpdu_bk", link_stat.ac[WIFI_AC_BK].tx_mpdu);
+ helper.setLongField(wifiLinkLayerStats, "txmpdu_vi", link_stat.ac[WIFI_AC_VI].tx_mpdu);
+ helper.setLongField(wifiLinkLayerStats, "txmpdu_vo", link_stat.ac[WIFI_AC_VO].tx_mpdu);
+ helper.setLongField(wifiLinkLayerStats, "lostmpdu_be", link_stat.ac[WIFI_AC_BE].mpdu_lost);
+ helper.setLongField(wifiLinkLayerStats, "lostmpdu_bk", link_stat.ac[WIFI_AC_BK].mpdu_lost);
+ helper.setLongField(wifiLinkLayerStats, "lostmpdu_vi", link_stat.ac[WIFI_AC_VI].mpdu_lost);
+ helper.setLongField(wifiLinkLayerStats, "lostmpdu_vo", link_stat.ac[WIFI_AC_VO].mpdu_lost);
+ helper.setLongField(wifiLinkLayerStats, "retries_be", link_stat.ac[WIFI_AC_BE].retries);
+ helper.setLongField(wifiLinkLayerStats, "retries_bk", link_stat.ac[WIFI_AC_BK].retries);
+ helper.setLongField(wifiLinkLayerStats, "retries_vi", link_stat.ac[WIFI_AC_VI].retries);
+ helper.setLongField(wifiLinkLayerStats, "retries_vo", link_stat.ac[WIFI_AC_VO].retries);
+ helper.setIntField(wifiLinkLayerStats, "on_time", radio_stat.on_time);
+ helper.setIntField(wifiLinkLayerStats, "tx_time", radio_stat.tx_time);
+ helper.setIntField(wifiLinkLayerStats, "rx_time", radio_stat.rx_time);
+ helper.setIntField(wifiLinkLayerStats, "on_time_scan", radio_stat.on_time_scan);
- setIntField(env, wifiLinkLayerStats, "on_time", radio_stat.on_time);
- setIntField(env, wifiLinkLayerStats, "tx_time", radio_stat.tx_time);
- setIntField(env, wifiLinkLayerStats, "rx_time", radio_stat.rx_time);
- setIntField(env, wifiLinkLayerStats, "on_time_scan", radio_stat.on_time_scan);
-
- return wifiLinkLayerStats;
+ return wifiLinkLayerStats.detach();
}
static jint android_net_wifi_getSupportedFeatures(JNIEnv *env, jclass cls, jint iface) {
- wifi_interface_handle handle = getIfaceHandle(env, cls, iface);
+
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
feature_set set = 0;
wifi_error result = WIFI_SUCCESS;
@@ -883,31 +1093,24 @@
| WIFI_FEATURE_EPR;
*/
- result = wifi_get_supported_feature_set(handle, &set);
+ result = hal_fn.wifi_get_supported_feature_set(handle, &set);
if (result == WIFI_SUCCESS) {
- /* Temporary workaround for RTT capability */
- set = set | WIFI_FEATURE_D2AP_RTT;
- ALOGD("wifi_get_supported_feature_set returned set = 0x%x", set);
+ // ALOGD("wifi_get_supported_feature_set returned set = 0x%x", set);
return set;
} else {
- ALOGD("wifi_get_supported_feature_set returned error = 0x%x", result);
+ ALOGE("wifi_get_supported_feature_set returned error = 0x%x", result);
return 0;
}
}
-static void onRttResults(wifi_request_id id, unsigned num_results, wifi_rtt_result results[]) {
- JNIEnv *env = NULL;
- mVM->AttachCurrentThread(&env, NULL);
+static void onRttResults(wifi_request_id id, unsigned num_results, wifi_rtt_result* results[]) {
- ALOGD("onRttResults called, vm = %p, obj = %p, env = %p", mVM, mCls, env);
+ JNIHelper helper(mVM);
- jclass clsRttResult = (env)->FindClass("android/net/wifi/RttManager$RttResult");
- if (clsRttResult == NULL) {
- ALOGE("Error in accessing class");
- return;
- }
+ ALOGD("onRttResults called, vm = %p, obj = %p", mVM, mCls);
- jobjectArray rttResults = env->NewObjectArray(num_results, clsRttResult, NULL);
+ JNIObject<jobjectArray> rttResults = helper.newObjectArray(
+ num_results, "android/net/wifi/RttManager$RttResult", NULL);
if (rttResults == NULL) {
ALOGE("Error in allocating array");
return;
@@ -915,37 +1118,76 @@
for (unsigned i = 0; i < num_results; i++) {
- wifi_rtt_result& result = results[i];
+ wifi_rtt_result *result = results[i];
- jobject rttResult = createObject(env, "android/net/wifi/RttManager$RttResult");
+ JNIObject<jobject> rttResult = helper.createObject("android/net/wifi/RttManager$RttResult");
if (rttResult == NULL) {
ALOGE("Error in creating rtt result");
return;
}
char bssid[32];
- sprintf(bssid, "%02x:%02x:%02x:%02x:%02x:%02x", result.addr[0], result.addr[1],
- result.addr[2], result.addr[3], result.addr[4], result.addr[5]);
+ sprintf(bssid, "%02x:%02x:%02x:%02x:%02x:%02x", result->addr[0], result->addr[1],
+ result->addr[2], result->addr[3], result->addr[4], result->addr[5]);
- setStringField(env, rttResult, "bssid", bssid);
- setIntField(env, rttResult, "status", result.status);
- setIntField(env, rttResult, "requestType", result.type);
- setLongField(env, rttResult, "ts", result.ts);
- setIntField(env, rttResult, "rssi", result.rssi);
- setIntField(env, rttResult, "rssi_spread", result.rssi_spread);
- setIntField(env, rttResult, "tx_rate", result.tx_rate.bitrate);
- setLongField(env, rttResult, "rtt_ns", result.rtt);
- setLongField(env, rttResult, "rtt_sd_ns", result.rtt_sd);
- setLongField(env, rttResult, "rtt_spread_ns", result.rtt_spread);
- setIntField(env, rttResult, "distance_cm", result.distance);
- setIntField(env, rttResult, "distance_sd_cm", result.distance_sd);
- setIntField(env, rttResult, "distance_spread_cm", result.distance_spread);
+ helper.setStringField(rttResult, "bssid", bssid);
+ helper.setIntField( rttResult, "burstNumber", result->burst_num);
+ helper.setIntField( rttResult, "measurementFrameNumber", result->measurement_number);
+ helper.setIntField( rttResult, "successMeasurementFrameNumber", result->success_number);
+ helper.setIntField(rttResult, "frameNumberPerBurstPeer", result->number_per_burst_peer);
+ helper.setIntField( rttResult, "status", result->status);
+ helper.setIntField( rttResult, "measurementType", result->type);
+ helper.setIntField(rttResult, "retryAfterDuration", result->retry_after_duration);
+ helper.setLongField(rttResult, "ts", result->ts);
+ helper.setIntField( rttResult, "rssi", result->rssi);
+ helper.setIntField( rttResult, "rssiSpread", result->rssi_spread);
+ helper.setIntField( rttResult, "txRate", result->tx_rate.bitrate);
+ helper.setIntField( rttResult, "rxRate", result->rx_rate.bitrate);
+ helper.setLongField(rttResult, "rtt", result->rtt);
+ helper.setLongField(rttResult, "rttStandardDeviation", result->rtt_sd);
+ helper.setIntField( rttResult, "distance", result->distance);
+ helper.setIntField( rttResult, "distanceStandardDeviation", result->distance_sd);
+ helper.setIntField( rttResult, "distanceSpread", result->distance_spread);
+ helper.setIntField( rttResult, "burstDuration", result->burst_duration);
+ helper.setIntField( rttResult, "negotiatedBurstNum", result->negotiated_burst_num);
- env->SetObjectArrayElement(rttResults, i, rttResult);
+ JNIObject<jobject> LCI = helper.createObject(
+ "android/net/wifi/RttManager$WifiInformationElement");
+ if (result->LCI != NULL && result->LCI->len > 0) {
+ ALOGD("Add LCI in result");
+ helper.setByteField(LCI, "id", result->LCI->id);
+ JNIObject<jbyteArray> elements = helper.newByteArray(result->LCI->len);
+ jbyte *bytes = (jbyte *)&(result->LCI->data[0]);
+ helper.setByteArrayRegion(elements, 0, result->LCI->len, bytes);
+ helper.setObjectField(LCI, "data", "[B", elements);
+ } else {
+ ALOGD("No LCI in result");
+ helper.setByteField(LCI, "id", (byte)(0xff));
+ }
+ helper.setObjectField(rttResult, "LCI",
+ "Landroid/net/wifi/RttManager$WifiInformationElement;", LCI);
+
+ JNIObject<jobject> LCR = helper.createObject(
+ "android/net/wifi/RttManager$WifiInformationElement");
+ if (result->LCR != NULL && result->LCR->len > 0) {
+ ALOGD("Add LCR in result");
+ helper.setByteField(LCR, "id", result->LCR->id);
+ JNIObject<jbyteArray> elements = helper.newByteArray(result->LCI->len);
+ jbyte *bytes = (jbyte *)&(result->LCR->data[0]);
+ helper.setByteArrayRegion(elements, 0, result->LCI->len, bytes);
+ helper.setObjectField(LCR, "data", "[B", elements);
+ } else {
+ ALOGD("No LCR in result");
+ helper.setByteField(LCR, "id", (byte)(0xff));
+ }
+ helper.setObjectField(rttResult, "LCR",
+ "Landroid/net/wifi/RttManager$WifiInformationElement;", LCR);
+
+ helper.setObjectArrayElement(rttResults, i, rttResult);
}
- reportEvent(env, mCls, "onRttResults", "(I[Landroid/net/wifi/RttManager$RttResult;)V",
- id, rttResults);
+ helper.reportEvent(mCls, "onRttResults", "(I[Landroid/net/wifi/RttManager$RttResult;)V",
+ id, rttResults.get());
}
const int MaxRttConfigs = 16;
@@ -953,20 +1195,22 @@
static jboolean android_net_wifi_requestRange(
JNIEnv *env, jclass cls, jint iface, jint id, jobject params) {
- wifi_interface_handle handle = getIfaceHandle(env, cls, iface);
+ JNIHelper helper(env);
+
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
ALOGD("sending rtt request [%d] = %p", id, handle);
wifi_rtt_config configs[MaxRttConfigs];
memset(&configs, 0, sizeof(configs));
- int len = env->GetArrayLength((jobjectArray)params);
+ int len = helper.getArrayLength((jobjectArray)params);
if (len > MaxRttConfigs) {
return false;
}
for (int i = 0; i < len; i++) {
- jobject param = env->GetObjectArrayElement((jobjectArray)params, i);
+ JNIObject<jobject> param = helper.getObjectArrayElement((jobjectArray)params, i);
if (param == NULL) {
ALOGD("could not get element %d", i);
continue;
@@ -975,37 +1219,62 @@
wifi_rtt_config &config = configs[i];
parseMacAddress(env, param, config.addr);
- config.type = (wifi_rtt_type)getIntField(env, param, "requestType");
- config.peer = (wifi_peer_type)getIntField(env, param, "deviceType");
- config.channel.center_freq = getIntField(env, param, "frequency");
- config.channel.width = (wifi_channel_width)getIntField(env, param, "channelWidth");
- config.num_samples_per_measurement = getIntField(env, param, "num_samples");
- config.num_retries_per_measurement = getIntField(env, param, "num_retries");
+ config.type = (wifi_rtt_type)helper.getIntField(param, "requestType");
+ config.peer = (rtt_peer_type)helper.getIntField(param, "deviceType");
+ config.channel.center_freq = helper.getIntField(param, "frequency");
+ config.channel.width = (wifi_channel_width) helper.getIntField(param, "channelWidth");
+ config.channel.center_freq0 = helper.getIntField(param, "centerFreq0");
+ config.channel.center_freq1 = helper.getIntField(param, "centerFreq1");
+
+ config.num_burst = helper.getIntField(param, "numberBurst");
+ config.burst_period = (unsigned) helper.getIntField(param, "interval");
+ config.num_frames_per_burst = (unsigned) helper.getIntField(param, "numSamplesPerBurst");
+ config.num_retries_per_rtt_frame = (unsigned) helper.getIntField(param,
+ "numRetriesPerMeasurementFrame");
+ config.num_retries_per_ftmr = (unsigned) helper.getIntField(param, "numRetriesPerFTMR");
+ config.LCI_request = helper.getBoolField(param, "LCIRequest") ? 1 : 0;
+ config.LCR_request = helper.getBoolField(param, "LCRRequest") ? 1 : 0;
+ config.burst_duration = (unsigned) helper.getIntField(param, "burstTimeout");
+ config.preamble = (wifi_rtt_preamble) helper.getIntField(param, "preamble");
+ config.bw = (wifi_rtt_bw) helper.getIntField(param, "bandwidth");
+
+ ALOGD("RTT request destination %d: type is %d, peer is %d, bw is %d, center_freq is %d ", i,
+ config.type,config.peer, config.channel.width, config.channel.center_freq);
+ ALOGD("center_freq0 is %d, center_freq1 is %d, num_burst is %d,interval is %d",
+ config.channel.center_freq0, config.channel.center_freq1, config.num_burst,
+ config.burst_period);
+ ALOGD("frames_per_burst is %d, retries of measurement frame is %d, retries_per_ftmr is %d",
+ config.num_frames_per_burst, config.num_retries_per_rtt_frame,
+ config.num_retries_per_ftmr);
+ ALOGD("LCI_requestis %d, LCR_request is %d, burst_timeout is %d, preamble is %d, bw is %d",
+ config.LCI_request, config.LCR_request, config.burst_duration, config.preamble,
+ config.bw);
}
wifi_rtt_event_handler handler;
handler.on_rtt_results = &onRttResults;
- return wifi_rtt_range_request(id, handle, len, configs, handler) == WIFI_SUCCESS;
+ return hal_fn.wifi_rtt_range_request(id, handle, len, configs, handler) == WIFI_SUCCESS;
}
static jboolean android_net_wifi_cancelRange(
JNIEnv *env, jclass cls, jint iface, jint id, jobject params) {
- wifi_interface_handle handle = getIfaceHandle(env, cls, iface);
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
ALOGD("cancelling rtt request [%d] = %p", id, handle);
mac_addr addrs[MaxRttConfigs];
memset(&addrs, 0, sizeof(addrs));
- int len = env->GetArrayLength((jobjectArray)params);
+ int len = helper.getArrayLength((jobjectArray)params);
if (len > MaxRttConfigs) {
return false;
}
for (int i = 0; i < len; i++) {
- jobject param = env->GetObjectArrayElement((jobjectArray)params, i);
+ JNIObject<jobject> param = helper.getObjectArrayElement(params, i);
if (param == NULL) {
ALOGD("could not get element %d", i);
continue;
@@ -1014,58 +1283,803 @@
parseMacAddress(env, param, addrs[i]);
}
- return wifi_rtt_range_cancel(id, handle, len, addrs) == WIFI_SUCCESS;
+ return hal_fn.wifi_rtt_range_cancel(id, handle, len, addrs) == WIFI_SUCCESS;
}
static jboolean android_net_wifi_setScanningMacOui(JNIEnv *env, jclass cls,
jint iface, jbyteArray param) {
- wifi_interface_handle handle = getIfaceHandle(env, cls, iface);
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
ALOGD("setting scan oui %p", handle);
static const unsigned oui_len = 3; /* OUI is upper 3 bytes of mac_address */
- int len = env->GetArrayLength(param);
+ int len = helper.getArrayLength(param);
if (len != oui_len) {
ALOGE("invalid oui length %d", len);
return false;
}
- jbyte* bytes = env->GetByteArrayElements(param, NULL);
+ ScopedBytesRO paramBytes(env, param);
+ const jbyte* bytes = paramBytes.get();
if (bytes == NULL) {
ALOGE("failed to get array");
return false;
}
- return wifi_set_scanning_mac_oui(handle, (byte *)bytes) == WIFI_SUCCESS;
+ return hal_fn.wifi_set_scanning_mac_oui(handle, (byte *)bytes) == WIFI_SUCCESS;
+}
+
+static jboolean android_net_wifi_is_get_channels_for_band_supported(JNIEnv *env, jclass cls){
+ return (hal_fn.wifi_get_valid_channels == wifi_get_valid_channels_stub);
}
static jintArray android_net_wifi_getValidChannels(JNIEnv *env, jclass cls,
jint iface, jint band) {
- wifi_interface_handle handle = getIfaceHandle(env, cls, iface);
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
ALOGD("getting valid channels %p", handle);
static const int MaxChannels = 64;
wifi_channel channels[64];
int num_channels = 0;
- wifi_error result = wifi_get_valid_channels(handle, band, MaxChannels,
+ wifi_error result = hal_fn.wifi_get_valid_channels(handle, band, MaxChannels,
channels, &num_channels);
if (result == WIFI_SUCCESS) {
- jintArray channelArray = env->NewIntArray(num_channels);
+ JNIObject<jintArray> channelArray = helper.newIntArray(num_channels);
if (channelArray == NULL) {
ALOGE("failed to allocate channel list");
return NULL;
}
- env->SetIntArrayRegion(channelArray, 0, num_channels, channels);
- return channelArray;
+ helper.setIntArrayRegion(channelArray, 0, num_channels, channels);
+ return channelArray.detach();
} else {
ALOGE("failed to get channel list : %d", result);
return NULL;
}
}
+static jboolean android_net_wifi_setDfsFlag(JNIEnv *env, jclass cls, jint iface, jboolean dfs) {
+
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+ ALOGD("setting dfs flag to %s, %p", dfs ? "true" : "false", handle);
+
+ u32 nodfs = dfs ? 0 : 1;
+ wifi_error result = hal_fn.wifi_set_nodfs_flag(handle, nodfs);
+ return result == WIFI_SUCCESS;
+}
+
+static jobject android_net_wifi_get_rtt_capabilities(JNIEnv *env, jclass cls, jint iface) {
+
+ JNIHelper helper(env);
+ wifi_rtt_capabilities rtt_capabilities;
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+ wifi_error ret = hal_fn.wifi_get_rtt_capabilities(handle, &rtt_capabilities);
+
+ if(WIFI_SUCCESS == ret) {
+ JNIObject<jobject> capabilities = helper.createObject(
+ "android/net/wifi/RttManager$RttCapabilities");
+ helper.setBooleanField(capabilities, "oneSidedRttSupported",
+ rtt_capabilities.rtt_one_sided_supported == 1);
+ helper.setBooleanField(capabilities, "twoSided11McRttSupported",
+ rtt_capabilities.rtt_ftm_supported == 1);
+ helper.setBooleanField(capabilities, "lciSupported",
+ rtt_capabilities.lci_support);
+ helper.setBooleanField(capabilities, "lcrSupported",
+ rtt_capabilities.lcr_support);
+ helper.setIntField(capabilities, "preambleSupported",
+ rtt_capabilities.preamble_support);
+ helper.setIntField(capabilities, "bwSupported",
+ rtt_capabilities.bw_support);
+ ALOGD("One side RTT is: %s", rtt_capabilities.rtt_one_sided_supported ==1 ? "support" :
+ "not support");
+ ALOGD("Two side RTT is: %s", rtt_capabilities.rtt_ftm_supported == 1 ? "support" :
+ "not support");
+ ALOGD("LCR is: %s", rtt_capabilities.lcr_support == 1 ? "support" : "not support");
+
+ ALOGD("LCI is: %s", rtt_capabilities.lci_support == 1 ? "support" : "not support");
+
+ ALOGD("Support Preamble is : %d support BW is %d", rtt_capabilities.preamble_support,
+ rtt_capabilities.bw_support);
+ return capabilities.detach();
+ } else {
+ return NULL;
+ }
+}
+
+static jboolean android_net_wifi_set_Country_Code_Hal(JNIEnv *env,jclass cls, jint iface,
+ jstring country_code) {
+
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+
+ ScopedUtfChars chars(env, country_code);
+ const char *country = chars.c_str();
+
+ ALOGD("set country code: %s", country);
+ wifi_error res = hal_fn.wifi_set_country_code(handle, country);
+ return res == WIFI_SUCCESS;
+}
+
+static jboolean android_net_wifi_enable_disable_tdls(JNIEnv *env,jclass cls, jint iface,
+ jboolean enable, jstring addr) {
+
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+
+ mac_addr address;
+ parseMacAddress(env, addr, address);
+ wifi_tdls_handler tdls_handler;
+ //tdls_handler.on_tdls_state_changed = &on_tdls_state_changed;
+
+ if(enable) {
+ return (hal_fn.wifi_enable_tdls(handle, address, NULL, tdls_handler) == WIFI_SUCCESS);
+ } else {
+ return (hal_fn.wifi_disable_tdls(handle, address) == WIFI_SUCCESS);
+ }
+}
+
+static void on_tdls_state_changed(mac_addr addr, wifi_tdls_status status) {
+
+ JNIHelper helper(mVM);
+
+ ALOGD("on_tdls_state_changed is called: vm = %p, obj = %p", mVM, mCls);
+
+ char mac[32];
+ sprintf(mac, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4],
+ addr[5]);
+
+ JNIObject<jstring> mac_address = helper.newStringUTF(mac);
+ helper.reportEvent(mCls, "onTdlsStatus", "(Ljava/lang/StringII;)V",
+ mac_address.get(), status.state, status.reason);
+
+}
+
+static jobject android_net_wifi_get_tdls_status(JNIEnv *env,jclass cls, jint iface,jstring addr) {
+
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+
+ mac_addr address;
+ parseMacAddress(env, addr, address);
+
+ wifi_tdls_status status;
+
+ wifi_error ret;
+ ret = hal_fn.wifi_get_tdls_status(handle, address, &status );
+
+ if (ret != WIFI_SUCCESS) {
+ return NULL;
+ } else {
+ JNIObject<jobject> tdls_status = helper.createObject(
+ "com/android/server/wifi/WifiNative$TdlsStatus");
+ helper.setIntField(tdls_status, "channel", status.channel);
+ helper.setIntField(tdls_status, "global_operating_class", status.global_operating_class);
+ helper.setIntField(tdls_status, "state", status.state);
+ helper.setIntField(tdls_status, "reason", status.reason);
+ return tdls_status.detach();
+ }
+}
+
+static jobject android_net_wifi_get_tdls_capabilities(JNIEnv *env, jclass cls, jint iface) {
+
+ JNIHelper helper(env);
+ wifi_tdls_capabilities tdls_capabilities;
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+ wifi_error ret = hal_fn.wifi_get_tdls_capabilities(handle, &tdls_capabilities);
+
+ if (WIFI_SUCCESS == ret) {
+ JNIObject<jobject> capabilities = helper.createObject(
+ "com/android/server/wifi/WifiNative$TdlsCapabilities");
+ helper.setIntField(capabilities, "maxConcurrentTdlsSessionNumber",
+ tdls_capabilities.max_concurrent_tdls_session_num);
+ helper.setBooleanField(capabilities, "isGlobalTdlsSupported",
+ tdls_capabilities.is_global_tdls_supported == 1);
+ helper.setBooleanField(capabilities, "isPerMacTdlsSupported",
+ tdls_capabilities.is_per_mac_tdls_supported == 1);
+ helper.setBooleanField(capabilities, "isOffChannelTdlsSupported",
+ tdls_capabilities.is_off_channel_tdls_supported);
+
+ ALOGD("TDLS Max Concurrent Tdls Session Number is: %d",
+ tdls_capabilities.max_concurrent_tdls_session_num);
+ ALOGD("Global Tdls is: %s", tdls_capabilities.is_global_tdls_supported == 1 ? "support" :
+ "not support");
+ ALOGD("Per Mac Tdls is: %s", tdls_capabilities.is_per_mac_tdls_supported == 1 ? "support" :
+ "not support");
+ ALOGD("Off Channel Tdls is: %s", tdls_capabilities.is_off_channel_tdls_supported == 1 ?
+ "support" : "not support");
+
+ return capabilities.detach();
+ } else {
+ return NULL;
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Debug framework
+// ----------------------------------------------------------------------------
+static jint android_net_wifi_get_supported_logger_feature(JNIEnv *env, jclass cls, jint iface){
+ //Not implemented yet
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+ return -1;
+}
+
+static jobject android_net_wifi_get_driver_version(JNIEnv *env, jclass cls, jint iface) {
+ //Need to be fixed. The memory should be allocated from lower layer
+ //char *buffer = NULL;
+ JNIHelper helper(env);
+ int buffer_length = 256;
+ char *buffer = (char *)malloc(buffer_length);
+ if (!buffer) return NULL;
+ memset(buffer, 0, buffer_length);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+
+ ALOGD("android_net_wifi_get_driver_version = %p", handle);
+
+ if (handle == 0) {
+ return NULL;
+ }
+
+ wifi_error result = hal_fn.wifi_get_driver_version(handle, buffer, buffer_length);
+
+ if (result == WIFI_SUCCESS) {
+ ALOGD("buffer is %p, length is %d", buffer, buffer_length);
+ JNIObject<jstring> driver_version = helper.newStringUTF(buffer);
+ free(buffer);
+ return driver_version.detach();
+ } else {
+ ALOGD("Fail to get driver version");
+ free(buffer);
+ return NULL;
+ }
+}
+
+static jobject android_net_wifi_get_firmware_version(JNIEnv *env, jclass cls, jint iface) {
+
+ //char *buffer = NULL;
+ JNIHelper helper(env);
+ int buffer_length = 256;
+ char *buffer = (char *)malloc(buffer_length);
+ if (!buffer) return NULL;
+ memset(buffer, 0, buffer_length);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+
+ ALOGD("android_net_wifi_get_firmware_version = %p", handle);
+
+ if (handle == 0) {
+ return NULL;
+ }
+
+ wifi_error result = hal_fn.wifi_get_firmware_version(handle, buffer, buffer_length);
+
+ if (result == WIFI_SUCCESS) {
+ ALOGD("buffer is %p, length is %d", buffer, buffer_length);
+ JNIObject<jstring> firmware_version = helper.newStringUTF(buffer);
+ free(buffer);
+ return firmware_version.detach();
+ } else {
+ ALOGD("Fail to get Firmware version");
+ free(buffer);
+ return NULL;
+ }
+}
+
+static jobject android_net_wifi_get_ring_buffer_status (JNIEnv *env, jclass cls, jint iface) {
+
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+
+ ALOGD("android_net_wifi_get_ring_buffer_status = %p", handle);
+
+ if (handle == 0) {
+ return NULL;
+ }
+
+ //wifi_ring_buffer_status *status = NULL;
+ u32 num_rings = 10;
+ wifi_ring_buffer_status *status =
+ (wifi_ring_buffer_status *)malloc(sizeof(wifi_ring_buffer_status) * num_rings);
+ if (!status) return NULL;
+ memset(status, 0, sizeof(wifi_ring_buffer_status) * num_rings);
+ wifi_error result = hal_fn.wifi_get_ring_buffers_status(handle, &num_rings, status);
+ if (result == WIFI_SUCCESS) {
+ ALOGD("status is %p, number is %d", status, num_rings);
+
+ JNIObject<jobjectArray> ringBuffersStatus = helper.newObjectArray(
+ num_rings, "com/android/server/wifi/WifiNative$RingBufferStatus", NULL);
+
+ wifi_ring_buffer_status *tmp = status;
+
+ for(u32 i = 0; i < num_rings; i++, tmp++) {
+
+ JNIObject<jobject> ringStatus = helper.createObject(
+ "com/android/server/wifi/WifiNative$RingBufferStatus");
+
+ if (ringStatus == NULL) {
+ ALOGE("Error in creating ringBufferStatus");
+ free(status);
+ return NULL;
+ }
+
+ char name[32];
+ for(int j = 0; j < 32; j++) {
+ name[j] = tmp->name[j];
+ }
+
+ helper.setStringField(ringStatus, "name", name);
+ helper.setIntField(ringStatus, "flag", tmp->flags);
+ helper.setIntField(ringStatus, "ringBufferId", tmp->ring_id);
+ helper.setIntField(ringStatus, "ringBufferByteSize", tmp->ring_buffer_byte_size);
+ helper.setIntField(ringStatus, "verboseLevel", tmp->verbose_level);
+ helper.setIntField(ringStatus, "writtenBytes", tmp->written_bytes);
+ helper.setIntField(ringStatus, "readBytes", tmp->read_bytes);
+ helper.setIntField(ringStatus, "writtenRecords", tmp->written_records);
+
+ helper.setObjectArrayElement(ringBuffersStatus, i, ringStatus);
+ }
+
+ free(status);
+ return ringBuffersStatus.detach();
+ } else {
+ free(status);
+ return NULL;
+ }
+}
+
+static void on_ring_buffer_data(char *ring_name, char *buffer, int buffer_size,
+ wifi_ring_buffer_status *status) {
+
+ if (!ring_name || !buffer || !status ||
+ (unsigned int)buffer_size <= sizeof(wifi_ring_buffer_entry)) {
+ ALOGE("Error input for on_ring_buffer_data!");
+ return;
+ }
+
+
+ JNIHelper helper(mVM);
+ /* ALOGD("on_ring_buffer_data called, vm = %p, obj = %p, env = %p buffer size = %d", mVM,
+ mCls, env, buffer_size); */
+
+ JNIObject<jobject> ringStatus = helper.createObject(
+ "com/android/server/wifi/WifiNative$RingBufferStatus");
+ if (status == NULL) {
+ ALOGE("Error in creating ringBufferStatus");
+ return;
+ }
+
+ helper.setStringField(ringStatus, "name", ring_name);
+ helper.setIntField(ringStatus, "flag", status->flags);
+ helper.setIntField(ringStatus, "ringBufferId", status->ring_id);
+ helper.setIntField(ringStatus, "ringBufferByteSize", status->ring_buffer_byte_size);
+ helper.setIntField(ringStatus, "verboseLevel", status->verbose_level);
+ helper.setIntField(ringStatus, "writtenBytes", status->written_bytes);
+ helper.setIntField(ringStatus, "readBytes", status->read_bytes);
+ helper.setIntField(ringStatus, "writtenRecords", status->written_records);
+
+ JNIObject<jbyteArray> bytes = helper.newByteArray(buffer_size);
+ helper.setByteArrayRegion(bytes, 0, buffer_size, (jbyte*)buffer);
+
+ helper.reportEvent(mCls,"onRingBufferData",
+ "(Lcom/android/server/wifi/WifiNative$RingBufferStatus;[B)V",
+ ringStatus.get(), bytes.get());
+}
+
+static void on_alert_data(wifi_request_id id, char *buffer, int buffer_size, int err_code){
+
+ JNIHelper helper(mVM);
+ ALOGD("on_alert_data called, vm = %p, obj = %p, buffer_size = %d, error code = %d"
+ , mVM, mCls, buffer_size, err_code);
+
+ if (buffer_size > 0) {
+ JNIObject<jbyteArray> records = helper.newByteArray(buffer_size);
+ jbyte *bytes = (jbyte *) buffer;
+ helper.setByteArrayRegion(records, 0,buffer_size, bytes);
+ helper.reportEvent(mCls,"onWifiAlert","([BI)V", records.get(), err_code);
+ } else {
+ helper.reportEvent(mCls,"onWifiAlert","([BI)V", NULL, err_code);
+ }
+}
+
+
+static jboolean android_net_wifi_start_logging_ring_buffer(JNIEnv *env, jclass cls, jint iface,
+ jint verbose_level,jint flags, jint max_interval,jint min_data_size, jstring ring_name) {
+
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+
+ ALOGD("android_net_wifi_start_logging_ring_buffer = %p", handle);
+
+ if (handle == 0) {
+ return false;
+ }
+
+ ScopedUtfChars chars(env, ring_name);
+ const char* ring_name_const_char = chars.c_str();
+ int ret = hal_fn.wifi_start_logging(handle, verbose_level,
+ flags, max_interval, min_data_size, const_cast<char *>(ring_name_const_char));
+
+ if (ret != WIFI_SUCCESS) {
+ ALOGE("Fail to start logging for ring %s", ring_name_const_char);
+ } else {
+ ALOGD("start logging for ring %s", ring_name_const_char);
+ }
+
+ return ret == WIFI_SUCCESS;
+}
+
+static jboolean android_net_wifi_get_ring_buffer_data(JNIEnv *env, jclass cls, jint iface,
+ jstring ring_name) {
+
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+ // ALOGD("android_net_wifi_get_ring_buffer_data = %p", handle);
+
+ ScopedUtfChars chars(env, ring_name);
+ const char* ring_name_const_char = chars.c_str();
+ int result = hal_fn.wifi_get_ring_data(handle, const_cast<char *>(ring_name_const_char));
+ return result == WIFI_SUCCESS;
+}
+
+
+void on_firmware_memory_dump(char *buffer, int buffer_size) {
+
+ JNIHelper helper(mVM);
+ /* ALOGD("on_firmware_memory_dump called, vm = %p, obj = %p, env = %p buffer_size = %d"
+ , mVM, mCls, env, buffer_size); */
+
+ if (buffer_size > 0) {
+ JNIObject<jbyteArray> dump = helper.newByteArray(buffer_size);
+ jbyte *bytes = (jbyte *) (buffer);
+ helper.setByteArrayRegion(dump, 0, buffer_size, bytes);
+ helper.reportEvent(mCls,"onWifiFwMemoryAvailable","([B)V", dump.get());
+ }
+}
+
+static jboolean android_net_wifi_get_fw_memory_dump(JNIEnv *env, jclass cls, jint iface){
+
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+ // ALOGD("android_net_wifi_get_fw_memory_dump = %p", handle);
+
+ if (handle == NULL) {
+ ALOGE("Can not get wifi_interface_handle");
+ return false;
+ }
+
+ wifi_firmware_memory_dump_handler fw_dump_handle;
+ fw_dump_handle.on_firmware_memory_dump = on_firmware_memory_dump;
+ int result = hal_fn.wifi_get_firmware_memory_dump(handle, fw_dump_handle);
+ return result == WIFI_SUCCESS;
+
+}
+
+static jboolean android_net_wifi_set_log_handler(JNIEnv *env, jclass cls, jint iface, jint id) {
+
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+ ALOGD("android_net_wifi_set_log_handler = %p", handle);
+
+ //initialize the handler on first time
+ wifi_ring_buffer_data_handler handler;
+ handler.on_ring_buffer_data = &on_ring_buffer_data;
+ int result = hal_fn.wifi_set_log_handler(id, handle, handler);
+ if (result != WIFI_SUCCESS) {
+ ALOGE("Fail to set logging handler");
+ return false;
+ }
+
+ //set alter handler This will start alert too
+ wifi_alert_handler alert_handler;
+ alert_handler.on_alert = &on_alert_data;
+ result = hal_fn.wifi_set_alert_handler(id, handle, alert_handler);
+ if (result != WIFI_SUCCESS) {
+ ALOGE(" Fail to set alert handler");
+ return false;
+ }
+
+ return true;
+}
+
+static jboolean android_net_wifi_reset_log_handler(JNIEnv *env, jclass cls, jint iface, jint id) {
+
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+
+ //reset alter handler
+ ALOGD("android_net_wifi_reset_alert_handler = %p", handle);
+ int result = hal_fn.wifi_reset_alert_handler(id, handle);
+ if (result != WIFI_SUCCESS) {
+ ALOGE(" Fail to reset alert handler");
+ return false;
+ }
+
+ //reset log handler
+ ALOGD("android_net_wifi_reset_log_handler = %p", handle);
+ result = hal_fn.wifi_reset_log_handler(id, handle);
+ if (result != WIFI_SUCCESS) {
+ ALOGE("Fail to reset logging handler");
+ return false;
+ }
+
+ return true;
+}
+
+// ----------------------------------------------------------------------------
+// ePno framework
+// ----------------------------------------------------------------------------
+
+
+static void onPnoNetworkFound(wifi_request_id id,
+ unsigned num_results, wifi_scan_result *results) {
+
+ JNIHelper helper(mVM);
+
+ ALOGD("onPnoNetworkFound called, vm = %p, obj = %p, num_results %u", mVM, mCls, num_results);
+
+ if (results == 0 || num_results == 0) {
+ ALOGE("onPnoNetworkFound: Error no results");
+ return;
+ }
+
+ jbyte *bytes;
+ JNIObject<jobjectArray> scanResults(helper, NULL);
+ //jbyteArray elements;
+
+ for (unsigned i=0; i<num_results; i++) {
+
+ JNIObject<jobject> scanResult = createScanResult(helper, &results[i]);
+ if (i == 0) {
+ scanResults = helper.newObjectArray(
+ num_results, "android/net/wifi/ScanResult", scanResult);
+ if (scanResults == 0) {
+ ALOGD("cant allocate array");
+ } else {
+ ALOGD("allocated array %u", helper.getArrayLength(scanResults));
+ }
+ } else {
+ helper.setObjectArrayElement(scanResults, i, scanResult);
+ }
+
+ ALOGD("Scan result with ie length %d, i %u, <%s> rssi=%d %02x:%02x:%02x:%02x:%02x:%02x",
+ results->ie_length, i, results[i].ssid, results[i].rssi, results[i].bssid[0],
+ results[i].bssid[1],results[i].bssid[2], results[i].bssid[3], results[i].bssid[4],
+ results[i].bssid[5]);
+
+ /*elements = helper.newByteArray(results->ie_length);
+ if (elements == NULL) {
+ ALOGE("Error in allocating array");
+ return;
+ }*/
+
+ //ALOGD("onPnoNetworkFound: Setting byte array");
+
+ //bytes = (jbyte *)&(results->ie_data[0]);
+ //helper.setByteArrayRegion(elements, 0, results->ie_length, bytes);
+
+ //ALOGD("onPnoNetworkFound: Returning result");
+ }
+
+
+ ALOGD("calling report");
+
+ helper.reportEvent(mCls, "onPnoNetworkFound", "(I[Landroid/net/wifi/ScanResult;)V", id,
+ scanResults.get());
+ ALOGD("free ref");
+}
+
+static jboolean android_net_wifi_setPnoListNative(
+ JNIEnv *env, jclass cls, jint iface, jint id, jobject list) {
+
+ JNIHelper helper(env);
+ wifi_epno_handler handler;
+ handler.on_network_found = &onPnoNetworkFound;
+
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+ ALOGD("configure ePno list request [%d] = %p", id, handle);
+
+ if (list == NULL) {
+ // stop pno
+ int result = hal_fn.wifi_set_epno_list(id, handle, 0, NULL, handler);
+ ALOGE(" setPnoListNative: STOP result = %d", result);
+ return result >= 0;
+ }
+
+ wifi_epno_network net_list[MAX_PNO_SSID];
+ memset(&net_list, 0, sizeof(net_list));
+
+ size_t len = helper.getArrayLength((jobjectArray)list);
+ if (len > (size_t)MAX_PNO_SSID) {
+ return false;
+ }
+
+ for (unsigned int i = 0; i < len; i++) {
+
+ JNIObject<jobject> pno_net = helper.getObjectArrayElement((jobjectArray)list, i);
+ if (pno_net == NULL) {
+ ALOGD("setPnoListNative: could not get element %d", i);
+ continue;
+ }
+
+ JNIObject<jstring> sssid = helper.getStringField(pno_net, "SSID");
+ if (sssid == NULL) {
+ ALOGE("Error setPnoListNative: getting ssid field");
+ return false;
+ }
+
+ ScopedUtfChars chars(env, (jstring)sssid.get());
+ const char *ssid = chars.c_str();
+ if (ssid == NULL) {
+ ALOGE("Error setPnoListNative: getting ssid");
+ return false;
+ }
+ int ssid_len = strnlen((const char*)ssid, 33);
+ if (ssid_len > 32) {
+ ALOGE("Error setPnoListNative: long ssid %u", strnlen((const char*)ssid, 256));
+ return false;
+ }
+
+ if (ssid_len > 1 && ssid[0] == '"' && ssid[ssid_len-1])
+ {
+ // strip leading and trailing '"'
+ ssid++;
+ ssid_len-=2;
+ }
+ if (ssid_len == 0) {
+ ALOGE("Error setPnoListNative: zero length ssid, skip it");
+ continue;
+ }
+ memcpy(net_list[i].ssid, ssid, ssid_len);
+
+ int rssit = helper.getIntField(pno_net, "rssi_threshold");
+ net_list[i].rssi_threshold = (byte)rssit;
+ int a = helper.getIntField(pno_net, "auth");
+ net_list[i].auth_bit_field = a;
+ int f = helper.getIntField(pno_net, "flags");
+ net_list[i].flags = f;
+ ALOGE(" setPnoListNative: idx %u rssi %d/%d auth %x/%x flags %x/%x [%s]", i,
+ (signed)net_list[i].rssi_threshold, net_list[i].rssi_threshold,
+ net_list[i].auth_bit_field, a, net_list[i].flags, f, net_list[i].ssid);
+ }
+
+ int result = hal_fn.wifi_set_epno_list(id, handle, len, net_list, handler);
+ ALOGE(" setPnoListNative: result %d", result);
+
+ return result >= 0;
+}
+
+static jboolean android_net_wifi_setLazyRoam(
+ JNIEnv *env, jclass cls, jint iface, jint id, jboolean enabled, jobject roam_param) {
+
+ JNIHelper helper(env);
+ wifi_error status = WIFI_SUCCESS;
+ wifi_roam_params params;
+ memset(¶ms, 0, sizeof(params));
+
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+ ALOGD("configure lazy roam request [%d] = %p", id, handle);
+
+ if (roam_param != NULL) {
+ params.A_band_boost_threshold = helper.getIntField(roam_param, "A_band_boost_threshold");
+ params.A_band_penalty_threshold = helper.getIntField(roam_param, "A_band_penalty_threshold");
+ params.A_band_boost_factor = helper.getIntField(roam_param, "A_band_boost_factor");
+ params.A_band_penalty_factor = helper.getIntField(roam_param, "A_band_penalty_factor");
+ params.A_band_max_boost = helper.getIntField(roam_param, "A_band_max_boost");
+ params.lazy_roam_hysteresis = helper.getIntField(roam_param, "lazy_roam_hysteresis");
+ params.alert_roam_rssi_trigger = helper.getIntField(roam_param, "alert_roam_rssi_trigger");
+ status = hal_fn.wifi_set_gscan_roam_params(id, handle, ¶ms);
+ }
+ ALOGE("android_net_wifi_setLazyRoam configured params status=%d\n", status);
+
+ if (status >= 0) {
+ int doEnable = enabled ? 1 : 0;
+ status = hal_fn.wifi_enable_lazy_roam(id, handle, doEnable);
+ ALOGE("android_net_wifi_setLazyRoam enabled roam status=%d\n", status);
+ }
+ return status >= 0;
+}
+
+static jboolean android_net_wifi_setBssidBlacklist(
+ JNIEnv *env, jclass cls, jint iface, jint id, jobject list) {
+
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+ ALOGD("configure BSSID black list request [%d] = %p", id, handle);
+
+ wifi_bssid_params params;
+ memset(¶ms, 0, sizeof(params));
+
+ if (list != NULL) {
+ size_t len = helper.getArrayLength((jobjectArray)list);
+ if (len > (size_t)MAX_BLACKLIST_BSSID) {
+ return false;
+ }
+ for (unsigned int i = 0; i < len; i++) {
+
+ JNIObject<jobject> jbssid = helper.getObjectArrayElement(list, i);
+ if (jbssid == NULL) {
+ ALOGD("configure BSSID blacklist: could not get element %d", i);
+ continue;
+ }
+
+ ScopedUtfChars chars(env, (jstring)jbssid.get());
+ const char *bssid = chars.c_str();
+ if (bssid == NULL) {
+ ALOGE("Error getting bssid");
+ return false;
+ }
+
+ mac_addr addr;
+ parseMacAddress(bssid, addr);
+ memcpy(params.bssids[i], 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]);
+
+ ALOGD("BSSID blacklist: added bssid %s", bssidOut);
+
+ params.num_bssid++;
+ }
+ }
+
+ ALOGD("Added %d bssids", params.num_bssid);
+ return hal_fn.wifi_set_bssid_blacklist(id, handle, params) == WIFI_SUCCESS;
+}
+
+static jboolean android_net_wifi_setSsidWhitelist(
+ JNIEnv *env, jclass cls, jint iface, jint id, jobject list) {
+
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
+ ALOGD("configure SSID white list request [%d] = %p", id, handle);
+ wifi_ssid *ssids = NULL;
+ int num_ssids = 0;
+ if (list != NULL) {
+ size_t len = helper.getArrayLength((jobjectArray)list);
+ if (len > 0) {
+ ssids = (wifi_ssid *)malloc(len * sizeof (wifi_ssid));
+ if (!ssids) return false;
+ memset(ssids, 0, len * sizeof (wifi_ssid));
+ for (unsigned int i = 0; i < len; i++) {
+
+ JNIObject<jobject> jssid = helper.getObjectArrayElement(list, i);
+ if (jssid == NULL) {
+ ALOGD("configure SSID whitelist: could not get element %d", i);
+ free(ssids);
+ return false;
+ }
+
+ ScopedUtfChars chars(env, (jstring)jssid.get());
+ const char *utf = chars.c_str();
+ if (utf == NULL) {
+ ALOGE("Error getting sssid");
+ free(ssids);
+ return false;
+ }
+
+ int slen = strnlen(utf, 33);
+ if (slen <= 0 || slen > 32) {
+ ALOGE("Error wrong ssid length %d", slen);
+ free(ssids);
+ return false;
+ }
+
+ memcpy(ssids[i].ssid, utf, slen);
+ num_ssids++;
+ ALOGD("SSID white list: added ssid %s", utf);
+ }
+ }
+ }
+
+ ALOGD("android_net_wifi_setSsidWhitelist Added %d sssids", num_ssids);
+ return hal_fn.wifi_set_ssid_white_list(id, handle, num_ssids, ssids) == WIFI_SUCCESS;
+}
+
// ----------------------------------------------------------------------------
/*
@@ -1097,7 +2111,7 @@
{ "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;",
+ { "getScanResultsNative", "(IZ)[Landroid/net/wifi/WifiScanner$ScanData;",
(void *) android_net_wifi_getScanResults},
{ "setHotlistNative", "(IILandroid/net/wifi/WifiScanner$HotlistSettings;)Z",
(void*) android_net_wifi_setHotlist},
@@ -1108,14 +2122,53 @@
(void*) android_net_wifi_untrackSignificantWifiChange},
{ "getWifiLinkLayerStatsNative", "(I)Landroid/net/wifi/WifiLinkLayerStats;",
(void*) android_net_wifi_getLinkLayerStats},
+ { "setWifiLinkLayerStatsNative", "(II)V",
+ (void*) android_net_wifi_setLinkLayerStats},
{ "getSupportedFeatureSetNative", "(I)I",
(void*) android_net_wifi_getSupportedFeatures},
{ "requestRangeNative", "(II[Landroid/net/wifi/RttManager$RttParams;)Z",
(void*) android_net_wifi_requestRange},
{ "cancelRangeRequestNative", "(II[Landroid/net/wifi/RttManager$RttParams;)Z",
(void*) android_net_wifi_cancelRange},
- { "setScanningMacOuiNative", "(I[B)Z", (void*) android_net_wifi_setScanningMacOui},
- { "getChannelsForBandNative", "(II)[I", (void*) android_net_wifi_getValidChannels}
+ { "setScanningMacOuiNative", "(I[B)Z", (void*) android_net_wifi_setScanningMacOui},
+ { "getChannelsForBandNative", "(II)[I", (void*) android_net_wifi_getValidChannels},
+ { "setDfsFlagNative", "(IZ)Z", (void*) android_net_wifi_setDfsFlag},
+ { "toggleInterfaceNative", "(I)Z", (void*) android_net_wifi_toggle_interface},
+ { "getRttCapabilitiesNative", "(I)Landroid/net/wifi/RttManager$RttCapabilities;",
+ (void*) android_net_wifi_get_rtt_capabilities},
+ {"setCountryCodeHalNative", "(ILjava/lang/String;)Z",
+ (void*) android_net_wifi_set_Country_Code_Hal},
+ { "setPnoListNative", "(II[Lcom/android/server/wifi/WifiNative$WifiPnoNetwork;)Z",
+ (void*) android_net_wifi_setPnoListNative},
+ {"enableDisableTdlsNative", "(IZLjava/lang/String;)Z",
+ (void*) android_net_wifi_enable_disable_tdls},
+ {"getTdlsStatusNative", "(ILjava/lang/String;)Lcom/android/server/wifi/WifiNative$TdlsStatus;",
+ (void*) android_net_wifi_get_tdls_status},
+ {"getTdlsCapabilitiesNative", "(I)Lcom/android/server/wifi/WifiNative$TdlsCapabilities;",
+ (void*) android_net_wifi_get_tdls_capabilities},
+ {"getSupportedLoggerFeatureSetNative","(I)I",
+ (void*) android_net_wifi_get_supported_logger_feature},
+ {"getDriverVersionNative", "(I)Ljava/lang/String;",
+ (void*) android_net_wifi_get_driver_version},
+ {"getFirmwareVersionNative", "(I)Ljava/lang/String;",
+ (void*) android_net_wifi_get_firmware_version},
+ {"getRingBufferStatusNative", "(I)[Lcom/android/server/wifi/WifiNative$RingBufferStatus;",
+ (void*) android_net_wifi_get_ring_buffer_status},
+ {"startLoggingRingBufferNative", "(IIIIILjava/lang/String;)Z",
+ (void*) android_net_wifi_start_logging_ring_buffer},
+ {"getRingBufferDataNative", "(ILjava/lang/String;)Z",
+ (void*) android_net_wifi_get_ring_buffer_data},
+ {"getFwMemoryDumpNative","(I)Z", (void*) android_net_wifi_get_fw_memory_dump},
+ { "setLazyRoamNative", "(IIZLcom/android/server/wifi/WifiNative$WifiLazyRoamParams;)Z",
+ (void*) android_net_wifi_setLazyRoam},
+ { "setBssidBlacklistNative", "(II[Ljava/lang/String;)Z",
+ (void*)android_net_wifi_setBssidBlacklist},
+ { "setSsidWhitelistNative", "(II[Ljava/lang/String;)Z",
+ (void*)android_net_wifi_setSsidWhitelist},
+ {"setLoggingEventHandlerNative", "(II)Z", (void *) android_net_wifi_set_log_handler},
+ {"resetLogHandlerNative", "(II)Z", (void *) android_net_wifi_reset_log_handler},
+ {"isGetChannelsForBandSupportedNative", "()Z",
+ (void*)android_net_wifi_is_get_channels_for_band_supported}
};
int register_android_net_wifi_WifiNative(JNIEnv* env) {
diff --git a/service/jni/jni_helper.cpp b/service/jni/jni_helper.cpp
index 8d5b520..7320961 100644
--- a/service/jni/jni_helper.cpp
+++ b/service/jni/jni_helper.cpp
@@ -31,393 +31,595 @@
/* JNI Helpers for wifi_hal implementation */
-void throwException( JNIEnv *env, const char *message, int line )
+JNIHelper::JNIHelper(JavaVM *vm)
+{
+ vm->AttachCurrentThread(&mEnv, NULL);
+ mVM = vm;
+}
+
+JNIHelper::JNIHelper(JNIEnv *env)
+{
+ mVM = NULL;
+ mEnv = env;
+}
+
+JNIHelper::~JNIHelper()
+{
+ if (mVM != NULL) {
+ // mVM->DetachCurrentThread(); /* 'attempting to detach while still running code' */
+ mVM = NULL; /* not really required; but may help debugging */
+ mEnv = NULL; /* not really required; but may help debugging */
+ }
+}
+
+jobject JNIHelper::newGlobalRef(jobject obj) {
+ return mEnv->NewGlobalRef(obj);
+}
+
+void JNIHelper::deleteGlobalRef(jobject obj) {
+ mEnv->DeleteGlobalRef(obj);
+}
+
+jobject JNIHelper::newLocalRef(jobject obj) {
+ return mEnv->NewLocalRef(obj);
+}
+
+void JNIHelper::deleteLocalRef(jobject obj) {
+ mEnv->DeleteLocalRef(obj);
+}
+
+void JNIHelper::throwException(const char *message, int line)
{
ALOGE("error at line %d: %s", line, message);
const char *className = "java/lang/Exception";
- jclass exClass = (env)->FindClass(className );
+ jclass exClass = mEnv->FindClass(className );
if ( exClass == NULL ) {
ALOGE("Could not find exception class to throw error");
ALOGE("error at line %d: %s", line, message);
return;
}
- (env)->ThrowNew(exClass, message);
+ mEnv->ThrowNew(exClass, message);
}
-jboolean getBoolField(JNIEnv *env, jobject obj, const char *name)
+jboolean JNIHelper::getBoolField(jobject obj, const char *name)
{
- jclass cls = (env)->GetObjectClass(obj);
- jfieldID field = (env)->GetFieldID(cls, name, "Z");
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
+ jfieldID field = mEnv->GetFieldID(cls, name, "Z");
if (field == 0) {
- THROW(env, "Error in accessing field");
+ THROW(*this, "Error in accessing field");
return 0;
}
- jboolean value = (env)->GetBooleanField(obj, field);
- env->DeleteLocalRef(cls);
- return value;
+ return mEnv->GetBooleanField(obj, field);
}
-jint getIntField(JNIEnv *env, jobject obj, const char *name)
+jint JNIHelper::getIntField(jobject obj, const char *name)
{
- jclass cls = (env)->GetObjectClass(obj);
- jfieldID field = (env)->GetFieldID(cls, name, "I");
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
+ jfieldID field = mEnv->GetFieldID(cls, name, "I");
if (field == 0) {
- THROW(env, "Error in accessing field");
+ THROW(*this, "Error in accessing field");
return 0;
}
- jint value = (env)->GetIntField(obj, field);
- env->DeleteLocalRef(cls);
- return value;
+ return mEnv->GetIntField(obj, field);
}
-jlong getLongField(JNIEnv *env, jobject obj, const char *name)
+jbyte JNIHelper::getByteField(jobject obj, const char *name)
{
- jclass cls = (env)->GetObjectClass(obj);
- jfieldID field = (env)->GetFieldID(cls, name, "J");
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
+ jfieldID field = mEnv->GetFieldID(cls, name, "B");
if (field == 0) {
- THROW(env, "Error in accessing field");
+ THROW(*this, "Error in accessing field");
return 0;
}
- jlong value = (env)->GetLongField(obj, field);
- env->DeleteLocalRef(cls);
- return value;
+ return mEnv->GetByteField(obj, field);
}
-jlong getStaticLongField(JNIEnv *env, jobject obj, const char *name)
+jlong JNIHelper::getLongField(jobject obj, const char *name)
{
- jclass cls = (env)->GetObjectClass(obj);
- jlong result = getStaticLongField(env, cls, name);
- env->DeleteLocalRef(cls);
- return result;
-}
-
-jlong getStaticLongField(JNIEnv *env, jclass cls, const char *name)
-{
- jfieldID field = (env)->GetStaticFieldID(cls, name, "J");
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
+ jfieldID field = mEnv->GetFieldID(cls, name, "J");
if (field == 0) {
- THROW(env, "Error in accessing field");
- return 0;
- }
- ALOGE("getStaticLongField %s %p", name, cls);
-
- return (env)->GetStaticLongField(cls, field);
-}
-
-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");
+ THROW(*this, "Error in accessing field");
return 0;
}
- jobject value = (env)->GetObjectField(obj, field);
- env->DeleteLocalRef(cls);
- return value;
+ return mEnv->GetLongField(obj, field);
}
-jlong getLongArrayField(JNIEnv *env, jobject obj, const char *name, int index)
+JNIObject<jstring> JNIHelper::getStringField(jobject obj, const char *name)
{
- jclass cls = (env)->GetObjectClass(obj);
- jfieldID field = (env)->GetFieldID(cls, name, "[J");
+ JNIObject<jobject> m = getObjectField(obj, name, "Ljava/lang/String;");
+ if (m == NULL) {
+ THROW(*this, "Error in accessing field");
+ return JNIObject<jstring>(*this, NULL);
+ }
+
+ return JNIObject<jstring>(*this, (jstring)m.detach());
+}
+
+bool JNIHelper::getStringFieldValue(jobject obj, const char *name, char *buf, int size)
+{
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
+ jfieldID field = mEnv->GetFieldID(cls, name, "Ljava/lang/String;");
if (field == 0) {
- THROW(env, "Error in accessing field definition");
+ THROW(*this, "Error in accessing field");
return 0;
}
- jlongArray array = (jlongArray)(env)->GetObjectField(obj, field);
+ JNIObject<jobject> value(*this, mEnv->GetObjectField(obj, field));
+ JNIObject<jstring> string(*this, (jstring)value.clone());
+ ScopedUtfChars chars(mEnv, string);
+
+ const char *utf = chars.c_str();
+ if (utf == NULL) {
+ THROW(*this, "Error in accessing value");
+ return false;
+ }
+
+ if (*utf != 0 && size < 1) {
+ return false;
+ }
+
+ strncpy(buf, utf, size);
+ if (size > 0) {
+ buf[size - 1] = 0;
+ }
+
+ return true;
+}
+
+jlong JNIHelper::getStaticLongField(jobject obj, const char *name)
+{
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
+ return getStaticLongField(cls, name);
+}
+
+jlong JNIHelper::getStaticLongField(jclass cls, const char *name)
+{
+ jfieldID field = mEnv->GetStaticFieldID(cls, name, "J");
+ if (field == 0) {
+ THROW(*this, "Error in accessing field");
+ return 0;
+ }
+ //ALOGE("getStaticLongField %s %p", name, cls);
+ return mEnv->GetStaticLongField(cls, field);
+}
+
+JNIObject<jobject> JNIHelper::getObjectField(jobject obj, const char *name, const char *type)
+{
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
+ jfieldID field = mEnv->GetFieldID(cls, name, type);
+ if (field == 0) {
+ THROW(*this, "Error in accessing field");
+ return JNIObject<jobject>(*this, NULL);
+ }
+
+ return JNIObject<jobject>(*this, mEnv->GetObjectField(obj, field));
+}
+
+JNIObject<jobjectArray> JNIHelper::getArrayField(jobject obj, const char *name, const char *type)
+{
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
+ jfieldID field = mEnv->GetFieldID(cls, name, type);
+ if (field == 0) {
+ THROW(*this, "Error in accessing field");
+ return JNIObject<jobjectArray>(*this, NULL);
+ }
+
+ return JNIObject<jobjectArray>(*this, (jobjectArray)mEnv->GetObjectField(obj, field));
+}
+
+jlong JNIHelper::getLongArrayField(jobject obj, const char *name, int index)
+{
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
+ jfieldID field = mEnv->GetFieldID(cls, name, "[J");
+ if (field == 0) {
+ THROW(*this, "Error in accessing field definition");
+ return 0;
+ }
+
+ JNIObject<jlongArray> array(*this, (jlongArray)mEnv->GetObjectField(obj, field));
if (array == NULL) {
- THROW(env, "Error in accessing array");
+ THROW(*this, "Error in accessing array");
return 0;
}
- jlong *elem = (env)->GetLongArrayElements(array, 0);
+ jlong *elem = mEnv->GetLongArrayElements(array, 0);
if (elem == NULL) {
- THROW(env, "Error in accessing index element");
+ THROW(*this, "Error in accessing index element");
return 0;
}
jlong value = elem[index];
- (env)->ReleaseLongArrayElements(array, elem, 0);
-
- env->DeleteLocalRef(array);
- env->DeleteLocalRef(cls);
-
+ mEnv->ReleaseLongArrayElements(array, elem, 0);
return value;
}
-jlong getStaticLongArrayField(JNIEnv *env, jobject obj, const char *name, int index)
+jlong JNIHelper::getStaticLongArrayField(jobject obj, const char *name, int index)
{
- jclass cls = (env)->GetObjectClass(obj);
- jlong result = getStaticLongArrayField(env, cls, name, index);
- env->DeleteLocalRef(cls);
- return result;
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
+ return getStaticLongArrayField(cls, name, index);
}
-jlong getStaticLongArrayField(JNIEnv *env, jclass cls, const char *name, int index)
+jlong JNIHelper::getStaticLongArrayField(jclass cls, const char *name, int index)
{
- jfieldID field = (env)->GetStaticFieldID(cls, name, "[J");
+ jfieldID field = mEnv->GetStaticFieldID(cls, name, "[J");
if (field == 0) {
- THROW(env, "Error in accessing field definition");
+ THROW(*this, "Error in accessing field definition");
return 0;
}
- jlongArray array = (jlongArray)(env)->GetStaticObjectField(cls, field);
- jlong *elem = (env)->GetLongArrayElements(array, 0);
+ JNIObject<jlongArray> array(*this, (jlongArray)mEnv->GetStaticObjectField(cls, field));
+ jlong *elem = mEnv->GetLongArrayElements(array, 0);
if (elem == NULL) {
- THROW(env, "Error in accessing index element");
+ THROW(*this, "Error in accessing index element");
return 0;
}
jlong value = elem[index];
- (env)->ReleaseLongArrayElements(array, elem, 0);
-
- env->DeleteLocalRef(array);
+ mEnv->ReleaseLongArrayElements(array, elem, 0);
return value;
}
-jobject getObjectArrayField(JNIEnv *env, jobject obj, const char *name, const char *type, int index)
+JNIObject<jobject> JNIHelper::getObjectArrayField(jobject obj, const char *name, const char *type,
+int index)
{
- jclass cls = (env)->GetObjectClass(obj);
- jfieldID field = (env)->GetFieldID(cls, name, type);
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
+ jfieldID field = mEnv->GetFieldID(cls, name, type);
if (field == 0) {
- THROW(env, "Error in accessing field definition");
- return 0;
+ THROW(*this, "Error in accessing field definition");
+ return JNIObject<jobject>(*this, NULL);
}
- 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;
+ JNIObject<jobjectArray> array(*this, (jobjectArray)mEnv->GetObjectField(obj, field));
+ JNIObject<jobject> elem(*this, mEnv->GetObjectArrayElement(array, index));
+ if (elem.isNull()) {
+ THROW(*this, "Error in accessing index element");
+ return JNIObject<jobject>(*this, NULL);
}
-
- env->DeleteLocalRef(array);
- env->DeleteLocalRef(cls);
return elem;
}
-void setIntField(JNIEnv *env, jobject obj, const char *name, jint value)
+void JNIHelper::setIntField(jobject obj, const char *name, jint value)
{
- jclass cls = (env)->GetObjectClass(obj);
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
if (cls == NULL) {
- THROW(env, "Error in accessing class");
+ THROW(*this, "Error in accessing class");
return;
}
- jfieldID field = (env)->GetFieldID(cls, name, "I");
+ jfieldID field = mEnv->GetFieldID(cls, name, "I");
if (field == NULL) {
- THROW(env, "Error in accessing field");
+ THROW(*this, "Error in accessing field");
return;
}
- (env)->SetIntField(obj, field, value);
- env->DeleteLocalRef(cls);
+ mEnv->SetIntField(obj, field, value);
}
-void setLongField(JNIEnv *env, jobject obj, const char *name, jlong value)
+void JNIHelper::setByteField(jobject obj, const char *name, jbyte value)
{
- jclass cls = (env)->GetObjectClass(obj);
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
if (cls == NULL) {
- THROW(env, "Error in accessing class");
+ THROW(*this, "Error in accessing class");
return;
}
- jfieldID field = (env)->GetFieldID(cls, name, "J");
+ jfieldID field = mEnv->GetFieldID(cls, name, "B");
if (field == NULL) {
- THROW(env, "Error in accessing field");
+ THROW(*this, "Error in accessing field");
return;
}
- (env)->SetLongField(obj, field, value);
- env->DeleteLocalRef(cls);
+ mEnv->SetByteField(obj, field, value);
}
-void setStaticLongField(JNIEnv *env, jobject obj, const char *name, jlong value)
+void JNIHelper::setBooleanField(jobject obj, const char *name, jboolean value)
{
- jclass cls = (env)->GetObjectClass(obj);
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
if (cls == NULL) {
- THROW(env, "Error in accessing class");
+ THROW(*this, "Error in accessing class");
return;
}
- setStaticLongField(env, cls, name, value);
- env->DeleteLocalRef(cls);
-}
-
-void setStaticLongField(JNIEnv *env, jclass cls, const char *name, jlong value)
-{
- jfieldID field = (env)->GetStaticFieldID(cls, name, "J");
+ jfieldID field = mEnv->GetFieldID(cls, name, "Z");
if (field == NULL) {
- THROW(env, "Error in accessing field");
+ THROW(*this, "Error in accessing field");
return;
}
- (env)->SetStaticLongField(cls, field, value);
+ mEnv->SetBooleanField(obj, field, value);
}
-void setLongArrayField(JNIEnv *env, jobject obj, const char *name, jlongArray value)
+void JNIHelper::setLongField(jobject obj, const char *name, jlong value)
{
- jclass cls = (env)->GetObjectClass(obj);
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
if (cls == NULL) {
- THROW(env, "Error in accessing field");
+ THROW(*this, "Error in accessing class");
return;
- } else {
- ALOGD("cls = %p", cls);
}
- jfieldID field = (env)->GetFieldID(cls, name, "[J");
+ jfieldID field = mEnv->GetFieldID(cls, name, "J");
if (field == NULL) {
- THROW(env, "Error in accessing field");
+ THROW(*this, "Error in accessing field");
return;
}
- (env)->SetObjectField(obj, field, value);
- ALOGD("array field set");
-
- env->DeleteLocalRef(cls);
+ mEnv->SetLongField(obj, field, value);
}
-void setStaticLongArrayField(JNIEnv *env, jobject obj, const char *name, jlongArray value)
+void JNIHelper::setStaticLongField(jobject obj, const char *name, jlong value)
{
- jclass cls = (env)->GetObjectClass(obj);
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
if (cls == NULL) {
- THROW(env, "Error in accessing field");
+ THROW(*this, "Error in accessing class");
return;
- } else {
- ALOGD("cls = %p", cls);
}
- setStaticLongArrayField(env, cls, name, value);
- env->DeleteLocalRef(cls);
+ setStaticLongField(cls, name, value);
}
-void setStaticLongArrayField(JNIEnv *env, jclass cls, const char *name, jlongArray value)
+void JNIHelper::setStaticLongField(jclass cls, const char *name, jlong value)
{
- jfieldID field = (env)->GetStaticFieldID(cls, name, "[J");
+ jfieldID field = mEnv->GetStaticFieldID(cls, name, "J");
if (field == NULL) {
- THROW(env, "Error in accessing field");
+ THROW(*this, "Error in accessing field");
return;
}
- (env)->SetStaticObjectField(cls, field, value);
+ mEnv->SetStaticLongField(cls, field, value);
+}
+
+void JNIHelper::setLongArrayField(jobject obj, const char *name, jlongArray value)
+{
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
+ if (cls == NULL) {
+ THROW(*this, "Error in accessing field");
+ return;
+ }
+
+ jfieldID field = mEnv->GetFieldID(cls, name, "[J");
+ if (field == NULL) {
+ THROW(*this, "Error in accessing field");
+ return;
+ }
+
+ mEnv->SetObjectField(obj, field, value);
+}
+
+void JNIHelper::setStaticLongArrayField(jobject obj, const char *name, jlongArray value)
+{
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
+ if (cls == NULL) {
+ THROW(*this, "Error in accessing field");
+ return;
+ }
+
+ setStaticLongArrayField(cls, name, value);
+}
+
+void JNIHelper::setStaticLongArrayField(jclass cls, const char *name, jlongArray value)
+{
+ jfieldID field = mEnv->GetStaticFieldID(cls, name, "[J");
+ if (field == NULL) {
+ THROW(*this, "Error in accessing field");
+ return;
+ }
+
+ mEnv->SetStaticObjectField(cls, field, value);
ALOGD("array field set");
}
-void setLongArrayElement(JNIEnv *env, jobject obj, const char *name, int index, jlong value)
+void JNIHelper::setLongArrayElement(jobject obj, const char *name, int index, jlong value)
{
- jclass cls = (env)->GetObjectClass(obj);
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
if (cls == NULL) {
- THROW(env, "Error in accessing field");
+ THROW(*this, "Error in accessing field");
return;
- } else {
- ALOGD("cls = %p", cls);
}
- jfieldID field = (env)->GetFieldID(cls, name, "[J");
+ jfieldID field = mEnv->GetFieldID(cls, name, "[J");
if (field == NULL) {
- THROW(env, "Error in accessing field");
+ THROW(*this, "Error in accessing field");
return;
- } else {
- ALOGD("field = %p", field);
}
- jlongArray array = (jlongArray)(env)->GetObjectField(obj, field);
+ JNIObject<jlongArray> array(*this, (jlongArray)mEnv->GetObjectField(obj, field));
if (array == NULL) {
- THROW(env, "Error in accessing array");
+ THROW(*this, "Error in accessing array");
return;
- } else {
- ALOGD("array = %p", array);
}
- jlong *elem = (env)->GetLongArrayElements(array, NULL);
+ jlong *elem = mEnv->GetLongArrayElements(array, NULL);
if (elem == NULL) {
- THROW(env, "Error in accessing index element");
+ THROW(*this, "Error in accessing index element");
return;
}
elem[index] = value;
- env->ReleaseLongArrayElements(array, elem, 0);
- env->DeleteLocalRef(array);
- env->DeleteLocalRef(cls);
+ mEnv->ReleaseLongArrayElements(array, elem, 0);
}
-void setObjectField(JNIEnv *env, jobject obj, const char *name, const char *type, jobject value)
+void JNIHelper::setObjectField(jobject obj, const char *name, const char *type, jobject value)
{
- jclass cls = (env)->GetObjectClass(obj);
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
if (cls == NULL) {
- THROW(env, "Error in accessing class");
+ THROW(*this, "Error in accessing class");
return;
}
- jfieldID field = (env)->GetFieldID(cls, name, type);
+ jfieldID field = mEnv->GetFieldID(cls, name, type);
if (field == NULL) {
- THROW(env, "Error in accessing field");
+ THROW(*this, "Error in accessing field");
return;
}
- (env)->SetObjectField(obj, field, value);
- env->DeleteLocalRef(cls);
+ mEnv->SetObjectField(obj, field, value);
}
-void setStringField(JNIEnv *env, jobject obj, const char *name, const char *value)
+jboolean JNIHelper::setStringField(jobject obj, const char *name, const char *value)
{
- jstring str = env->NewStringUTF(value);
+ JNIObject<jstring> str(*this, mEnv->NewStringUTF(value));
+
+ if (mEnv->ExceptionCheck()) {
+ mEnv->ExceptionDescribe();
+ mEnv->ExceptionClear();
+ return false;
+ }
if (str == NULL) {
- THROW(env, "Error in accessing class");
- return;
+ THROW(*this, "Error creating string");
+ return false;
}
- setObjectField(env, obj, name, "Ljava/lang/String;", str);
- env->DeleteLocalRef(str);
+ setObjectField(obj, name, "Ljava/lang/String;", str);
+ return true;
}
-void reportEvent(JNIEnv *env, jclass cls, const char *method, const char *signature, ...)
+void JNIHelper::reportEvent(jclass cls, const char *method, const char *signature, ...)
{
va_list params;
va_start(params, signature);
- jmethodID methodID = env->GetStaticMethodID(cls, method, signature);
- if (method == NULL) {
+ jmethodID methodID = mEnv->GetStaticMethodID(cls, method, signature);
+ if (methodID == 0) {
ALOGE("Error in getting method ID");
return;
}
- env->CallStaticVoidMethodV(cls, methodID, params);
+ mEnv->CallStaticVoidMethodV(cls, methodID, params);
+ if (mEnv->ExceptionCheck()) {
+ mEnv->ExceptionDescribe();
+ mEnv->ExceptionClear();
+ }
+
va_end(params);
}
-jobject createObject(JNIEnv *env, const char *className)
+jboolean JNIHelper::callStaticMethod(jclass cls, const char *method, const char *signature, ...)
{
- jclass cls = env->FindClass(className);
+ va_list params;
+ va_start(params, signature);
+
+ jmethodID methodID = mEnv->GetStaticMethodID(cls, method, signature);
+ if (methodID == 0) {
+ ALOGE("Error in getting method ID");
+ return false;
+ }
+
+ jboolean result = mEnv->CallStaticBooleanMethodV(cls, methodID, params);
+ if (mEnv->ExceptionCheck()) {
+ mEnv->ExceptionDescribe();
+ mEnv->ExceptionClear();
+ return false;
+ }
+
+ va_end(params);
+ return result;
+}
+
+JNIObject<jobject> JNIHelper::createObject(const char *className)
+{
+ JNIObject<jclass> cls(*this, mEnv->FindClass(className));
if (cls == NULL) {
- ALOGE("Error in finding class");
- return NULL;
+ ALOGE("Error in finding class %s", className);
+ return JNIObject<jobject>(*this, NULL);
}
- jmethodID constructor = env->GetMethodID(cls, "<init>", "()V");
- if (constructor == NULL) {
- ALOGE("Error in constructor ID");
- return NULL;
+ jmethodID constructor = mEnv->GetMethodID(cls, "<init>", "()V");
+ if (constructor == 0) {
+ ALOGE("Error in constructor ID for %s", className);
+ return JNIObject<jobject>(*this, NULL);
}
- jobject obj = env->NewObject(cls, constructor);
- if (constructor == NULL) {
+
+ JNIObject<jobject> obj(*this, mEnv->NewObject(cls, constructor));
+ if (obj == NULL) {
ALOGE("Could not create new object of %s", className);
- return NULL;
+ return JNIObject<jobject>(*this, NULL);
}
- env->DeleteLocalRef(cls);
return obj;
}
+JNIObject<jobjectArray> JNIHelper::createObjectArray(const char *className, int num)
+{
+ JNIObject<jclass> cls(*this, mEnv->FindClass(className));
+ if (cls == NULL) {
+ ALOGE("Error in finding class %s", className);
+ return JNIObject<jobjectArray>(*this, NULL);
+ }
+
+ JNIObject<jobject> array(*this, mEnv->NewObjectArray(num, cls.get(), NULL));
+ if (array.get() == NULL) {
+ ALOGE("Error in creating array of class %s", className);
+ return JNIObject<jobjectArray>(*this, NULL);
+ }
+
+ return JNIObject<jobjectArray>(*this, (jobjectArray)array.detach());
+}
+
+JNIObject<jobject> JNIHelper::getObjectArrayElement(jobjectArray array, int index)
+{
+ return JNIObject<jobject>(*this, mEnv->GetObjectArrayElement(array, index));
+}
+
+JNIObject<jobject> JNIHelper::getObjectArrayElement(jobject array, int index)
+{
+ return getObjectArrayElement((jobjectArray)array, index);
+}
+
+int JNIHelper::getArrayLength(jarray array) {
+ return mEnv->GetArrayLength(array);
+}
+
+JNIObject<jobjectArray> JNIHelper::newObjectArray(int num, const char *className, jobject val) {
+ JNIObject<jclass> cls(*this, mEnv->FindClass(className));
+ if (cls == NULL) {
+ ALOGE("Error in finding class %s", className);
+ return JNIObject<jobjectArray>(*this, NULL);
+ }
+
+ return JNIObject<jobjectArray>(*this, mEnv->NewObjectArray(num, cls, val));
+}
+
+JNIObject<jbyteArray> JNIHelper::newByteArray(int num) {
+ return JNIObject<jbyteArray>(*this, mEnv->NewByteArray(num));
+}
+
+JNIObject<jintArray> JNIHelper::newIntArray(int num) {
+ return JNIObject<jintArray>(*this, mEnv->NewIntArray(num));
+}
+
+JNIObject<jlongArray> JNIHelper::newLongArray(int num) {
+ return JNIObject<jlongArray>(*this, mEnv->NewLongArray(num));
+}
+
+JNIObject<jstring> JNIHelper::newStringUTF(const char *utf) {
+ return JNIObject<jstring>(*this, mEnv->NewStringUTF(utf));
+}
+
+void JNIHelper::setObjectArrayElement(jobjectArray array, int index, jobject obj) {
+ mEnv->SetObjectArrayElement(array, index, obj);
+}
+
+void JNIHelper::setByteArrayRegion(jbyteArray array, int from, int to, jbyte *bytes) {
+ mEnv->SetByteArrayRegion(array, from, to, bytes);
+}
+
+void JNIHelper::setIntArrayRegion(jintArray array, int from, int to, jint *ints) {
+ mEnv->SetIntArrayRegion(array, from, to, ints);
+}
+
+void JNIHelper::setLongArrayRegion(jlongArray array, int from, int to, jlong *longs) {
+ mEnv->SetLongArrayRegion(array, from, to, longs);
+}
+
}; // namespace android
diff --git a/service/jni/jni_helper.h b/service/jni/jni_helper.h
index eaa87c2..834b0ac 100644
--- a/service/jni/jni_helper.h
+++ b/service/jni/jni_helper.h
@@ -3,30 +3,158 @@
/* 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);
-void setLongArrayElement(JNIEnv *env, jobject obj, const char *name, int index, jlong value);
-void setStringField(JNIEnv *env, jobject obj, const char *name, const char *value);
-void reportEvent(JNIEnv *env, jclass cls, const char *method, const char *signature, ...);
-jobject createObject(JNIEnv *env, const char *className);
-void setObjectField(JNIEnv *env, jobject obj, const char *name, const char *type, jobject value);
+class JNIHelper;
-jlong getStaticLongField(JNIEnv *env, jobject obj, const char *name);
-jlong getStaticLongField(JNIEnv *env, jclass cls, const char *name);
-void setStaticLongField(JNIEnv *env, jobject obj, const char *name, jlong value);
-void setStaticLongField(JNIEnv *env, jclass cls, const char *name, jlong value);
-jlong getStaticLongArrayField(JNIEnv *env, jobject obj, const char *name, int index);
-jlong getStaticLongArrayField(JNIEnv *env, jclass cls, const char *name, int index);
-void setStaticLongArrayField(JNIEnv *env, jobject obj, const char *name, jlongArray value);
-void setStaticLongArrayField(JNIEnv *env, jclass obj, const char *name, jlongArray value);
+template<typename T>
+class JNIObject {
+protected:
+ JNIHelper &mHelper;
+ T mObj;
+public:
+ JNIObject(JNIHelper &helper, T obj);
+ JNIObject(const JNIObject<T>& rhs);
+ virtual ~JNIObject();
+ JNIHelper& getHelper() const {
+ return mHelper;
+ }
+ T get() const {
+ return mObj;
+ }
+ operator T() const {
+ return mObj;
+ }
+ bool isNull() const {
+ return mObj == NULL;
+ }
+ void release();
+ T detach() {
+ T tObj = mObj;
+ mObj = NULL;
+ return tObj;
+ }
+ T clone();
+ JNIObject<T>& operator = (const JNIObject<T>& rhs) {
+ release();
+ mHelper = rhs.mHelper;
+ mObj = rhs.mObj;
+ return *this;
+ }
+ void print() {
+ ALOGD("holding %p", mObj);
+ }
+
+private:
+ template<typename T2>
+ JNIObject(const JNIObject<T2>& rhs);
+};
+
+class JNIHelper {
+ JavaVM *mVM;
+ JNIEnv *mEnv;
+
+public :
+ JNIHelper(JavaVM *vm);
+ JNIHelper(JNIEnv *env);
+ ~JNIHelper();
+
+ void throwException(const char *message, int line);
+
+ /* helpers to deal with members */
+ jboolean getBoolField(jobject obj, const char *name);
+ jint getIntField(jobject obj, const char *name);
+ jlong getLongField(jobject obj, const char *name);
+ JNIObject<jstring> getStringField(jobject obj, const char *name);
+ bool getStringFieldValue(jobject obj, const char *name, char *buf, int size);
+ JNIObject<jobject> getObjectField(jobject obj, const char *name, const char *type);
+ JNIObject<jobjectArray> getArrayField(jobject obj, const char *name, const char *type);
+ jlong getLongArrayField(jobject obj, const char *name, int index);
+ JNIObject<jobject> getObjectArrayField(
+ jobject obj, const char *name, const char *type, int index);
+ void setIntField(jobject obj, const char *name, jint value);
+ void setByteField(jobject obj, const char *name, jbyte value);
+ jbyte getByteField(jobject obj, const char *name);
+ void setBooleanField(jobject obj, const char *name, jboolean value);
+ void setLongField(jobject obj, const char *name, jlong value);
+ void setLongArrayField(jobject obj, const char *name, jlongArray value);
+ void setLongArrayElement(jobject obj, const char *name, int index, jlong value);
+ jboolean setStringField(jobject obj, const char *name, const char *value);
+ void reportEvent(jclass cls, const char *method, const char *signature, ...);
+ JNIObject<jobject> createObject(const char *className);
+ JNIObject<jobjectArray> createObjectArray(const char *className, int size);
+ void setObjectField(jobject obj, const char *name, const char *type, jobject value);
+
+ /* helpers to deal with static members */
+ jlong getStaticLongField(jobject obj, const char *name);
+ jlong getStaticLongField(jclass cls, const char *name);
+ void setStaticLongField(jobject obj, const char *name, jlong value);
+ void setStaticLongField(jclass cls, const char *name, jlong value);
+ jlong getStaticLongArrayField(jobject obj, const char *name, int index);
+ jlong getStaticLongArrayField(jclass cls, const char *name, int index);
+ void setStaticLongArrayField(jobject obj, const char *name, jlongArray value);
+ void setStaticLongArrayField(jclass obj, const char *name, jlongArray value);
+ jboolean callStaticMethod(jclass cls, const char *method, const char *signature, ...);
+
+ JNIObject<jobject> getObjectArrayElement(jobjectArray array, int index);
+ JNIObject<jobject> getObjectArrayElement(jobject array, int index);
+ int getArrayLength(jarray array);
+ JNIObject<jobjectArray> newObjectArray(int num, const char *className, jobject val);
+ JNIObject<jbyteArray> newByteArray(int num);
+ JNIObject<jintArray> newIntArray(int num);
+ JNIObject<jlongArray> newLongArray(int num);
+ JNIObject<jstring> newStringUTF(const char *utf);
+ void setObjectArrayElement(jobjectArray array, int index, jobject obj);
+ void setByteArrayRegion(jbyteArray array, int from, int to, jbyte *bytes);
+ void setIntArrayRegion(jintArray array, int from, int to, jint *ints);
+ void setLongArrayRegion(jlongArray array, int from, int to, jlong *longs);
+
+ jobject newGlobalRef(jobject obj);
+ void deleteGlobalRef(jobject obj);
+
+private:
+ /* Jni wrappers */
+ friend class JNIObject<jobject>;
+ friend class JNIObject<jstring>;
+ friend class JNIObject<jobjectArray>;
+ friend class JNIObject<jclass>;
+ friend class JNIObject<jlongArray>;
+ friend class JNIObject<jbyteArray>;
+ friend class JNIObject<jintArray>;
+ jobject newLocalRef(jobject obj);
+ void deleteLocalRef(jobject obj);
+};
+
+template<typename T>
+JNIObject<T>::JNIObject(JNIHelper &helper, T obj)
+ : mHelper(helper), mObj(obj)
+{ }
+
+template<typename T>
+JNIObject<T>::JNIObject(const JNIObject<T>& rhs)
+ : mHelper(rhs.mHelper), mObj(NULL)
+{
+ mObj = (T)mHelper.newLocalRef(rhs.mObj);
}
-#define THROW(env, message) throwException(env, message, __LINE__)
+template<typename T>
+JNIObject<T>::~JNIObject() {
+ release();
+}
+
+template<typename T>
+void JNIObject<T>::release()
+{
+ if (mObj != NULL) {
+ mHelper.deleteLocalRef(mObj);
+ mObj = NULL;
+ }
+}
+
+template<typename T>
+T JNIObject<T>::clone()
+{
+ return mHelper.newLocalRef(mObj);
+}
+
+}
+
+#define THROW(env, message) (env).throwException(message, __LINE__)
diff --git a/service/jni/wifi_hal_stub.h b/service/jni/wifi_hal_stub.h
new file mode 100644
index 0000000..bd00947
--- /dev/null
+++ b/service/jni/wifi_hal_stub.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef __WIFI_HAL_STUB_H__
+#define __WIFI_HAL_STUB_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include "wifi_hal.h"
+/* declare all HAL stub API here*/
+wifi_error wifi_initialize_stub(wifi_handle *handle);
+void wifi_cleanup_stub(wifi_handle handle, wifi_cleaned_up_handler handler);
+void wifi_event_loop_stub(wifi_handle handle);
+void wifi_get_error_info_stub(wifi_error err, const char **msg);
+wifi_error wifi_get_supported_feature_set_stub(wifi_interface_handle handle, feature_set *set);
+wifi_error wifi_get_concurrency_matrix_stub(wifi_interface_handle handle, int set_size_max,
+ feature_set set[], int *set_size);
+wifi_error wifi_get_ifaces_stub(wifi_handle handle, int *num_ifaces, wifi_interface_handle **ifaces);
+wifi_error wifi_get_iface_name_stub(wifi_interface_handle iface, char *name, size_t size);
+wifi_error wifi_set_iface_event_handler_stub(wifi_request_id id, wifi_interface_handle iface,
+ wifi_event_handler eh);
+wifi_error wifi_reset_iface_event_handler_stub(wifi_request_id id, wifi_interface_handle iface);
+wifi_error wifi_set_nodfs_flag_stub(wifi_interface_handle handle, u32 nodfs);
+wifi_error wifi_set_scanning_mac_oui_stub(wifi_interface_handle handle, unsigned char *oui);
+wifi_error wifi_get_supported_channels_stub(wifi_handle handle, int *size, wifi_channel *list);
+wifi_error wifi_is_epr_supported_stub(wifi_handle handle);
+wifi_error wifi_start_gscan_stub(wifi_request_id id, wifi_interface_handle iface,
+ wifi_scan_cmd_params params, wifi_scan_result_handler handler);
+wifi_error wifi_stop_gscan_stub(wifi_request_id id, wifi_interface_handle iface);
+wifi_error wifi_get_cached_gscan_results_stub(wifi_interface_handle iface, byte flush,
+ int max, wifi_cached_scan_results *results, int *num);
+wifi_error wifi_set_bssid_hotlist_stub(wifi_request_id id, wifi_interface_handle iface,
+ wifi_bssid_hotlist_params params, wifi_hotlist_ap_found_handler handler);
+wifi_error wifi_reset_bssid_hotlist_stub(wifi_request_id id, wifi_interface_handle iface);
+wifi_error wifi_set_significant_change_handler_stub(wifi_request_id id, wifi_interface_handle iface,
+ wifi_significant_change_params params, wifi_significant_change_handler handler);
+wifi_error wifi_reset_significant_change_handler_stub(wifi_request_id id,
+ wifi_interface_handle iface);
+wifi_error wifi_get_gscan_capabilities_stub(wifi_interface_handle handle,
+ wifi_gscan_capabilities *capabilities);
+wifi_error wifi_set_link_stats_stub(wifi_interface_handle iface, wifi_link_layer_params params);
+wifi_error wifi_get_link_stats_stub(wifi_request_id id,
+ wifi_interface_handle iface, wifi_stats_result_handler handler);
+wifi_error wifi_clear_link_stats_stub(wifi_interface_handle iface,
+ u32 stats_clear_req_mask, u32 *stats_clear_rsp_mask, u8 stop_req, u8 *stop_rsp);
+wifi_error wifi_get_valid_channels_stub(wifi_interface_handle handle,
+ int band, int max_channels, wifi_channel *channels, int *num_channels);
+wifi_error wifi_rtt_range_request_stub(wifi_request_id id, wifi_interface_handle iface,
+ unsigned num_rtt_config, wifi_rtt_config rtt_config[], wifi_rtt_event_handler handler);
+wifi_error wifi_rtt_range_cancel_stub(wifi_request_id id, wifi_interface_handle iface,
+ unsigned num_devices, mac_addr addr[]);
+wifi_error wifi_get_rtt_capabilities_stub(wifi_interface_handle iface,
+ wifi_rtt_capabilities *capabilities);
+wifi_error wifi_set_nodfs_flag_stub(wifi_interface_handle iface, u32 nodfs);
+wifi_error wifi_start_logging_stub(wifi_interface_handle iface, u32 verbose_level, u32 flags,
+ u32 max_interval_sec, u32 min_data_size, char *buffer_name);
+wifi_error wifi_set_epno_list_stub(int id, wifi_interface_info *iface, int num_networks,
+ wifi_epno_network *networks, wifi_epno_handler handler);
+wifi_error wifi_set_country_code_stub(wifi_interface_handle iface, const char *code);
+wifi_error wifi_get_firmware_memory_dump_stub( wifi_interface_handle iface,
+ wifi_firmware_memory_dump_handler handler);
+wifi_error wifi_set_log_handler_stub(wifi_request_id id, wifi_interface_handle iface,
+ wifi_ring_buffer_data_handler handler);
+wifi_error wifi_reset_log_handler_stub(wifi_request_id id, wifi_interface_handle iface);
+wifi_error wifi_set_alert_handler_stub(wifi_request_id id, wifi_interface_handle iface,
+ wifi_alert_handler handler);
+wifi_error wifi_reset_alert_handler_stub(wifi_request_id id, wifi_interface_handle iface);
+wifi_error wifi_get_firmware_version_stub(wifi_interface_handle iface, char *buffer,
+ int buffer_size);
+wifi_error wifi_get_ring_buffers_status_stub(wifi_interface_handle iface,
+ u32 *num_rings, wifi_ring_buffer_status *status);
+wifi_error wifi_get_logger_supported_feature_set_stub(wifi_interface_handle iface,
+ unsigned int *support);
+wifi_error wifi_get_ring_data_stub(wifi_interface_handle iface, char *ring_name);
+wifi_error wifi_set_epno_list_stub(int id, wifi_interface_info *iface, int num_networks,
+ wifi_epno_network *networks, wifi_epno_handler handler);
+wifi_error wifi_enable_tdls_stub(wifi_interface_handle iface, mac_addr addr,
+ wifi_tdls_params *params, wifi_tdls_handler handler);
+wifi_error wifi_disable_tdls_stub(wifi_interface_handle iface, mac_addr addr);
+wifi_error wifi_get_tdls_status_stub(wifi_interface_handle iface, mac_addr addr,
+ wifi_tdls_status *status);
+wifi_error wifi_get_tdls_capabilities_stub(wifi_interface_handle iface,
+ wifi_tdls_capabilities *capabilities);
+wifi_error wifi_get_driver_version_stub(wifi_interface_handle iface, char *buffer,
+ int buffer_size);
+ wifi_error wifi_set_country_code_stub(wifi_interface_handle iface, const char *code);
+wifi_error wifi_set_bssid_blacklist_stub(wifi_request_id id, wifi_interface_handle iface,
+ wifi_bssid_params params);
+wifi_error wifi_enable_lazy_roam_stub(wifi_request_id id, wifi_interface_handle iface, int enable);
+wifi_error wifi_set_bssid_preference_stub(wifi_request_id id, wifi_interface_handle iface,
+ int num_bssid, wifi_bssid_preference *prefs);
+wifi_error wifi_set_gscan_roam_params_stub(wifi_request_id id, wifi_interface_handle iface,
+ wifi_roam_params * params);
+wifi_error wifi_set_ssid_white_list_stub(wifi_request_id id, wifi_interface_handle iface,
+ int num_networks, wifi_ssid *ssids);
+wifi_error wifi_start_sending_offloaded_packet_stub(wifi_request_id id,
+ wifi_interface_handle iface, u8 *ip_packet, u16 ip_packet_len,
+ u8 *src_mac_addr, u8 *dst_mac_addr, u32 period_msec);
+wifi_error wifi_stop_sending_offloaded_packet_stub(wifi_request_id id, wifi_interface_handle iface);
+#ifdef __cplusplus
+}
+#endif
+#endif //__WIFI_HAL_STUB_H__
diff --git a/service/lib/wifi_hal.cpp b/service/lib/wifi_hal.cpp
index 237efd9..25bb373 100644
--- a/service/lib/wifi_hal.cpp
+++ b/service/lib/wifi_hal.cpp
@@ -1,135 +1,6 @@
#include <stdint.h>
#include "wifi_hal.h"
-wifi_error wifi_initialize(wifi_handle *handle) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-void wifi_cleanup(wifi_handle handle, wifi_cleaned_up_handler handler) {
-}
-
-void wifi_event_loop(wifi_handle handle) {
-
-}
-
-void wifi_get_error_info(wifi_error err, const char **msg) {
- *msg = NULL;
-}
-
-wifi_error wifi_get_supported_feature_set(wifi_interface_handle handle, feature_set *set) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_get_concurrency_matrix(wifi_interface_handle handle, int max_size,
- feature_set *matrix, int *size) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_set_scanning_mac_oui(wifi_interface_handle handle, unsigned char *oui) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-/* List of all supported channels, including 5GHz channels */
-wifi_error wifi_get_supported_channels(wifi_handle handle, int *size, wifi_channel *list) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-/* Enhanced power reporting */
-wifi_error wifi_is_epr_supported(wifi_handle handle) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-/* multiple interface support */
-wifi_error wifi_get_ifaces(wifi_handle handle, int *num_ifaces, wifi_interface_handle **ifaces) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_get_iface_name(wifi_interface_handle iface, char *name, size_t size) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_set_iface_event_handler(wifi_request_id id,
- wifi_interface_handle iface, wifi_event_handler eh) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_reset_iface_event_handler(wifi_request_id id, wifi_interface_handle iface) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_start_gscan(wifi_request_id id, wifi_interface_handle iface,
- wifi_scan_cmd_params params, wifi_scan_result_handler handler) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_stop_gscan(wifi_request_id id, wifi_interface_handle iface) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_get_cached_gscan_results(wifi_interface_handle iface, byte flush,
- int max, wifi_scan_result *results, int *num) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_get_cached_gscan_results(wifi_interface_handle iface, byte flush,
- wifi_scan_result *results, int *num) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_set_bssid_hotlist(wifi_request_id id, wifi_interface_handle iface,
- wifi_bssid_hotlist_params params, wifi_hotlist_ap_found_handler handler) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_reset_bssid_hotlist(wifi_request_id id, wifi_interface_handle iface) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_set_significant_change_handler(wifi_request_id id, wifi_interface_handle iface,
- wifi_significant_change_params params, wifi_significant_change_handler handler) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_reset_significant_change_handler(wifi_request_id id, wifi_interface_handle iface) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_get_gscan_capabilities(wifi_interface_handle handle,
- wifi_gscan_capabilities *capabilities) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_set_link_stats(wifi_interface_handle iface, wifi_link_layer_params params) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_get_link_stats(wifi_request_id id,
- wifi_interface_handle iface, wifi_stats_result_handler handler) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_clear_link_stats(wifi_interface_handle iface,
- u32 stats_clear_req_mask, u32 *stats_clear_rsp_mask, u8 stop_req, u8 *stop_rsp) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_get_valid_channels(wifi_interface_handle handle,
- int band, int max_channels, wifi_channel *channels, int *num_channels) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-/* API to request RTT measurement */
-wifi_error wifi_rtt_range_request(wifi_request_id id, wifi_interface_handle iface,
- unsigned num_rtt_config, wifi_rtt_config rtt_config[], wifi_rtt_event_handler handler) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-/* API to cancel RTT measurements */
-wifi_error wifi_rtt_range_cancel(wifi_request_id id, wifi_interface_handle iface,
- unsigned num_devices, mac_addr addr[]) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_set_nodfs_flag(wifi_interface_handle iface, u32 nodfs) {
+wifi_error init_wifi_vendor_hal_func_table(wifi_hal_fn *fn) {
return WIFI_ERROR_NOT_SUPPORTED;
}
diff --git a/service/lib/wifi_hal_stub.cpp b/service/lib/wifi_hal_stub.cpp
new file mode 100644
index 0000000..65770f3
--- /dev/null
+++ b/service/lib/wifi_hal_stub.cpp
@@ -0,0 +1,253 @@
+#include <stdint.h>
+#include "wifi_hal.h"
+#include "wifi_hal_stub.h"
+
+wifi_error wifi_initialize_stub(wifi_handle *handle) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+void wifi_cleanup_stub(wifi_handle handle, wifi_cleaned_up_handler handler) {
+}
+
+void wifi_event_loop_stub(wifi_handle handle) {
+
+}
+
+void wifi_get_error_info_stub(wifi_error err, const char **msg) {
+ *msg = NULL;
+}
+
+wifi_error wifi_get_supported_feature_set_stub(wifi_interface_handle handle, feature_set *set) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_get_concurrency_matrix_stub(wifi_interface_handle handle, int max_size,
+ feature_set *matrix, int *size) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_set_scanning_mac_oui_stub(wifi_interface_handle handle, unsigned char *oui) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+/* List of all supported channels, including 5GHz channels */
+wifi_error wifi_get_supported_channels_stub(wifi_handle handle, int *size, wifi_channel *list) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+/* Enhanced power reporting */
+wifi_error wifi_is_epr_supported_stub(wifi_handle handle) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+/* multiple interface support */
+wifi_error wifi_get_ifaces_stub(wifi_handle handle, int *num_ifaces, wifi_interface_handle **ifaces) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_get_iface_name_stub(wifi_interface_handle iface, char *name, size_t size) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_set_iface_event_handler_stub(wifi_request_id id,
+ wifi_interface_handle iface, wifi_event_handler eh) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_reset_iface_event_handler_stub(wifi_request_id id, wifi_interface_handle iface) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_start_gscan_stub(wifi_request_id id, wifi_interface_handle iface,
+ wifi_scan_cmd_params params, wifi_scan_result_handler handler) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_stop_gscan_stub(wifi_request_id id, wifi_interface_handle iface) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_get_cached_gscan_results_stub(wifi_interface_handle iface, byte flush,
+ int max, wifi_cached_scan_results *results, int *num) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_set_bssid_hotlist_stub(wifi_request_id id, wifi_interface_handle iface,
+ wifi_bssid_hotlist_params params, wifi_hotlist_ap_found_handler handler) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_reset_bssid_hotlist_stub(wifi_request_id id, wifi_interface_handle iface) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_set_significant_change_handler_stub(wifi_request_id id, wifi_interface_handle iface,
+ wifi_significant_change_params params, wifi_significant_change_handler handler) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_reset_significant_change_handler_stub(wifi_request_id id, wifi_interface_handle iface) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_get_gscan_capabilities_stub(wifi_interface_handle handle,
+ wifi_gscan_capabilities *capabilities) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_set_link_stats_stub(wifi_interface_handle iface, wifi_link_layer_params params) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_get_link_stats_stub(wifi_request_id id,
+ wifi_interface_handle iface, wifi_stats_result_handler handler) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_clear_link_stats_stub(wifi_interface_handle iface,
+ u32 stats_clear_req_mask, u32 *stats_clear_rsp_mask, u8 stop_req, u8 *stop_rsp) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_get_valid_channels_stub(wifi_interface_handle handle,
+ int band, int max_channels, wifi_channel *channels, int *num_channels) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+/* API to request RTT measurement */
+wifi_error wifi_rtt_range_request_stub(wifi_request_id id, wifi_interface_handle iface,
+ unsigned num_rtt_config, wifi_rtt_config rtt_config[], wifi_rtt_event_handler handler) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+/* API to cancel RTT measurements */
+wifi_error wifi_rtt_range_cancel_stub(wifi_request_id id, wifi_interface_handle iface,
+ unsigned num_devices, mac_addr addr[]) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+/* API to get RTT capability */
+wifi_error wifi_get_rtt_capabilities_stub(wifi_interface_handle iface,
+ wifi_rtt_capabilities *capabilities)
+{
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_set_nodfs_flag_stub(wifi_interface_handle iface, u32 nodfs) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_start_logging_stub(wifi_interface_handle iface, u32 verbose_level, u32 flags,
+ u32 max_interval_sec, u32 min_data_size, char *buffer_name) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_set_epno_list_stub(int id, wifi_interface_info *iface, int num_networks,
+ wifi_epno_network *networks, wifi_epno_handler handler) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_set_country_code_stub(wifi_interface_handle iface, const char *code) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_firmware_memory_dump_stub( wifi_interface_handle iface,
+ wifi_firmware_memory_dump_handler handler){
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_set_log_handler_stub(wifi_request_id id, wifi_interface_handle iface,
+ wifi_ring_buffer_data_handler handler) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_reset_log_handler_stub(wifi_request_id id, wifi_interface_handle iface) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_set_alert_handler_stub(wifi_request_id id, wifi_interface_handle iface,
+ wifi_alert_handler handler) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_reset_alert_handler_stub(wifi_request_id id, wifi_interface_handle iface) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_firmware_version_stub( wifi_interface_handle iface, char *buffer,
+ int buffer_size) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_ring_buffers_status_stub(wifi_interface_handle iface,
+ u32 *num_rings, wifi_ring_buffer_status *status) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_logger_supported_feature_set_stub(wifi_interface_handle iface,
+ unsigned int *support) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_ring_data_stub(wifi_interface_handle iface, char *ring_name) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_driver_version_stub(wifi_interface_handle iface, char *buffer,
+ int buffer_size) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_enable_tdls_stub(wifi_interface_handle iface, mac_addr addr,
+ wifi_tdls_params *params, wifi_tdls_handler handler) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_disable_tdls_stub(wifi_interface_handle iface, mac_addr addr) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_tdls_status_stub(wifi_interface_handle iface, mac_addr addr,
+ wifi_tdls_status *status) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_tdls_capabilities_stub(wifi_interface_handle iface,
+ wifi_tdls_capabilities *capabilities) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_set_bssid_blacklist_stub(wifi_request_id id, wifi_interface_handle iface,
+ wifi_bssid_params params) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_enable_lazy_roam_stub(wifi_request_id id, wifi_interface_handle iface, int enable)
+{
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_set_bssid_preference_stub(wifi_request_id id, wifi_interface_handle iface,
+ int num_bssid, wifi_bssid_preference *prefs) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_set_gscan_roam_params_stub(wifi_request_id id, wifi_interface_handle iface,
+ wifi_roam_params * params) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_set_ssid_white_list_stub(wifi_request_id id, wifi_interface_handle iface,
+ int num_networks, wifi_ssid *ssids) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_start_sending_offloaded_packet_stub(wifi_request_id id,
+ wifi_interface_handle iface, u8 *ip_packet, u16 ip_packet_len,
+ u8 *src_mac_addr, u8 *dst_mac_addr, u32 period_msec) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_stop_sending_offloaded_packet_stub(wifi_request_id id, wifi_interface_handle iface) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
diff --git a/service/tools/halutil/halutil.cpp b/service/tools/halutil/halutil.cpp
deleted file mode 100644
index 0dc98e3..0000000
--- a/service/tools/halutil/halutil.cpp
+++ /dev/null
@@ -1,1282 +0,0 @@
-#include <stdint.h>
-#include <stdlib.h>
-
-#include "wifi_hal.h"
-
-#define LOG_TAG "WifiHAL"
-
-#include <utils/Log.h>
-#include <inttypes.h>
-#include <sys/socket.h>
-#include <linux/if.h>
-#include <ctype.h>
-#include <stdarg.h>
-
-pthread_mutex_t printMutex;
-void printMsg(const char *fmt, ...)
-{
- pthread_mutex_lock(&printMutex);
- va_list l;
- va_start(l, fmt);
-
- vprintf(fmt, l);
- va_end(l);
- pthread_mutex_unlock(&printMutex);
-}
-
-template<typename T, unsigned N>
-unsigned countof(T (&rgt)[N]) {
- return N;
-}
-
-template<typename T>
-T min(const T& t1, const T& t2) {
- return (t1 < t2) ? t1 : t2;
-}
-
-#define EVENT_BUF_SIZE 2048
-#define MAX_CH_BUF_SIZE 64
-#define MAX_FEATURE_SET 8
-#define HOTLIST_LOST_WINDOW 5
-
-static wifi_handle halHandle;
-static wifi_interface_handle *ifaceHandles;
-static wifi_interface_handle wlan0Handle;
-static wifi_interface_handle p2p0Handle;
-static int numIfaceHandles;
-static int cmdId = 0;
-static int ioctl_sock = 0;
-static int max_event_wait = 5;
-static int stest_max_ap = 10;
-static int stest_base_period = 5000;
-static int stest_threshold = 80;
-static int swctest_rssi_sample_size = 3;
-static int swctest_rssi_lost_ap = 3;
-static int swctest_rssi_min_breaching = 2;
-static int swctest_rssi_ch_threshold = 1;
-static int htest_low_threshold = 90;
-static int htest_high_threshold = 10;
-static int rtt_samples = 30;
-static wifi_band band = WIFI_BAND_UNSPECIFIED;
-
-mac_addr hotlist_bssids[16];
-unsigned char mac_oui[3];
-int channel_list[16];
-int num_hotlist_bssids = 0;
-int num_channels = 0;
-
-void parseMacAddress(const char *str, mac_addr addr);
-
-int linux_set_iface_flags(int sock, const char *ifname, int dev_up)
-{
- struct ifreq ifr;
- int ret;
-
- printMsg("setting interface %s flags (%s)\n", ifname, dev_up ? "UP" : "DOWN");
-
- if (sock < 0) {
- printMsg("Bad socket: %d\n", sock);
- return -1;
- }
-
- memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-
- printMsg("reading old value\n");
-
- if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
- ret = errno ? -errno : -999;
- printMsg("Could not read interface %s flags: %d\n", ifname, errno);
- return ret;
- }else {
- printMsg("writing new value\n");
- }
-
- if (dev_up) {
- if (ifr.ifr_flags & IFF_UP) {
- printMsg("interface %s is already up\n", ifname);
- return 0;
- }
- ifr.ifr_flags |= IFF_UP;
- }else {
- if (!(ifr.ifr_flags & IFF_UP)) {
- printMsg("interface %s is already down\n", ifname);
- return 0;
- }
- ifr.ifr_flags &= ~IFF_UP;
- }
-
- if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) {
- printMsg("Could not set interface %s flags \n", ifname);
- return ret;
- }else {
- printMsg("set interface %s flags (%s)\n", ifname, dev_up ? "UP" : "DOWN");
- }
- printMsg("Done\n");
- return 0;
-}
-
-
-static int init() {
-
- ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
- if (ioctl_sock < 0) {
- printMsg("Bad socket: %d\n", ioctl_sock);
- return errno;
- } else {
- printMsg("Good socket: %d\n", ioctl_sock);
- }
-
- int ret = linux_set_iface_flags(ioctl_sock, "wlan0", 1);
- if (ret < 0) {
- return ret;
- }
-
- wifi_error res = wifi_initialize(&halHandle);
- if (res < 0) {
- return res;
- }
-
- res = wifi_get_ifaces(halHandle, &numIfaceHandles, &ifaceHandles);
- if (res < 0) {
- return res;
- }
-
- char buf[EVENT_BUF_SIZE];
- for (int i = 0; i < numIfaceHandles; i++) {
- if (wifi_get_iface_name(ifaceHandles[i], buf, sizeof(buf)) == WIFI_SUCCESS) {
- if (strcmp(buf, "wlan0") == 0) {
- printMsg("found interface %s\n", buf);
- wlan0Handle = ifaceHandles[i];
- } else if (strcmp(buf, "p2p0") == 0) {
- printMsg("found interface %s\n", buf);
- p2p0Handle = ifaceHandles[i];
- }
- }
- }
-
- return res;
-}
-
-static void cleaned_up_handler(wifi_handle handle) {
- printMsg("HAL cleaned up handler\n");
- halHandle = NULL;
- ifaceHandles = NULL;
-}
-
-static void cleanup() {
- printMsg("cleaning up HAL\n");
- wifi_cleanup(halHandle, cleaned_up_handler);
-}
-
-static void *eventThreadFunc(void *context) {
-
- printMsg("starting wifi event loop\n");
- wifi_event_loop(halHandle);
- printMsg("out of wifi event loop\n");
-
- return NULL;
-}
-
-
-static int getNewCmdId() {
- return cmdId++;
-}
-
-/* ------------------------------------------- */
-/* helpers */
-/* ------------------------------------------- */
-
-void printScanHeader() {
- printMsg("SSID\t\t\t\t\tBSSID\t\t RSSI\tchannel\ttimestamp\tRTT\tRTT SD\n");
-}
-
-void printScanResult(wifi_scan_result result) {
-
- printMsg("%-32s\t", result.ssid);
-
- printMsg("%02x:%02x:%02x:%02x:%02x:%02x ", result.bssid[0], result.bssid[1],
- result.bssid[2], result.bssid[3], result.bssid[4], result.bssid[5]);
-
- printMsg("%d\t", result.rssi);
- printMsg("%d\t", result.channel);
- printMsg("%lld\t", result.ts);
- printMsg("%lld\t", result.rtt);
- printMsg("%lld\n", result.rtt_sd);
-}
-
-void printSignificantChangeResult(wifi_significant_change_result *res) {
-
- wifi_significant_change_result &result = *res;
- printMsg("%02x:%02x:%02x:%02x:%02x:%02x ", result.bssid[0], result.bssid[1],
- result.bssid[2], result.bssid[3], result.bssid[4], result.bssid[5]);
-
- printMsg("%d\t", result.channel);
-
- for (int i = 0; i < result.num_rssi; i++) {
- printMsg("%d,", result.rssi[i]);
- }
- printMsg("\n");
-}
-
-void printScanCapabilities(wifi_gscan_capabilities capabilities)
-{
- printMsg("max_scan_cache_size = %d\n", capabilities.max_scan_cache_size);
- printMsg("max_scan_buckets = %d\n", capabilities.max_scan_buckets);
- printMsg("max_ap_cache_per_scan = %d\n", capabilities.max_ap_cache_per_scan);
- printMsg("max_rssi_sample_size = %d\n", capabilities.max_rssi_sample_size);
- printMsg("max_scan_reporting_threshold = %d\n", capabilities.max_scan_reporting_threshold);
- printMsg("max_hotlist_aps = %d\n", capabilities.max_hotlist_aps);
- printMsg("max_significant_wifi_change_aps = %d\n",
- capabilities.max_significant_wifi_change_aps);
-}
-
-
-/* ------------------------------------------- */
-/* commands and events */
-/* ------------------------------------------- */
-
-typedef enum {
- EVENT_TYPE_SCAN_RESULTS_AVAILABLE = 1000,
- EVENT_TYPE_HOTLIST_AP_FOUND = 1001,
- EVENT_TYPE_SIGNIFICANT_WIFI_CHANGE = 1002,
- EVENT_TYPE_RTT_RESULTS = 1003,
- EVENT_TYPE_SCAN_COMPLETE = 1004,
- EVENT_TYPE_HOTLIST_AP_LOST = 1005
-} EventType;
-
-typedef struct {
- int type;
- char buf[256];
-} EventInfo;
-
-const int MAX_EVENTS_IN_CACHE = 256;
-EventInfo eventCache[256];
-int eventsInCache = 0;
-pthread_cond_t eventCacheCondition;
-pthread_mutex_t eventCacheMutex;
-
-void putEventInCache(int type, const char *msg) {
- pthread_mutex_lock(&eventCacheMutex);
- if (eventsInCache + 1 < MAX_EVENTS_IN_CACHE) {
- eventCache[eventsInCache].type = type;
- strcpy(eventCache[eventsInCache].buf, msg);
- eventsInCache++;
- pthread_cond_signal(&eventCacheCondition);
- //printf("put new event in cache; size = %d\n", eventsInCache);
- } else {
- printf("Too many events in the cache\n");
- }
- pthread_mutex_unlock(&eventCacheMutex);
-}
-
-void getEventFromCache(EventInfo& info) {
- pthread_mutex_lock(&eventCacheMutex);
- while (true) {
- if (eventsInCache > 0) {
- //printf("found an event in cache; size = %d\n", eventsInCache);
- info.type = eventCache[0].type;
- strcpy(info.buf, eventCache[0].buf);
- eventsInCache--;
- memmove(&eventCache[0], &eventCache[1], sizeof(EventInfo) * eventsInCache);
- pthread_mutex_unlock(&eventCacheMutex);
- return;
- } else {
- pthread_cond_wait(&eventCacheCondition, &eventCacheMutex);
- //printf("pthread_cond_wait unblocked ...\n");
- }
- }
-}
-
-int numScanResultsAvailable = 0;
-static void onScanResultsAvailable(wifi_request_id id, unsigned num_results) {
- printMsg("Received scan results available event\n");
- numScanResultsAvailable = num_results;
- putEventInCache(EVENT_TYPE_SCAN_RESULTS_AVAILABLE, "New scan results are available");
-}
-
-static void on_scan_event(wifi_scan_event event, unsigned status) {
- if (event == WIFI_SCAN_BUFFER_FULL) {
- printMsg("Received scan complete event - WIFI_SCAN_BUFFER_FULL \n");
- } else if(event == WIFI_SCAN_COMPLETE) {
- printMsg("Received scan complete event - WIFI_SCAN_COMPLETE\n");
- }
-}
-
-static int scanCmdId;
-static int hotlistCmdId;
-static int significantChangeCmdId;
-static int rttCmdId;
-
-static bool startScan( void (*pfnOnResultsAvailable)(wifi_request_id, unsigned),
- int max_ap_per_scan, int base_period, int report_threshold) {
-
- /* Get capabilties */
- wifi_gscan_capabilities capabilities;
- int result = wifi_get_gscan_capabilities(wlan0Handle, &capabilities);
- if (result < 0) {
- printMsg("failed to get scan capabilities - %d\n", result);
- printMsg("trying scan anyway ..\n");
- } else {
- printScanCapabilities(capabilities);
- }
-
- wifi_scan_cmd_params params;
- memset(¶ms, 0, sizeof(params));
-
- if(num_channels > 0){
- params.max_ap_per_scan = max_ap_per_scan;
- params.base_period = base_period; // 5 second by default
- params.report_threshold = report_threshold;
- params.num_buckets = 1;
-
- params.buckets[0].bucket = 0;
- params.buckets[0].band = WIFI_BAND_UNSPECIFIED;
- params.buckets[0].period = base_period;
- params.buckets[0].num_channels = num_channels;
-
- for(int i = 0; i < num_channels; i++){
- params.buckets[0].channels[i].channel = channel_list[i];
- }
-
- } else {
-
- /* create a schedule to scan channels 1, 6, 11 every 5 second and
- * scan 36, 40, 44, 149, 153, 157, 161 165 every 10 second */
-
- params.max_ap_per_scan = max_ap_per_scan;
- params.base_period = base_period; // 5 second
- params.report_threshold = report_threshold;
- params.num_buckets = 3;
-
- params.buckets[0].bucket = 0;
- params.buckets[0].band = WIFI_BAND_UNSPECIFIED;
- params.buckets[0].period = 5000; // 5 second
- params.buckets[0].report_events = 0;
- params.buckets[0].num_channels = 2;
-
- params.buckets[0].channels[0].channel = 2412;
- params.buckets[0].channels[1].channel = 2437;
-
- params.buckets[1].bucket = 1;
- params.buckets[1].band = WIFI_BAND_A;
- params.buckets[1].period = 10000; // 10 second
- params.buckets[1].report_events = 1;
- params.buckets[1].num_channels = 8; // driver should ignore list since band is specified
-
-
- params.buckets[1].channels[0].channel = 5180;
- params.buckets[1].channels[1].channel = 5200;
- params.buckets[1].channels[2].channel = 5220;
- params.buckets[1].channels[3].channel = 5745;
- params.buckets[1].channels[4].channel = 5765;
- params.buckets[1].channels[5].channel = 5785;
- params.buckets[1].channels[6].channel = 5805;
- params.buckets[1].channels[7].channel = 5825;
-
- params.buckets[2].bucket = 2;
- params.buckets[2].band = WIFI_BAND_UNSPECIFIED;
- params.buckets[2].period = 15000; // 15 second
- params.buckets[2].report_events = 2;
- params.buckets[2].num_channels = 1;
-
- params.buckets[2].channels[0].channel = 2462;
-
- }
-
- wifi_scan_result_handler handler;
- memset(&handler, 0, sizeof(handler));
- handler.on_scan_results_available = pfnOnResultsAvailable;
- handler.on_scan_event = on_scan_event;
-
- scanCmdId = getNewCmdId();
- printMsg("Starting scan --->\n");
- return wifi_start_gscan(scanCmdId, wlan0Handle, params, handler) == WIFI_SUCCESS;
-}
-
-static void stopScan() {
- wifi_request_id id = scanCmdId;
- if (id == 0)
- id = -1;
-
- wifi_stop_gscan(id, wlan0Handle);
- scanCmdId = 0;
-}
-
-wifi_scan_result *saved_scan_results;
-unsigned max_saved_scan_results;
-unsigned num_saved_scan_results;
-
-static void on_single_shot_scan_event(wifi_scan_event event, unsigned status) {
- if (event == WIFI_SCAN_BUFFER_FULL) {
- printMsg("Received scan complete event - WIFI_SCAN_BUFFER_FULL \n");
- } else if(event == WIFI_SCAN_COMPLETE) {
- printMsg("Received scan complete event - WIFI_SCAN_COMPLETE\n");
- putEventInCache(EVENT_TYPE_SCAN_COMPLETE, "One scan completed");
- }
-}
-
-static void on_full_scan_result(wifi_request_id id, wifi_scan_result *r) {
- if (num_saved_scan_results < max_saved_scan_results) {
- wifi_scan_result *result = &(saved_scan_results[num_saved_scan_results]);
- memcpy(result, r, sizeof(wifi_scan_result));
- //printMsg("Retrieved full scan result for %s(%02x:%02x:%02x:%02x:%02x:%02x)\n",
- // result->ssid, result->bssid[0], result->bssid[1], result->bssid[2], result->bssid[3],
- // result->bssid[4], result->bssid[5]);
- num_saved_scan_results++;
- }
-}
-
-static int scanOnce(wifi_band band, wifi_scan_result *results, int num_results) {
-
- saved_scan_results = results;
- max_saved_scan_results = num_results;
- num_saved_scan_results = 0;
-
- wifi_scan_cmd_params params;
- memset(¶ms, 0, sizeof(params));
-
- params.max_ap_per_scan = 10;
- params.base_period = 5000; // 5 second by default
- params.report_threshold = 90;
- params.num_buckets = 1;
-
- params.buckets[0].bucket = 0;
- params.buckets[0].band = band;
- params.buckets[0].period = 5000; // 5 second
- params.buckets[0].report_events = 2; // REPORT_EVENTS_AFTER_EACH_SCAN
- params.buckets[0].num_channels = 0;
-
- wifi_scan_result_handler handler;
- memset(&handler, 0, sizeof(handler));
- handler.on_scan_results_available = NULL;
- handler.on_scan_event = on_single_shot_scan_event;
- handler.on_full_scan_result = on_full_scan_result;
-
- int scanCmdId = getNewCmdId();
- printMsg("Starting scan --->\n");
- if (wifi_start_gscan(scanCmdId, wlan0Handle, params, handler) == WIFI_SUCCESS) {
- int events = 0;
- while (true) {
- EventInfo info;
- memset(&info, 0, sizeof(info));
- getEventFromCache(info);
- if (info.type == EVENT_TYPE_SCAN_RESULTS_AVAILABLE
- || info.type == EVENT_TYPE_SCAN_COMPLETE) {
- int retrieved_num_results = num_saved_scan_results;
- if (retrieved_num_results == 0) {
- printMsg("fetched 0 scan results, waiting for more..\n");
- continue;
- } else {
- printMsg("fetched %d scan results\n", retrieved_num_results);
-
- /*
- printScanHeader();
-
- for (int i = 0; i < retrieved_num_results; i++) {
- printScanResult(results[i]);
- }
- */
-
- printMsg("Scan once completed, stopping scan\n");
- wifi_stop_gscan(scanCmdId, wlan0Handle);
- saved_scan_results = NULL;
- max_saved_scan_results = 0;
- num_saved_scan_results = 0;
- return retrieved_num_results;
- }
- }
- }
- } else {
- return 0;
- }
-}
-
-static void retrieveScanResults() {
-
- wifi_scan_result results[256];
- memset(results, 0, sizeof(wifi_scan_result) * 256);
- printMsg("Retrieve Scan results available -->\n");
- int num_results = 256;
- int result = wifi_get_cached_gscan_results(wlan0Handle, 1, num_results, results, &num_results);
- if (result < 0) {
- printMsg("failed to fetch scan results : %d\n", result);
- return;
- } else {
- printMsg("fetched %d scan results\n", num_results);
- }
-
- printScanHeader();
- for (int i = 0; i < num_results; i++) {
- printScanResult(results[i]);
- }
-}
-
-
-static int compareScanResultsByRssi(const void *p1, const void *p2) {
- const wifi_scan_result *result1 = static_cast<const wifi_scan_result *>(p1);
- const wifi_scan_result *result2 = static_cast<const wifi_scan_result *>(p2);
-
- /* RSSI is -ve, so lower one wins */
- if (result1->rssi < result2->rssi) {
- return 1;
- } else if (result1->rssi == result2->rssi) {
- return 0;
- } else {
- return -1;
- }
-}
-
-static void sortScanResultsByRssi(wifi_scan_result *results, int num_results) {
- qsort(results, num_results, sizeof(wifi_scan_result), &compareScanResultsByRssi);
-}
-
-static int removeDuplicateScanResults(wifi_scan_result *results, int num) {
- /* remove duplicates by BSSID */
- int num_results = num;
- for (int i = 0; i < num_results; i++) {
- //printMsg("Processing result[%d] - %02x:%02x:%02x:%02x:%02x:%02x\n", i,
- // results[i].bssid[0], results[i].bssid[1], results[i].bssid[2],
- // results[i].bssid[3], results[i].bssid[4], results[i].bssid[5]);
-
- for (int j = i + 1; j < num_results; ) {
- if (memcmp(results[i].bssid, results[j].bssid, sizeof(mac_addr)) == 0) {
- /* 'remove' this scan result from the list */
- // printMsg("removing dupe entry\n");
- int num_to_move = num_results - j - 1;
- memmove(&results[j], &results[j+1], num_to_move * sizeof(wifi_scan_result));
- num_results--;
- } else {
- j++;
- }
- }
-
- // printMsg("num_results = %d\n", num_results);
- }
-
- return num_results;
-}
-
-static void onRTTResults (wifi_request_id id, unsigned num_results, wifi_rtt_result result[]) {
-
- printMsg("RTT results!!\n");
- printMsg("Addr\t\t\tts\t\tRSSI\tSpread\trtt\tsd\tspread\tdist\tsd\tspread\n");
-
- for (unsigned i = 0; i < num_results; i++) {
- printMsg("%02x:%02x:%02x:%02x:%02x:%02x\t%lld\t%d\t%d\t%lld\t%lld\t%lld\t%d\t%d\t%d\n",
- result[i].addr[0], result[i].addr[1], result[i].addr[2], result[i].addr[3],
- result[i].addr[4], result[i].addr[5], result[i].ts, result[i].rssi,
- result[i].rssi_spread, result[i].rtt, result[i].rtt_sd, result[i].rtt_spread,
- result[i].distance, result[i].distance_sd, result[i].distance_spread);
- }
-
- putEventInCache(EVENT_TYPE_RTT_RESULTS, "RTT results");
-}
-
-static void onHotlistAPFound(wifi_request_id id, unsigned num_results, wifi_scan_result *results) {
-
- printMsg("Found hotlist APs\n");
- for (unsigned i = 0; i < num_results; i++) {
- printScanResult(results[i]);
- }
- putEventInCache(EVENT_TYPE_HOTLIST_AP_FOUND, "Found a hotlist AP");
-}
-
-static void onHotlistAPLost(wifi_request_id id, unsigned num_results, wifi_scan_result *results) {
-
- printMsg("Lost hotlist APs\n");
- for (unsigned i = 0; i < num_results; i++) {
- printScanResult(results[i]);
- }
- putEventInCache(EVENT_TYPE_HOTLIST_AP_LOST, "Lost event Hotlist APs");
-}
-
-static void testRTT() {
-
- wifi_scan_result results[256];
- int num_results = scanOnce(WIFI_BAND_ABG, results, countof(results));
- if (num_results == 0) {
- printMsg("RTT aborted because of no scan results\n");
- return;
- } else {
- printMsg("Retrieved %d scan results\n", num_results);
- }
-
- num_results = removeDuplicateScanResults(results, num_results);
- /*
- printMsg("Deduped scan results - %d\n", num_results);
- for (int i = 0; i < num_results; i++) {
- printScanResult(results[i]);
- }
- */
-
- sortScanResultsByRssi(results, num_results);
- printMsg("Sorted scan results -\n");
- for (int i = 0; i < num_results; i++) {
- printScanResult(results[i]);
- }
-
-
- static const int max_ap = 5;
- wifi_rtt_config params[max_ap];
- memset(params, 0, sizeof(params));
-
- printMsg("Configuring RTT for %d APs, num_samples = %d\n",
- min(num_results, max_ap), rtt_samples);
-
- unsigned num_ap = 0;
- for (int i = 0; i < min(num_results, max_ap); i++, num_ap++) {
-
- memcpy(params[i].addr, results[i].bssid, sizeof(mac_addr));
- mac_addr &addr = params[i].addr;
- printMsg("Adding %02x:%02x:%02x:%02x:%02x:%02x (%d) for RTT\n", addr[0],
- addr[1], addr[2], addr[3], addr[4], addr[5], results[i].channel);
-
- params[i].type = RTT_TYPE_1_SIDED;
- params[i].channel.center_freq = results[i].channel;
- params[i].channel.width = WIFI_CHAN_WIDTH_20;
- params[i].peer = WIFI_PEER_INVALID;
- params[i].continuous = 1;
- params[i].interval = 1000;
- params[i].num_samples_per_measurement = rtt_samples;
- params[i].num_retries_per_measurement = 10;
- }
-
- wifi_rtt_event_handler handler;
- handler.on_rtt_results = &onRTTResults;
-
- int result = wifi_rtt_range_request(rttCmdId, wlan0Handle, num_ap, params, handler);
-
- if (result == WIFI_SUCCESS) {
- printMsg("Waiting for RTT results\n");
-
- while (true) {
- EventInfo info;
- memset(&info, 0, sizeof(info));
- getEventFromCache(info);
-
- if (info.type == EVENT_TYPE_SCAN_RESULTS_AVAILABLE) {
- retrieveScanResults();
- } else if (info.type == EVENT_TYPE_RTT_RESULTS) {
- break;
- }
- }
- } else {
- printMsg("Could not set setRTTAPs : %d\n", result);
- }
-}
-
-
-static wifi_error setHotlistAPsUsingScanResult(wifi_bssid_hotlist_params *params){
- printMsg("testHotlistAPs Scan started, waiting for event ...\n");
- EventInfo info;
- memset(&info, 0, sizeof(info));
- getEventFromCache(info);
-
- wifi_scan_result results[256];
- memset(results, 0, sizeof(wifi_scan_result) * 256);
-
- printMsg("Retrieving scan results for Hotlist AP setting\n");
- int num_results = 256;
- int result = wifi_get_cached_gscan_results(wlan0Handle, 1, num_results, results, &num_results);
- if (result < 0) {
- printMsg("failed to fetch scan results : %d\n", result);
- return WIFI_ERROR_UNKNOWN;
- } else {
- printMsg("fetched %d scan results\n", num_results);
- }
-
- for (int i = 0; i < num_results; i++) {
- printScanResult(results[i]);
- }
-
- for (int i = 0; i < stest_max_ap; i++) {
- memcpy(params->ap[i].bssid, results[i].bssid, sizeof(mac_addr));
- params->ap[i].low = -htest_low_threshold;
- params->ap[i].high = -htest_high_threshold;
- }
- params->num_ap = stest_max_ap;
- return WIFI_SUCCESS;
-}
-
-static wifi_error setHotlistAPs() {
- wifi_bssid_hotlist_params params;
- memset(¶ms, 0, sizeof(params));
-
- params.lost_ap_sample_size = HOTLIST_LOST_WINDOW;
- if (num_hotlist_bssids > 0) {
- for (int i = 0; i < num_hotlist_bssids; i++) {
- memcpy(params.ap[i].bssid, hotlist_bssids[i], sizeof(mac_addr));
- params.ap[i].low = -htest_low_threshold;
- params.ap[i].high = -htest_high_threshold;
- }
- params.num_ap = num_hotlist_bssids;
- } else {
- setHotlistAPsUsingScanResult(¶ms);
- }
-
- printMsg("BSSID\t\t\tHIGH\tLOW\n");
- for (int i = 0; i < params.num_ap; i++) {
- mac_addr &addr = params.ap[i].bssid;
- printMsg("%02x:%02x:%02x:%02x:%02x:%02x\t%d\t%d\n", addr[0],
- addr[1], addr[2], addr[3], addr[4], addr[5],
- params.ap[i].high, params.ap[i].low);
- }
-
- wifi_hotlist_ap_found_handler handler;
- handler.on_hotlist_ap_found = &onHotlistAPFound;
- handler.on_hotlist_ap_lost = &onHotlistAPLost;
- hotlistCmdId = getNewCmdId();
- printMsg("Setting hotlist APs threshold\n");
- return wifi_set_bssid_hotlist(hotlistCmdId, wlan0Handle, params, handler);
-}
-
-static void resetHotlistAPs() {
- printMsg(", stoping Hotlist AP scanning\n");
- wifi_reset_bssid_hotlist(hotlistCmdId, wlan0Handle);
-}
-
-static void setPnoMacOui() {
- wifi_set_scanning_mac_oui(wlan0Handle, mac_oui);
-}
-
-static void testHotlistAPs(){
-
- EventInfo info;
- memset(&info, 0, sizeof(info));
-
- printMsg("starting Hotlist AP scanning\n");
- if (!startScan(&onScanResultsAvailable, stest_max_ap,stest_base_period, stest_threshold)) {
- printMsg("testHotlistAPs failed to start scan!!\n");
- return;
- }
-
- int result = setHotlistAPs();
- if (result == WIFI_SUCCESS) {
- printMsg("Waiting for Hotlist AP event\n");
- while (true) {
- memset(&info, 0, sizeof(info));
- getEventFromCache(info);
-
- if (info.type == EVENT_TYPE_SCAN_RESULTS_AVAILABLE) {
- retrieveScanResults();
- } else if (info.type == EVENT_TYPE_HOTLIST_AP_FOUND ||
- info.type == EVENT_TYPE_HOTLIST_AP_LOST) {
- printMsg("Hotlist APs");
- if (--max_event_wait > 0)
- printMsg(", waiting for more event ::%d\n", max_event_wait);
- else
- break;
- }
- }
- resetHotlistAPs();
- } else {
- printMsg("Could not set AP hotlist : %d\n", result);
- }
-}
-
-static void onSignificantWifiChange(wifi_request_id id,
- unsigned num_results, wifi_significant_change_result **results)
-{
- printMsg("Significant wifi change for %d\n", num_results);
- for (unsigned i = 0; i < num_results; i++) {
- printSignificantChangeResult(results[i]);
- }
- putEventInCache(EVENT_TYPE_SIGNIFICANT_WIFI_CHANGE, "significant wifi change noticed");
-}
-
-static int SelectSignificantAPsFromScanResults() {
- wifi_scan_result results[256];
- memset(results, 0, sizeof(wifi_scan_result) * 256);
- printMsg("Retrieving scan results for significant wifi change setting\n");
- int num_results = 256;
- int result = wifi_get_cached_gscan_results(wlan0Handle, 1, num_results, results, &num_results);
- if (result < 0) {
- printMsg("failed to fetch scan results : %d\n", result);
- return WIFI_ERROR_UNKNOWN;
- } else {
- printMsg("fetched %d scan results\n", num_results);
- }
-
- for (int i = 0; i < num_results; i++) {
- printScanResult(results[i]);
- }
-
- wifi_significant_change_params params;
- memset(¶ms, 0, sizeof(params));
-
- params.rssi_sample_size = swctest_rssi_sample_size;
- params.lost_ap_sample_size = swctest_rssi_lost_ap;
- params.min_breaching = swctest_rssi_min_breaching;
-
- for (int i = 0; i < stest_max_ap; i++) {
- memcpy(params.ap[i].bssid, results[i].bssid, sizeof(mac_addr));
- params.ap[i].low = results[i].rssi - swctest_rssi_ch_threshold;
- params.ap[i].high = results[i].rssi + swctest_rssi_ch_threshold;
- }
- params.num_ap = stest_max_ap;
-
- printMsg("Settting Significant change params rssi_sample_size#%d lost_ap_sample_size#%d"
- " and min_breaching#%d\n", params.rssi_sample_size,
- params.lost_ap_sample_size , params.min_breaching);
- printMsg("BSSID\t\t\tHIGH\tLOW\n");
- for (int i = 0; i < params.num_ap; i++) {
- mac_addr &addr = params.ap[i].bssid;
- printMsg("%02x:%02x:%02x:%02x:%02x:%02x\t%d\t%d\n", addr[0],
- addr[1], addr[2], addr[3], addr[4], addr[5],
- params.ap[i].high, params.ap[i].low);
- }
- wifi_significant_change_handler handler;
- memset(&handler, 0, sizeof(handler));
- handler.on_significant_change = &onSignificantWifiChange;
-
- int id = getNewCmdId();
- return wifi_set_significant_change_handler(id, wlan0Handle, params, handler);
-
-}
-
-static void untrackSignificantChange() {
- printMsg(", Stop tracking SignificantChange\n");
- wifi_reset_bssid_hotlist(hotlistCmdId, wlan0Handle);
-}
-
-static void trackSignificantChange() {
- printMsg("starting trackSignificantChange\n");
-
- if (!startScan(&onScanResultsAvailable, stest_max_ap,stest_base_period, stest_threshold)) {
- printMsg("trackSignificantChange failed to start scan!!\n");
- return;
- } else {
- printMsg("trackSignificantChange Scan started, waiting for event ...\n");
- }
-
- EventInfo info;
- memset(&info, 0, sizeof(info));
- getEventFromCache(info);
-
- int result = SelectSignificantAPsFromScanResults();
- if (result == WIFI_SUCCESS) {
- printMsg("Waiting for significant wifi change event\n");
- while (true) {
- memset(&info, 0, sizeof(info));
- getEventFromCache(info);
-
- if (info.type == EVENT_TYPE_SCAN_RESULTS_AVAILABLE) {
- retrieveScanResults();
- } else if(info.type == EVENT_TYPE_SIGNIFICANT_WIFI_CHANGE) {
- printMsg("Received significant wifi change");
- if (--max_event_wait > 0)
- printMsg(", waiting for more event ::%d\n", max_event_wait);
- else
- break;
- }
- }
- untrackSignificantChange();
- } else {
- printMsg("Failed to set significant change ::%d\n", result);
- }
-}
-
-/* ------------------------------------------- */
-/* tests */
-/* ------------------------------------------- */
-
-void testScan() {
- printf("starting scan with max_ap_per_scan#%d base_period#%d threshold#%d \n",
- stest_max_ap,stest_base_period, stest_threshold);
- if (!startScan(&onScanResultsAvailable, stest_max_ap,stest_base_period, stest_threshold)) {
- printMsg("failed to start scan!!\n");
- return;
- } else {
- EventInfo info;
- memset(&info, 0, sizeof(info));
-
- while (true) {
- getEventFromCache(info);
- printMsg("retrieved event %d : %s\n", info.type, info.buf);
- retrieveScanResults();
- if(--max_event_wait > 0)
- printMsg("Waiting for more :: %d event \n", max_event_wait);
- else
- break;
- }
-
- stopScan();
- printMsg("stopped scan\n");
- }
-}
-
-void testStopScan() {
- stopScan();
- printMsg("stopped scan\n");
-}
-
-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 {
- printMsg("invalid character in bssid %c\n", ch);
- return 0;
- }
-}
-
-byte parseHexByte(char ch1, char ch2) {
- return (parseHexChar(ch1) << 4) | parseHexChar(ch2);
-}
-
-void parseMacAddress(const char *str, mac_addr addr) {
- addr[0] = parseHexByte(str[0], str[1]);
- addr[1] = parseHexByte(str[3], str[4]);
- addr[2] = parseHexByte(str[6], str[7]);
- addr[3] = parseHexByte(str[9], str[10]);
- addr[4] = parseHexByte(str[12], str[13]);
- addr[5] = parseHexByte(str[15], str[16]);
- // printMsg("read mac addr: %02x:%02x:%02x:%02x:%02x:%02x\n", addr[0],
- // addr[1], addr[2], addr[3], addr[4], addr[5]);
-}
-
-void parseMacOUI(char *str, unsigned char *addr) {
- addr[0] = parseHexByte(str[0], str[1]);
- addr[1] = parseHexByte(str[3], str[4]);
- addr[2] = parseHexByte(str[6], str[7]);
- printMsg("read mac OUI: %02x:%02x:%02x\n", addr[0],
- addr[1], addr[2]);
-}
-
-void readTestOptions(int argc, char *argv[]){
-
- printf("Total number of argc #%d\n", argc);
- for (int j = 1; j < argc-1; j++) {
- if (strcmp(argv[j], "-max_ap") == 0 && isdigit(argv[j+1][0])) {
- stest_max_ap = atoi(argv[++j]);
- printf(" max_ap #%d\n", stest_max_ap);
- } else if (strcmp(argv[j], "-base_period") == 0 && isdigit(argv[j+1][0])) {
- stest_base_period = atoi(argv[++j]);
- printf(" base_period #%d\n", stest_base_period);
- } else if (strcmp(argv[j], "-threshold") == 0 && isdigit(argv[j+1][0])) {
- stest_threshold = atoi(argv[++j]);
- printf(" threshold #%d\n", stest_threshold);
- } else if (strcmp(argv[j], "-avg_RSSI") == 0 && isdigit(argv[j+1][0])) {
- swctest_rssi_sample_size = atoi(argv[++j]);
- printf(" avg_RSSI #%d\n", swctest_rssi_sample_size);
- } else if (strcmp(argv[j], "-ap_loss") == 0 && isdigit(argv[j+1][0])) {
- swctest_rssi_lost_ap = atoi(argv[++j]);
- printf(" ap_loss #%d\n", swctest_rssi_lost_ap);
- } else if (strcmp(argv[j], "-ap_breach") == 0 && isdigit(argv[j+1][0])) {
- swctest_rssi_min_breaching = atoi(argv[++j]);
- printf(" ap_breach #%d\n", swctest_rssi_min_breaching);
- } else if (strcmp(argv[j], "-ch_threshold") == 0 && isdigit(argv[j+1][0])) {
- swctest_rssi_ch_threshold = atoi(argv[++j]);
- printf(" ch_threshold #%d\n", swctest_rssi_ch_threshold);
- } else if (strcmp(argv[j], "-wt_event") == 0 && isdigit(argv[j+1][0])) {
- max_event_wait = atoi(argv[++j]);
- printf(" wt_event #%d\n", max_event_wait);
- } else if (strcmp(argv[j], "-low_th") == 0 && isdigit(argv[j+1][0])) {
- htest_low_threshold = atoi(argv[++j]);
- printf(" low_threshold #-%d\n", htest_low_threshold);
- } else if (strcmp(argv[j], "-high_th") == 0 && isdigit(argv[j+1][0])) {
- htest_high_threshold = atoi(argv[++j]);
- printf(" high_threshold #-%d\n", htest_high_threshold);
- } else if (strcmp(argv[j], "-hotlist_bssids") == 0 && isxdigit(argv[j+1][0])) {
- j++;
- for (num_hotlist_bssids = 0;
- j < argc && isxdigit(argv[j][0]);
- j++, num_hotlist_bssids++) {
- parseMacAddress(argv[j], hotlist_bssids[num_hotlist_bssids]);
- }
- j -= 1;
- } else if (strcmp(argv[j], "-channel_list") == 0 && isxdigit(argv[j+1][0])) {
- j++;
- for (num_channels = 0; j < argc && isxdigit(argv[j][0]); j++, num_channels++) {
- channel_list[num_channels] = atoi(argv[j]);
- }
- j -= 1;
- } else if ((strcmp(argv[j], "-get_ch_list") == 0)) {
- if(strcmp(argv[j + 1], "a") == 0) {
- band = WIFI_BAND_A_WITH_DFS;
- } else if(strcmp(argv[j + 1], "bg") == 0) {
- band = WIFI_BAND_BG;
- } else if(strcmp(argv[j + 1], "abg") == 0) {
- band = WIFI_BAND_ABG_WITH_DFS;
- } else if(strcmp(argv[j + 1], "a_nodfs") == 0) {
- band = WIFI_BAND_A;
- } else if(strcmp(argv[j + 1], "dfs") == 0) {
- band = WIFI_BAND_A_DFS;
- } else if(strcmp(argv[j + 1], "abg_nodfs") == 0) {
- band = WIFI_BAND_ABG;
- }
- j++;
- } else if ((strcmp(argv[j], "-rtt_samples") == 0)) {
- rtt_samples = atoi(argv[++j]);
- printf(" rtt_retries #-%d\n", rtt_samples);
- } else if (strcmp(argv[j], "-scan_mac_oui") == 0 && isxdigit(argv[j+1][0])) {
- parseMacOUI(argv[++j], mac_oui);
- }
- }
-}
-
-wifi_iface_stat link_stat;
-wifi_radio_stat trx_stat;
-wifi_peer_info peer_info;
-wifi_rate_stat rate_stat[32];
-void onLinkStatsResults(wifi_request_id id, wifi_iface_stat *iface_stat,
- int num_radios, wifi_radio_stat *radio_stat)
-{
- int num_peer = iface_stat->num_peers;
- memcpy(&trx_stat, radio_stat, sizeof(wifi_radio_stat));
- memcpy(&link_stat, iface_stat, sizeof(wifi_iface_stat));
- memcpy(&peer_info, iface_stat->peer_info, num_peer*sizeof(wifi_peer_info));
- int num_rate = peer_info.num_rate;
- memcpy(&rate_stat, iface_stat->peer_info->rate_stats, num_rate*sizeof(wifi_rate_stat));
-}
-
-void printFeatureListBitMask(void)
-{
- printMsg("WIFI_FEATURE_INFRA 0x0001 - Basic infrastructure mode\n");
- printMsg("WIFI_FEATURE_INFRA_5G 0x0002 - Support for 5 GHz Band\n");
- printMsg("WIFI_FEATURE_HOTSPOT 0x0004 - Support for GAS/ANQP\n");
- printMsg("WIFI_FEATURE_P2P 0x0008 - Wifi-Direct\n");
- printMsg("WIFI_FEATURE_SOFT_AP 0x0010 - Soft AP\n");
- printMsg("WIFI_FEATURE_GSCAN 0x0020 - Google-Scan APIs\n");
- printMsg("WIFI_FEATURE_NAN 0x0040 - Neighbor Awareness Networking\n");
- printMsg("WIFI_FEATURE_D2D_RTT 0x0080 - Device-to-device RTT\n");
- printMsg("WIFI_FEATURE_D2AP_RTT 0x0100 - Device-to-AP RTT\n");
- printMsg("WIFI_FEATURE_BATCH_SCAN 0x0200 - Batched Scan (legacy)\n");
- printMsg("WIFI_FEATURE_PNO 0x0400 - Preferred network offload\n");
- printMsg("WIFI_FEATURE_ADDITIONAL_STA 0x0800 - Support for two STAs\n");
- printMsg("WIFI_FEATURE_TDLS 0x1000 - Tunnel directed link setup\n");
- printMsg("WIFI_FEATURE_TDLS_OFFCHANNEL 0x2000 - Support for TDLS off channel\n");
- printMsg("WIFI_FEATURE_EPR 0x4000 - Enhanced power reporting\n");
- printMsg("WIFI_FEATURE_AP_STA 0x8000 - Support for AP STA Concurrency\n");
-}
-
-char *rates[] = {
- "1Mbps",
- "2Mbps",
- "5.5Mbps",
- "6Mbps",
- "9Mbps",
- "11Mbps",
- "12Mbps",
- "18Mbps",
- "24Mbps",
- "36Mbps",
- "48Mbps",
- "54Mbps",
- "VHT MCS0 ss1",
- "VHT MCS1 ss1",
- "VHT MCS2 ss1",
- "VHT MCS3 ss1",
- "VHT MCS4 ss1",
- "VHT MCS5 ss1",
- "VHT MCS6 ss1",
- "VHT MCS7 ss1",
- "VHT MCS8 ss1",
- "VHT MCS9 ss1",
- "VHT MCS0 ss2",
- "VHT MCS1 ss2",
- "VHT MCS2 ss2",
- "VHT MCS3 ss2",
- "VHT MCS4 ss2",
- "VHT MCS5 ss2",
- "VHT MCS6 ss2",
- "VHT MCS7 ss2",
- "VHT MCS8 ss2",
- "VHT MCS9 ss2"
- };
-
-void printLinkStats(wifi_iface_stat link_stat, wifi_radio_stat trx_stat)
-{
- printMsg("Printing link layer statistics:\n");
- printMsg("-------------------------------\n");
- printMsg("beacon_rx = %d\n", link_stat.beacon_rx);
- printMsg("RSSI = %d\n", link_stat.rssi_mgmt);
- printMsg("AC_BE:\n");
- printMsg("txmpdu = %d\n", link_stat.ac[WIFI_AC_BE].tx_mpdu);
- printMsg("rxmpdu = %d\n", link_stat.ac[WIFI_AC_BE].rx_mpdu);
- printMsg("mpdu_lost = %d\n", link_stat.ac[WIFI_AC_BE].mpdu_lost);
- printMsg("retries = %d\n", link_stat.ac[WIFI_AC_BE].retries);
- printMsg("AC_BK:\n");
- printMsg("txmpdu = %d\n", link_stat.ac[WIFI_AC_BK].tx_mpdu);
- printMsg("rxmpdu = %d\n", link_stat.ac[WIFI_AC_BK].rx_mpdu);
- printMsg("mpdu_lost = %d\n", link_stat.ac[WIFI_AC_BK].mpdu_lost);
- printMsg("AC_VI:\n");
- printMsg("txmpdu = %d\n", link_stat.ac[WIFI_AC_VI].tx_mpdu);
- printMsg("rxmpdu = %d\n", link_stat.ac[WIFI_AC_VI].rx_mpdu);
- printMsg("mpdu_lost = %d\n", link_stat.ac[WIFI_AC_VI].mpdu_lost);
- printMsg("AC_VO:\n");
- printMsg("txmpdu = %d\n", link_stat.ac[WIFI_AC_VO].tx_mpdu);
- printMsg("rxmpdu = %d\n", link_stat.ac[WIFI_AC_VO].rx_mpdu);
- printMsg("mpdu_lost = %d\n", link_stat.ac[WIFI_AC_VO].mpdu_lost);
- printMsg("\n");
- printMsg("Printing radio statistics:\n");
- printMsg("--------------------------\n");
- printMsg("on time = %d\n", trx_stat.on_time);
- printMsg("tx time = %d\n", trx_stat.tx_time);
- printMsg("rx time = %d\n", trx_stat.rx_time);
- printMsg("\n");
- printMsg("Printing rate statistics:\n");
- printMsg("-------------------------\n");
- printMsg("%27s %12s %14s %15s\n", "TX", "RX", "LOST", "RETRIES");
- for (int i=0; i < 32; i++) {
- printMsg("%-15s %10d %10d %10d %10d\n",
- rates[i], rate_stat[i].tx_mpdu, rate_stat[i].rx_mpdu,
- rate_stat[i].mpdu_lost, rate_stat[i].retries);
- }
-}
-
-void getLinkStats(void)
-{
- wifi_stats_result_handler handler;
- memset(&handler, 0, sizeof(handler));
- handler.on_link_stats_results = &onLinkStatsResults;
-
- int result = wifi_get_link_stats(0, wlan0Handle, handler);
- if (result < 0) {
- printMsg("failed to get link statistics - %d\n", result);
- } else {
- printLinkStats(link_stat, trx_stat);
- }
-}
-
-void getChannelList(void)
-{
- wifi_channel channel[MAX_CH_BUF_SIZE];
- int num_channels = 0, i;
-
- int result = wifi_get_valid_channels(wlan0Handle, band, MAX_CH_BUF_SIZE,
- channel, &num_channels);
- printMsg("Number of channels - %d\nChannel List:\n",num_channels);
- for (i = 0; i < num_channels; i++) {
- printMsg("%d MHz\n", channel[i]);
- }
-}
-
-void getFeatureSet(void)
-{
- feature_set set;
- int result = wifi_get_supported_feature_set(wlan0Handle, &set);
-
- if (result < 0) {
- printMsg("Error %d\n",result);
- return;
- }
- printFeatureListBitMask();
- printMsg("Supported feature set bit mask - %x\n", set);
- return;
-}
-
-void getFeatureSetMatrix(void)
-{
- feature_set set[MAX_FEATURE_SET];
- int size;
-
- int result = wifi_get_concurrency_matrix(wlan0Handle, MAX_FEATURE_SET, set, &size);
-
- if (result < 0) {
- printMsg("Error %d\n",result);
- return;
- }
- printFeatureListBitMask();
- for (int i = 0; i < size; i++)
- printMsg("Concurrent feature set - %x\n", set[i]);
- return;
-}
-
-
-
-int main(int argc, char *argv[]) {
-
- pthread_mutex_init(&printMutex, NULL);
-
- if (init() != 0) {
- printMsg("could not initiate HAL");
- return -1;
- } else {
- printMsg("successfully initialized HAL; wlan0 = %p\n", wlan0Handle);
- }
-
- pthread_cond_init(&eventCacheCondition, NULL);
- pthread_mutex_init(&eventCacheMutex, NULL);
-
- pthread_t tidEvent;
- pthread_create(&tidEvent, NULL, &eventThreadFunc, NULL);
-
- sleep(2); // let the thread start
-
- if (argc < 2 || argv[1][0] != '-') {
- printf("Usage: halutil [OPTION]\n");
- printf(" -s start AP scan test\n");
- printf(" -swc start Significant Wifi change test\n");
- printf(" -h start Hotlist APs scan test\n");
- printf(" -ss stop scan test\n");
- printf(" -max_ap Max AP for scan \n");
- printf(" -base_period Base period for scan \n");
- printf(" -threshold Threshold scan test\n");
- printf(" -avg_RSSI samples for averaging RSSI\n");
- printf(" -ap_loss samples to confirm AP loss\n");
- printf(" -ap_breach APs breaching threshold\n");
- printf(" -ch_threshold Change in threshold\n");
- printf(" -wt_event Waiting event for test\n");
- printf(" -low_th Low threshold for hotlist APs\n");
- printf(" -hight_th High threshold for hotlist APs\n");
- printf(" -hotlist_bssids BSSIDs for hotlist test\n");
- printf(" -stats print link layer statistics\n");
- printf(" -get_ch_list <a/bg/abg/a_nodfs/abg_nodfs/dfs> Get channel list\n");
- printf(" -get_feature_set Get Feature set\n");
- printf(" -get_feature_matrix Get concurrent feature matrix\n");
- printf(" -rtt Run RTT on nearby APs\n");
- printf(" -rtt_samples Run RTT on nearby APs\n");
- printf(" -scan_mac_oui XY:AB:CD\n");
- printf(" -nodfs <0|1> Turn OFF/ON non-DFS locales\n");
- goto cleanup;
- }
- memset(mac_oui, 0, 3);
-
- if (strcmp(argv[1], "-s") == 0) {
- readTestOptions(argc, argv);
- setPnoMacOui();
- testScan();
- }else if(strcmp(argv[1], "-swc") == 0){
- readTestOptions(argc, argv);
- setPnoMacOui();
- trackSignificantChange();
- }else if (strcmp(argv[1], "-ss") == 0) {
- // Stop scan so clear the OUI too
- setPnoMacOui();
- testStopScan();
- }else if ((strcmp(argv[1], "-h") == 0) ||
- (strcmp(argv[1], "-hotlist_bssids") == 0)) {
- readTestOptions(argc, argv);
- setPnoMacOui();
- testHotlistAPs();
- }else if (strcmp(argv[1], "-stats") == 0) {
- getLinkStats();
- } else if ((strcmp(argv[1], "-rtt") == 0)) {
- readTestOptions(argc, argv);
- testRTT();
- } else if ((strcmp(argv[1], "-get_ch_list") == 0)) {
- readTestOptions(argc, argv);
- getChannelList();
- } else if ((strcmp(argv[1], "-get_feature_set") == 0)) {
- getFeatureSet();
- } else if ((strcmp(argv[1], "-get_feature_matrix") == 0)) {
- getFeatureSetMatrix();
- } else if ((strcmp(argv[1], "-scan_mac_oui") == 0)) {
- readTestOptions(argc, argv);
- setPnoMacOui();
- testScan();
- } else if (strcmp(argv[1], "-nodfs") == 0) {
- u32 nodfs = 0;
- if (argc > 2)
- nodfs = (u32)atoi(argv[2]);
- wifi_set_nodfs_flag(wlan0Handle, nodfs);
- }
-cleanup:
- cleanup();
- return 0;
-}