Merge "Check roaming network for Fast Re-Authentication"
am: c707c6d7fb -s ours
Change-Id: Ia8dddaa6dc26416e725a358d2a2c380d9382f237
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..ca79125
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,18 @@
+// Copyright (C) 2017 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.
+
+subdirs = [
+ "libwifi_system",
+ "libwifi_system_iface",
+]
diff --git a/libwifi_system/Android.bp b/libwifi_system/Android.bp
new file mode 100644
index 0000000..877d05f
--- /dev/null
+++ b/libwifi_system/Android.bp
@@ -0,0 +1,76 @@
+// Copyright (C) 2016 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.
+
+cc_defaults {
+ name: "libwifi-system-defaults",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ "-Winit-self",
+ "-Wno-unused-function",
+ "-Wno-unused-parameter",
+ "-Wshadow",
+ "-Wunused-variable",
+ "-Wwrite-strings",
+ ],
+}
+
+// Device independent wifi system logic.
+// ============================================================
+cc_library_shared {
+ name: "libwifi-system",
+ defaults: ["libwifi-system-defaults"],
+ export_include_dirs: ["include"],
+ export_shared_lib_headers: ["libbase"],
+ shared_libs: [
+ "libbase",
+ "libcrypto",
+ "libcutils",
+ ],
+ srcs: [
+ "hostapd_manager.cpp",
+ "supplicant_manager.cpp",
+ ],
+}
+
+// Test utilities (e.g. mock classes) for libwifi-system
+// ============================================================
+cc_library_static {
+ name: "libwifi-system-test",
+ defaults: ["libwifi-system-defaults"],
+ static_libs: ["libgmock"],
+ export_include_dirs: [
+ "include",
+ "testlib/include",
+ ],
+}
+
+// Unit tests for libwifi-system
+// ============================================================
+cc_test {
+ name: "libwifi-system_tests",
+ defaults: ["libwifi-system-defaults"],
+ srcs: [
+ "tests/main.cpp",
+ "tests/hostapd_manager_unittest.cpp",
+ ],
+ static_libs: [
+ "libgmock",
+ ],
+ shared_libs: [
+ "libbase",
+ "libwifi-system",
+ ],
+}
diff --git a/libwifi_system/Android.mk b/libwifi_system/Android.mk
deleted file mode 100644
index 5541867..0000000
--- a/libwifi_system/Android.mk
+++ /dev/null
@@ -1,76 +0,0 @@
-# Copyright (C) 2016 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.
-
-LOCAL_PATH := $(call my-dir)
-
-wifi_system_cflags := \
- -Wall \
- -Werror \
- -Wextra \
- -Winit-self \
- -Wno-unused-function \
- -Wno-unused-parameter \
- -Wshadow \
- -Wunused-variable \
- -Wwrite-strings
-
-# Device independent wifi system logic.
-# ============================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := libwifi-system
-LOCAL_CFLAGS := $(wifi_system_cflags)
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libbase
-LOCAL_SHARED_LIBRARIES := \
- libbase \
- libcrypto \
- libcutils
-
-LOCAL_SRC_FILES := \
- hostapd_manager.cpp \
- interface_tool.cpp \
- supplicant_manager.cpp
-include $(BUILD_SHARED_LIBRARY)
-
-# Test utilities (e.g. mock classes) for libwifi-system
-# ============================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := libwifi-system-test
-LOCAL_CFLAGS := $(wifi_system_cflags)
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/include \
- $(LOCAL_PATH)/testlib/include
-LOCAL_STATIC_LIBRARIES := libgmock
-LOCAL_EXPORT_C_INCLUDE_DIRS := \
- $(LOCAL_PATH)/include \
- $(LOCAL_PATH)/testlib/include
-include $(BUILD_STATIC_LIBRARY)
-
-
-# Unit tests for libwifi-system
-# ============================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := libwifi-system_tests
-LOCAL_CPPFLAGS := $(wificond_cpp_flags)
-LOCAL_SRC_FILES := \
- tests/main.cpp \
- tests/hostapd_manager_unittest.cpp
-LOCAL_STATIC_LIBRARIES := \
- libgmock \
- libgtest
-LOCAL_SHARED_LIBRARIES := \
- libbase \
- libwifi-system
-include $(BUILD_NATIVE_TEST)
diff --git a/libwifi_system/testlib/include/wifi_system_test/mock_hal_tool.h b/libwifi_system/testlib/include/wifi_system_test/mock_hal_tool.h
index 312428f..eb93e26 100644
--- a/libwifi_system/testlib/include/wifi_system_test/mock_hal_tool.h
+++ b/libwifi_system/testlib/include/wifi_system_test/mock_hal_tool.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_WIFI_SYSTEM_TEST_MOCK_HAL_TOOL_H
#define ANDROID_WIFI_SYSTEM_TEST_MOCK_HAL_TOOL_H
+#include <gmock/gmock.h>
#include <wifi_system/hal_tool.h>
namespace android {
diff --git a/libwifi_system/testlib/include/wifi_system_test/mock_hostapd_manager.h b/libwifi_system/testlib/include/wifi_system_test/mock_hostapd_manager.h
index 6dbeafe..94ed41b 100644
--- a/libwifi_system/testlib/include/wifi_system_test/mock_hostapd_manager.h
+++ b/libwifi_system/testlib/include/wifi_system_test/mock_hostapd_manager.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_WIFI_SYSTEM_TEST_MOCK_HOSTAPD_MANAGER_H
#define ANDROID_WIFI_SYSTEM_TEST_MOCK_HOSTAPD_MANAGER_H
+#include <gmock/gmock.h>
#include <wifi_system/hostapd_manager.h>
namespace android {
diff --git a/libwifi_system/testlib/include/wifi_system_test/mock_supplicant_manager.h b/libwifi_system/testlib/include/wifi_system_test/mock_supplicant_manager.h
index 01d604f..d55012e 100644
--- a/libwifi_system/testlib/include/wifi_system_test/mock_supplicant_manager.h
+++ b/libwifi_system/testlib/include/wifi_system_test/mock_supplicant_manager.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_WIFI_SYSTEM_TEST_MOCK_SUPPLICANT_MANAGER_H
#define ANDROID_WIFI_SYSTEM_TEST_MOCK_SUPPLICANT_MANAGER_H
+#include <gmock/gmock.h>
#include <wifi_system/supplicant_manager.h>
namespace android {
diff --git a/libwifi_system_iface/Android.bp b/libwifi_system_iface/Android.bp
new file mode 100644
index 0000000..4be0aa0
--- /dev/null
+++ b/libwifi_system_iface/Android.bp
@@ -0,0 +1,58 @@
+// Copyright (C) 2017 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.
+
+wifi_system_iface_cflags = [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ "-Winit-self",
+ "-Wno-unused-function",
+ "-Wno-unused-parameter",
+ "-Wshadow",
+ "-Wunused-variable",
+ "-Wwrite-strings",
+]
+
+// Device independent wifi system logic.
+// ============================================================
+cc_library_shared {
+ name: "libwifi-system-iface",
+ vendor_available: true,
+ cflags: wifi_system_iface_cflags,
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+ shared_libs: [
+ "libbase",
+ ],
+
+ srcs: [
+ "interface_tool.cpp",
+ ],
+}
+
+// Test utilities (e.g. mock classes) for libwifi-system-iface
+// ============================================================
+cc_library_static {
+ name: "libwifi-system-iface-test",
+ cflags: wifi_system_iface_cflags,
+ local_include_dirs: [
+ "include",
+ "testlib/include",
+ ],
+ static_libs: ["libgmock"],
+ export_include_dirs: [
+ "include",
+ "testlib/include",
+ ],
+}
diff --git a/libwifi_system/include/wifi_system/interface_tool.h b/libwifi_system_iface/include/wifi_system/interface_tool.h
similarity index 100%
rename from libwifi_system/include/wifi_system/interface_tool.h
rename to libwifi_system_iface/include/wifi_system/interface_tool.h
diff --git a/libwifi_system/interface_tool.cpp b/libwifi_system_iface/interface_tool.cpp
similarity index 100%
rename from libwifi_system/interface_tool.cpp
rename to libwifi_system_iface/interface_tool.cpp
diff --git a/libwifi_system/testlib/include/wifi_system_test/mock_interface_tool.h b/libwifi_system_iface/testlib/include/wifi_system_test/mock_interface_tool.h
similarity index 97%
rename from libwifi_system/testlib/include/wifi_system_test/mock_interface_tool.h
rename to libwifi_system_iface/testlib/include/wifi_system_test/mock_interface_tool.h
index b9926c9..200f0c0 100644
--- a/libwifi_system/testlib/include/wifi_system_test/mock_interface_tool.h
+++ b/libwifi_system_iface/testlib/include/wifi_system_test/mock_interface_tool.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_WIFI_SYSTEM_TEST_MOCK_INTERFACE_TOOL_H
#define ANDROID_WIFI_SYSTEM_TEST_MOCK_INTERFACE_TOOL_H
+#include <gmock/gmock.h>
#include <wifi_system/interface_tool.h>
namespace android {
diff --git a/service/Android.mk b/service/Android.mk
index 16b063e..155f5b2 100644
--- a/service/Android.mk
+++ b/service/Android.mk
@@ -63,6 +63,7 @@
services
LOCAL_STATIC_JAVA_LIBRARIES := \
android.hardware.wifi-V1.0-java \
+ android.hardware.wifi-V1.1-java \
android.hardware.wifi.supplicant-V1.0-java
LOCAL_REQUIRED_MODULES := services
LOCAL_MODULE_TAGS :=
diff --git a/service/java/com/android/server/wifi/AggressiveConnectedScore.java b/service/java/com/android/server/wifi/AggressiveConnectedScore.java
new file mode 100644
index 0000000..a0de773
--- /dev/null
+++ b/service/java/com/android/server/wifi/AggressiveConnectedScore.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.content.Context;
+import android.net.wifi.WifiInfo;
+
+import com.android.internal.R;
+
+/**
+ * Experimental scorer, used when aggressive handover preference is set.
+ */
+public class AggressiveConnectedScore extends ConnectedScore {
+
+ // Device configs. The values are examples.
+ private final int mThresholdQualifiedRssi5; // -70
+ private final int mThresholdQualifiedRssi24; // -73
+
+ private int mFrequencyMHz = 5000;
+ private int mRssi = 0;
+
+ public AggressiveConnectedScore(Context context, Clock clock) {
+ super(clock);
+ mThresholdQualifiedRssi5 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz);
+ mThresholdQualifiedRssi24 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz);
+ }
+
+ @Override
+ public void updateUsingRssi(int rssi, long millis, double standardDeviation) {
+ mRssi = rssi;
+ }
+
+ @Override
+ public void updateUsingWifiInfo(WifiInfo wifiInfo, long millis) {
+ mFrequencyMHz = wifiInfo.getFrequency();
+ mRssi = wifiInfo.getRssi();
+ }
+
+ @Override
+ public void reset() {
+ mFrequencyMHz = 5000;
+ }
+
+ @Override
+ public int generateScore() {
+ int badRssi = mFrequencyMHz >= 5000 ? mThresholdQualifiedRssi5 : mThresholdQualifiedRssi24;
+ int score = (mRssi - badRssi) + WIFI_TRANSITION_SCORE;
+ return score;
+ }
+}
diff --git a/service/java/com/android/server/wifi/ConfigurationMap.java b/service/java/com/android/server/wifi/ConfigurationMap.java
index fc85f64..0652b40 100644
--- a/service/java/com/android/server/wifi/ConfigurationMap.java
+++ b/service/java/com/android/server/wifi/ConfigurationMap.java
@@ -1,24 +1,21 @@
package com.android.server.wifi;
-import android.content.pm.UserInfo;
+import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.os.UserHandle;
import android.os.UserManager;
-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;
-import java.util.Set;
public class ConfigurationMap {
private final Map<Integer, WifiConfiguration> mPerID = new HashMap<>();
private final Map<Integer, WifiConfiguration> mPerIDForCurrentUser = new HashMap<>();
- private final Map<String, WifiConfiguration> mPerFQDNForCurrentUser = new HashMap<>();
+ private final Map<ScanResultMatchInfo, WifiConfiguration>
+ mScanResultMatchInfoMapForCurrentUser = new HashMap<>();
private final UserManager mUserManager;
@@ -34,9 +31,8 @@
if (WifiConfigurationUtil.isVisibleToAnyProfile(config,
mUserManager.getProfiles(mCurrentUserId))) {
mPerIDForCurrentUser.put(config.networkId, config);
- if (config.FQDN != null && config.FQDN.length() > 0) {
- mPerFQDNForCurrentUser.put(config.FQDN, config);
- }
+ mScanResultMatchInfoMapForCurrentUser.put(
+ ScanResultMatchInfo.fromWifiConfiguration(config), config);
}
return current;
}
@@ -48,11 +44,12 @@
}
mPerIDForCurrentUser.remove(netID);
- Iterator<Map.Entry<String, WifiConfiguration>> entries =
- mPerFQDNForCurrentUser.entrySet().iterator();
- while (entries.hasNext()) {
- if (entries.next().getValue().networkId == netID) {
- entries.remove();
+
+ Iterator<Map.Entry<ScanResultMatchInfo, WifiConfiguration>> scanResultMatchInfoEntries =
+ mScanResultMatchInfoMapForCurrentUser.entrySet().iterator();
+ while (scanResultMatchInfoEntries.hasNext()) {
+ if (scanResultMatchInfoEntries.next().getValue().networkId == netID) {
+ scanResultMatchInfoEntries.remove();
break;
}
}
@@ -62,7 +59,7 @@
public void clear() {
mPerID.clear();
mPerIDForCurrentUser.clear();
- mPerFQDNForCurrentUser.clear();
+ mScanResultMatchInfoMapForCurrentUser.clear();
}
/**
@@ -91,10 +88,6 @@
return mPerIDForCurrentUser.size();
}
- public WifiConfiguration getByFQDNForCurrentUser(String fqdn) {
- return mPerFQDNForCurrentUser.get(fqdn);
- }
-
public WifiConfiguration getByConfigKeyForCurrentUser(String key) {
if (key == null) {
return null;
@@ -107,23 +100,14 @@
return null;
}
- public Collection<WifiConfiguration> getEnabledNetworksForCurrentUser() {
- List<WifiConfiguration> list = new ArrayList<>();
- for (WifiConfiguration config : mPerIDForCurrentUser.values()) {
- if (config.status != WifiConfiguration.Status.DISABLED) {
- list.add(config);
- }
- }
- return list;
- }
-
- public WifiConfiguration getEphemeralForCurrentUser(String ssid) {
- for (WifiConfiguration config : mPerIDForCurrentUser.values()) {
- if (ssid.equals(config.SSID) && config.ephemeral) {
- return config;
- }
- }
- return null;
+ /**
+ * Retrieves the |WifiConfiguration| object matching the provided |scanResult| from the internal
+ * map.
+ * Essentially checks if network config and scan result have the same SSID and encryption type.
+ */
+ public WifiConfiguration getByScanResultForCurrentUser(ScanResult scanResult) {
+ return mScanResultMatchInfoMapForCurrentUser.get(
+ ScanResultMatchInfo.fromScanResult(scanResult));
}
public Collection<WifiConfiguration> valuesForAllUsers() {
diff --git a/service/java/com/android/server/wifi/ConnectedScore.java b/service/java/com/android/server/wifi/ConnectedScore.java
new file mode 100644
index 0000000..5c7ffa8
--- /dev/null
+++ b/service/java/com/android/server/wifi/ConnectedScore.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.net.NetworkAgent;
+import android.net.wifi.WifiInfo;
+
+/**
+ * Base class for connection scoring
+ */
+public abstract class ConnectedScore {
+
+ /** Maximum NetworkAgent score that should be generated by wifi */
+ public static final int WIFI_MAX_SCORE = NetworkAgent.WIFI_BASE_SCORE;
+
+ /** Score at which wifi is considered poor enough to give up ant try something else */
+ public static final int WIFI_TRANSITION_SCORE = NetworkAgent.WIFI_BASE_SCORE - 10;
+
+ public static final int WIFI_MIN_SCORE = 0;
+
+ final Clock mClock;
+
+ /** This is a typical STD for the connected RSSI for a phone sitting still */
+ public double mDefaultRssiStandardDeviation = 2.0;
+
+ /**
+ *
+ * @param clock is the time source for getMillis()
+ */
+ public ConnectedScore(Clock clock) {
+ mClock = clock;
+ }
+
+ /**
+ * Returns the current time in milliseconds
+ *
+ * This time is to be passed into the update methods.
+ * The scoring methods generally don't need a particular epoch, depending
+ * only on deltas. So a different time source may be used, as long as it is consistent.
+ *
+ * Note that when there are long intervals between updates, it is unlikely to matter much
+ * how large the interval is, so a time source that does not update while the processor is
+ * asleep could be just fine.
+ *
+ * @return millisecond-resolution time.
+ */
+ public long getMillis() {
+ return mClock.getWallClockMillis();
+ }
+
+ /**
+ * Updates scoring state using RSSI alone
+ *
+ * @param rssi signal strength (dB).
+ * @param millis millisecond-resolution time.
+ */
+ public void updateUsingRssi(int rssi, long millis) {
+ updateUsingRssi(rssi, millis, mDefaultRssiStandardDeviation);
+ }
+
+ /**
+ * Updates scoring state using RSSI and noise estimate
+ *
+ * This is useful if an RSSI comes from another source (e.g. scan results) and the
+ * expected noise varies by source.
+ *
+ * @param rssi signal strength (dB).
+ * @param millis millisecond-resolution time.
+ * @param standardDeviation of the RSSI.
+ */
+ public abstract void updateUsingRssi(int rssi, long millis, double standardDeviation);
+
+ /**
+ * Updates the score using relevant parts of WifiInfo
+ *
+ * @param wifiInfo object holding relevant values.
+ * @param millis millisecond-resolution time.
+ */
+ public void updateUsingWifiInfo(WifiInfo wifiInfo, long millis) {
+ updateUsingRssi(wifiInfo.getRssi(), millis);
+ }
+
+ /**
+ * Generates a score based on the current state
+ *
+ * @return network score - on NetworkAgent scale.
+ */
+ public abstract int generateScore();
+
+ /**
+ * Clears out state associated with the connection
+ */
+ public abstract void reset();
+}
diff --git a/service/java/com/android/server/wifi/FrameworkFacade.java b/service/java/com/android/server/wifi/FrameworkFacade.java
index ba114df..760ee69 100644
--- a/service/java/com/android/server/wifi/FrameworkFacade.java
+++ b/service/java/com/android/server/wifi/FrameworkFacade.java
@@ -18,6 +18,7 @@
import android.app.ActivityManager;
import android.app.AppGlobals;
+import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -94,6 +95,13 @@
return PendingIntent.getBroadcast(context, requestCode, intent, flags);
}
+ /**
+ * Wrapper for {@link PendingIntent#getActivity}.
+ */
+ public PendingIntent getActivity(Context context, int requestCode, Intent intent, int flags) {
+ return PendingIntent.getActivity(context, requestCode, intent, flags);
+ }
+
public SupplicantStateTracker makeSupplicantStateTracker(Context context,
WifiConfigManager configManager, Handler handler) {
return new SupplicantStateTracker(context, configManager, this, handler);
@@ -172,4 +180,14 @@
public boolean isAppForeground(int uid) throws RemoteException {
return ActivityManager.getService().isAppForeground(uid);
}
+
+ /**
+ * Create a new instance of {@link Notification.Builder}.
+ * @param context reference to a Context
+ * @param channelId ID of the notification channel
+ * @return an instance of Notification.Builder
+ */
+ public Notification.Builder makeNotificationBuilder(Context context, String channelId) {
+ return new Notification.Builder(context, channelId);
+ }
}
diff --git a/service/java/com/android/server/wifi/HalDeviceManager.java b/service/java/com/android/server/wifi/HalDeviceManager.java
index a5e1273..d6009c7 100644
--- a/service/java/com/android/server/wifi/HalDeviceManager.java
+++ b/service/java/com/android/server/wifi/HalDeviceManager.java
@@ -170,7 +170,7 @@
*
* @return A set of IfaceTypes constants (possibly empty, e.g. on error).
*/
- Set<Integer> getSupportedIfaceTypes() {
+ public Set<Integer> getSupportedIfaceTypes() {
return getSupportedIfaceTypesInternal(null);
}
@@ -179,7 +179,7 @@
*
* @return A set of IfaceTypes constants (possibly empty, e.g. on error).
*/
- Set<Integer> getSupportedIfaceTypes(IWifiChip chip) {
+ public Set<Integer> getSupportedIfaceTypes(IWifiChip chip) {
return getSupportedIfaceTypesInternal(chip);
}
@@ -244,12 +244,13 @@
* other functions - e.g. calling the debug/trace methods.
*/
public IWifiChip getChip(IWifiIface iface) {
- if (DBG) Log.d(TAG, "getChip: iface(name)=" + getName(iface));
+ String name = getName(iface);
+ if (DBG) Log.d(TAG, "getChip: iface(name)=" + name);
synchronized (mLock) {
- InterfaceCacheEntry cacheEntry = mInterfaceInfoCache.get(iface);
+ InterfaceCacheEntry cacheEntry = mInterfaceInfoCache.get(name);
if (cacheEntry == null) {
- Log.e(TAG, "getChip: no entry for iface(name)=" + getName(iface));
+ Log.e(TAG, "getChip: no entry for iface(name)=" + name);
return null;
}
@@ -267,13 +268,13 @@
public boolean registerDestroyedListener(IWifiIface iface,
InterfaceDestroyedListener destroyedListener,
Looper looper) {
- if (DBG) Log.d(TAG, "registerDestroyedListener: iface(name)=" + getName(iface));
+ String name = getName(iface);
+ if (DBG) Log.d(TAG, "registerDestroyedListener: iface(name)=" + name);
synchronized (mLock) {
- InterfaceCacheEntry cacheEntry = mInterfaceInfoCache.get(iface);
+ InterfaceCacheEntry cacheEntry = mInterfaceInfoCache.get(name);
if (cacheEntry == null) {
- Log.e(TAG, "registerDestroyedListener: no entry for iface(name)="
- + getName(iface));
+ Log.e(TAG, "registerDestroyedListener: no entry for iface(name)=" + name);
return false;
}
@@ -303,9 +304,13 @@
*/
public void registerInterfaceAvailableForRequestListener(int ifaceType,
InterfaceAvailableForRequestListener listener, Looper looper) {
- mInterfaceAvailableForRequestListeners.get(ifaceType).add(
- new InterfaceAvailableForRequestListenerProxy(listener,
- looper == null ? Looper.myLooper() : looper));
+ if (DBG) Log.d(TAG, "registerInterfaceAvailableForRequestListener: ifaceType=" + ifaceType);
+
+ synchronized (mLock) {
+ mInterfaceAvailableForRequestListeners.get(ifaceType).add(
+ new InterfaceAvailableForRequestListenerProxy(listener,
+ looper == null ? Looper.myLooper() : looper));
+ }
WifiChipInfo[] chipInfos = getAllChipInfo();
if (chipInfos == null) {
@@ -323,12 +328,18 @@
public void unregisterInterfaceAvailableForRequestListener(
int ifaceType,
InterfaceAvailableForRequestListener listener) {
- Iterator<InterfaceAvailableForRequestListenerProxy> it =
- mInterfaceAvailableForRequestListeners.get(ifaceType).iterator();
- while (it.hasNext()) {
- if (it.next().mListener == listener) {
- it.remove();
- return;
+ if (DBG) {
+ Log.d(TAG, "unregisterInterfaceAvailableForRequestListener: ifaceType=" + ifaceType);
+ }
+
+ synchronized (mLock) {
+ Iterator<InterfaceAvailableForRequestListenerProxy> it =
+ mInterfaceAvailableForRequestListeners.get(ifaceType).iterator();
+ while (it.hasNext()) {
+ if (it.next().mListener == listener) {
+ it.remove();
+ return;
+ }
}
}
}
@@ -443,13 +454,14 @@
private final Set<ManagerStatusListenerProxy> mManagerStatusListeners = new HashSet<>();
private final SparseArray<Set<InterfaceAvailableForRequestListenerProxy>>
mInterfaceAvailableForRequestListeners = new SparseArray<>();
+ private final SparseArray<IWifiChipEventCallback.Stub> mDebugCallbacks = new SparseArray<>();
/*
* This is the only place where we cache HIDL information in this manager. Necessary since
* we need to keep a list of registered destroyed listeners. Will be validated regularly
* in getAllChipInfoAndValidateCache().
*/
- private final Map<IWifiIface, InterfaceCacheEntry> mInterfaceInfoCache = new HashMap<>();
+ private final Map<String, InterfaceCacheEntry> mInterfaceInfoCache = new HashMap<>();
private class InterfaceCacheEntry {
public IWifiChip chip;
@@ -520,6 +532,9 @@
private void initializeInternal() {
initIServiceManagerIfNecessary();
+ if (isSupportedInternal()) {
+ initIWifiIfNecessary();
+ }
}
private void teardownInternal() {
@@ -548,9 +563,9 @@
boolean preexisting) {
Log.d(TAG, "IWifi registration notification: fqName=" + fqName
+ ", name=" + name + ", preexisting=" + preexisting);
- mWifi = null; // get rid of old copy!
- initIWifiIfNecessary();
- stopWifi(); // just in case
+ synchronized (mLock) {
+ initIWifiIfNecessary();
+ }
}
};
@@ -659,7 +674,8 @@
mWifi = null;
return;
}
- managerStatusListenerDispatch();
+ // Stopping wifi just in case. This would also trigger the status callback.
+ stopWifi();
} catch (RemoteException e) {
Log.e(TAG, "Exception while operating on IWifi: " + e);
}
@@ -720,7 +736,7 @@
continue; // still try next one?
}
- WifiStatus status = chipResp.value.registerEventCallback(
+ IWifiChipEventCallback.Stub callback =
new IWifiChipEventCallback.Stub() {
@Override
public void onChipReconfigured(int modeId) throws RemoteException {
@@ -759,7 +775,9 @@
throws RemoteException {
Log.d(TAG, "onDebugErrorAlert");
}
- });
+ };
+ mDebugCallbacks.put(chipId, callback); // store to prevent GC: needed by HIDL
+ WifiStatus status = chipResp.value.registerEventCallback(callback);
if (status.code != WifiStatusCode.SUCCESS) {
Log.e(TAG, "registerEventCallback failed: " + statusString(status));
continue; // still try next one?
@@ -1028,37 +1046,36 @@
if (DBG) Log.d(TAG, "validateInterfaceCache");
synchronized (mLock) {
- for (Map.Entry<IWifiIface, InterfaceCacheEntry> entry: mInterfaceInfoCache.entrySet()) {
+ for (InterfaceCacheEntry entry: mInterfaceInfoCache.values()) {
// search for chip
WifiChipInfo matchingChipInfo = null;
for (WifiChipInfo ci: chipInfos) {
- if (ci.chipId == entry.getValue().chipId) {
+ if (ci.chipId == entry.chipId) {
matchingChipInfo = ci;
break;
}
}
if (matchingChipInfo == null) {
- Log.e(TAG, "validateInterfaceCache: no chip found for " + entry.getValue());
+ Log.e(TAG, "validateInterfaceCache: no chip found for " + entry);
return false;
}
// search for interface
- WifiIfaceInfo[] ifaceInfoList = matchingChipInfo.ifaces[entry.getValue().type];
+ WifiIfaceInfo[] ifaceInfoList = matchingChipInfo.ifaces[entry.type];
if (ifaceInfoList == null) {
- Log.e(TAG, "validateInterfaceCache: invalid type on entry " + entry.getValue());
+ Log.e(TAG, "validateInterfaceCache: invalid type on entry " + entry);
return false;
}
boolean matchFound = false;
for (WifiIfaceInfo ifaceInfo: ifaceInfoList) {
- if (ifaceInfo.name.equals(entry.getValue().name)) {
+ if (ifaceInfo.name.equals(entry.name)) {
matchFound = true;
break;
}
}
if (!matchFound) {
- Log.e(TAG, "validateInterfaceCache: no interface found for "
- + entry.getValue());
+ Log.e(TAG, "validateInterfaceCache: no interface found for " + entry);
return false;
}
}
@@ -1323,7 +1340,8 @@
looper == null ? Looper.myLooper() : looper));
}
- mInterfaceInfoCache.put(iface, cacheEntry);
+ if (DBG) Log.d(TAG, "createIfaceIfPossible: added cacheEntry=" + cacheEntry);
+ mInterfaceInfoCache.put(cacheEntry.name, cacheEntry);
return iface;
}
}
@@ -1676,21 +1694,21 @@
}
private boolean removeIfaceInternal(IWifiIface iface) {
- if (DBG) Log.d(TAG, "removeIfaceInternal: iface(name)=" + getName(iface));
+ String name = getName(iface);
+ if (DBG) Log.d(TAG, "removeIfaceInternal: iface(name)=" + name);
synchronized (mLock) {
if (mWifi == null) {
- Log.e(TAG, "removeIfaceInternal: null IWifi -- iface(name)=" + getName(iface));
+ Log.e(TAG, "removeIfaceInternal: null IWifi -- iface(name)=" + name);
return false;
}
IWifiChip chip = getChip(iface);
if (chip == null) {
- Log.e(TAG, "removeIfaceInternal: null IWifiChip -- iface(name)=" + getName(iface));
+ Log.e(TAG, "removeIfaceInternal: null IWifiChip -- iface(name)=" + name);
return false;
}
- String name = getName(iface);
if (name == null) {
Log.e(TAG, "removeIfaceInternal: can't get name");
return false;
@@ -1698,7 +1716,7 @@
int type = getType(iface);
if (type == -1) {
- Log.e(TAG, "removeIfaceInternal: can't get type -- iface(name)=" + getName(iface));
+ Log.e(TAG, "removeIfaceInternal: can't get type -- iface(name)=" + name);
return false;
}
@@ -1726,7 +1744,7 @@
}
// dispatch listeners no matter what status
- dispatchDestroyedListeners(iface);
+ dispatchDestroyedListeners(name);
if (status != null && status.code == WifiStatusCode.SUCCESS) {
return true;
@@ -1786,14 +1804,13 @@
// dispatch all destroyed listeners registered for the specified interface AND remove the
// cache entry
- private void dispatchDestroyedListeners(IWifiIface iface) {
- if (DBG) Log.d(TAG, "dispatchDestroyedListeners: iface(name)=" + getName(iface));
+ private void dispatchDestroyedListeners(String name) {
+ if (DBG) Log.d(TAG, "dispatchDestroyedListeners: iface(name)=" + name);
synchronized (mLock) {
- InterfaceCacheEntry entry = mInterfaceInfoCache.get(iface);
+ InterfaceCacheEntry entry = mInterfaceInfoCache.get(name);
if (entry == null) {
- Log.e(TAG, "dispatchDestroyedListeners: no cache entry for iface(name)="
- + getName(iface));
+ Log.e(TAG, "dispatchDestroyedListeners: no cache entry for iface(name)=" + name);
return;
}
@@ -1801,7 +1818,7 @@
listener.trigger();
}
entry.destroyedListeners.clear(); // for insurance (though cache entry is removed)
- mInterfaceInfoCache.remove(iface);
+ mInterfaceInfoCache.remove(name);
}
}
@@ -1810,7 +1827,7 @@
if (DBG) Log.d(TAG, "dispatchAllDestroyedListeners");
synchronized (mLock) {
- Iterator<Map.Entry<IWifiIface, InterfaceCacheEntry>> it =
+ Iterator<Map.Entry<String, InterfaceCacheEntry>> it =
mInterfaceInfoCache.entrySet().iterator();
while (it.hasNext()) {
InterfaceCacheEntry entry = it.next().getValue();
diff --git a/service/java/com/android/server/wifi/LegacyConnectedScore.java b/service/java/com/android/server/wifi/LegacyConnectedScore.java
new file mode 100644
index 0000000..facab0a
--- /dev/null
+++ b/service/java/com/android/server/wifi/LegacyConnectedScore.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.content.Context;
+import android.net.NetworkAgent;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+
+import com.android.internal.R;
+
+/**
+ * Class used to calculate scores for connected wifi networks and report it to the associated
+ * network agent.
+ */
+public class LegacyConnectedScore extends ConnectedScore {
+
+ private static final int STARTING_SCORE = 56;
+
+ private static final int SCAN_CACHE_VISIBILITY_MS = 12000;
+ private static final int HOME_VISIBLE_NETWORK_MAX_COUNT = 6;
+ private static final int SCAN_CACHE_COUNT_PENALTY = 2;
+ private static final int MAX_SUCCESS_RATE_OF_STUCK_LINK = 3; // proportional to packets per sec
+ private static final int MAX_STUCK_LINK_COUNT = 5;
+ private static final int MAX_BAD_RSSI_COUNT = 7;
+ private static final int BAD_RSSI_COUNT_PENALTY = 2;
+ private static final int MAX_LOW_RSSI_COUNT = 1;
+ private static final double MIN_TX_FAILURE_RATE_FOR_WORKING_LINK = 0.3;
+ private static final int MIN_SUSTAINED_LINK_STUCK_COUNT = 1;
+ private static final int LINK_STUCK_PENALTY = 2;
+ private static final int BAD_LINKSPEED_PENALTY = 4;
+ private static final int GOOD_LINKSPEED_BONUS = 4;
+
+ // Device configs. The values are examples.
+ private final int mThresholdMinimumRssi5; // -82
+ private final int mThresholdQualifiedRssi5; // -70
+ private final int mThresholdSaturatedRssi5; // -57
+ private final int mThresholdMinimumRssi24; // -85
+ private final int mThresholdQualifiedRssi24; // -73
+ private final int mThresholdSaturatedRssi24; // -60
+ private final int mBadLinkSpeed24; // 6 Mbps
+ private final int mBadLinkSpeed5; // 12 Mbps
+ private final int mGoodLinkSpeed24; // 24 Mbps
+ private final int mGoodLinkSpeed5; // 36 Mbps
+
+ private final WifiConfigManager mWifiConfigManager;
+ private boolean mVerboseLoggingEnabled = false;
+
+ private boolean mMultiBandScanResults;
+ private boolean mIsHomeNetwork;
+ private int mScore = 0;
+
+ LegacyConnectedScore(Context context, WifiConfigManager wifiConfigManager, Clock clock) {
+ super(clock);
+ // Fetch all the device configs.
+ mThresholdMinimumRssi5 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz);
+ mThresholdQualifiedRssi5 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz);
+ mThresholdSaturatedRssi5 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz);
+ mThresholdMinimumRssi24 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz);
+ mThresholdQualifiedRssi24 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz);
+ mThresholdSaturatedRssi24 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz);
+ mBadLinkSpeed24 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_link_speed_24);
+ mBadLinkSpeed5 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_link_speed_5);
+ mGoodLinkSpeed24 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_link_speed_24);
+ mGoodLinkSpeed5 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_link_speed_5);
+
+ mWifiConfigManager = wifiConfigManager;
+ }
+
+ @Override
+ public void updateUsingWifiInfo(WifiInfo wifiInfo, long millis) {
+ mMultiBandScanResults = multiBandScanResults(wifiInfo);
+ mIsHomeNetwork = isHomeNetwork(wifiInfo);
+
+ int rssiThreshBad = mThresholdMinimumRssi24;
+ int rssiThreshLow = mThresholdQualifiedRssi24;
+
+ if (wifiInfo.is5GHz() && !mMultiBandScanResults) {
+ rssiThreshBad = mThresholdMinimumRssi5;
+ rssiThreshLow = mThresholdQualifiedRssi5;
+ }
+
+ int rssi = wifiInfo.getRssi();
+ if (mIsHomeNetwork) {
+ rssi += WifiConfiguration.HOME_NETWORK_RSSI_BOOST;
+ }
+
+ if ((wifiInfo.txBadRate >= 1)
+ && (wifiInfo.txSuccessRate < MAX_SUCCESS_RATE_OF_STUCK_LINK)
+ && rssi < rssiThreshLow) {
+ // Link is stuck
+ if (wifiInfo.linkStuckCount < MAX_STUCK_LINK_COUNT) {
+ wifiInfo.linkStuckCount += 1;
+ }
+ } else if (wifiInfo.txBadRate < MIN_TX_FAILURE_RATE_FOR_WORKING_LINK) {
+ if (wifiInfo.linkStuckCount > 0) {
+ wifiInfo.linkStuckCount -= 1;
+ }
+ }
+
+ if (rssi < rssiThreshBad) {
+ if (wifiInfo.badRssiCount < MAX_BAD_RSSI_COUNT) {
+ wifiInfo.badRssiCount += 1;
+ }
+ } else if (rssi < rssiThreshLow) {
+ wifiInfo.lowRssiCount = MAX_LOW_RSSI_COUNT; // Dont increment the lowRssi count above 1
+ if (wifiInfo.badRssiCount > 0) {
+ // Decrement bad Rssi count
+ wifiInfo.badRssiCount -= 1;
+ }
+ } else {
+ wifiInfo.badRssiCount = 0;
+ wifiInfo.lowRssiCount = 0;
+ }
+
+ // Ugh, we need to finish the score calculation while we have wifiInfo
+ mScore = calculateScore(wifiInfo);
+
+ }
+
+ @Override
+ public void updateUsingRssi(int rssi, long millis, double standardDeviation) {
+ // This scorer needs more than just the RSSI. Just ignore.
+ }
+
+ @Override
+ public int generateScore() {
+ return mScore;
+ }
+
+ @Override
+ public void reset() {
+ mScore = 0;
+ }
+
+ /**
+ * Calculates a score based on the current state and wifiInfo
+ */
+ private int calculateScore(WifiInfo wifiInfo) {
+ int score = STARTING_SCORE;
+
+ int rssiThreshSaturated = mThresholdSaturatedRssi24;
+ int linkspeedThreshBad = mBadLinkSpeed24;
+ int linkspeedThreshGood = mGoodLinkSpeed24;
+
+ if (wifiInfo.is5GHz()) {
+ if (!mMultiBandScanResults) {
+ rssiThreshSaturated = mThresholdSaturatedRssi5;
+ }
+ linkspeedThreshBad = mBadLinkSpeed5;
+ linkspeedThreshGood = mGoodLinkSpeed5;
+ }
+
+ int rssi = wifiInfo.getRssi();
+ if (mIsHomeNetwork) {
+ rssi += WifiConfiguration.HOME_NETWORK_RSSI_BOOST;
+ }
+
+ int linkSpeed = wifiInfo.getLinkSpeed();
+
+ if (wifiInfo.linkStuckCount > MIN_SUSTAINED_LINK_STUCK_COUNT) {
+ // Once link gets stuck for more than 3 seconds, start reducing the score
+ score = score - LINK_STUCK_PENALTY * (wifiInfo.linkStuckCount - 1);
+ }
+
+ if (linkSpeed < linkspeedThreshBad) {
+ score -= BAD_LINKSPEED_PENALTY;
+ } else if ((linkSpeed >= linkspeedThreshGood) && (wifiInfo.txSuccessRate > 5)) {
+ score += GOOD_LINKSPEED_BONUS; // So as bad rssi alone doesn't kill us
+ }
+
+ score -= wifiInfo.badRssiCount * BAD_RSSI_COUNT_PENALTY + wifiInfo.lowRssiCount;
+
+ if (rssi >= rssiThreshSaturated) score += 5;
+
+ if (score > NetworkAgent.WIFI_BASE_SCORE) score = NetworkAgent.WIFI_BASE_SCORE;
+ if (score < 0) score = 0;
+
+ return score;
+ }
+
+ /**
+ * Determines if we can see both 2.4GHz and 5GHz for current config
+ */
+ private boolean multiBandScanResults(WifiInfo wifiInfo) {
+ WifiConfiguration currentConfiguration =
+ mWifiConfigManager.getConfiguredNetwork(wifiInfo.getNetworkId());
+ if (currentConfiguration == null) return false;
+ ScanDetailCache scanDetailCache =
+ mWifiConfigManager.getScanDetailCacheForNetwork(wifiInfo.getNetworkId());
+ if (scanDetailCache == null) return false;
+ // Nasty that we change state here...
+ currentConfiguration.setVisibility(scanDetailCache.getVisibility(SCAN_CACHE_VISIBILITY_MS));
+ if (currentConfiguration.visibility == null) return false;
+ if (currentConfiguration.visibility.rssi24 == WifiConfiguration.INVALID_RSSI) return false;
+ if (currentConfiguration.visibility.rssi5 == WifiConfiguration.INVALID_RSSI) return false;
+ // N.B. this does not do exactly what is claimed!
+ if (currentConfiguration.visibility.rssi24
+ >= currentConfiguration.visibility.rssi5 - SCAN_CACHE_COUNT_PENALTY) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Decides whether the current network is a "home" network
+ */
+ private boolean isHomeNetwork(WifiInfo wifiInfo) {
+ WifiConfiguration currentConfiguration =
+ mWifiConfigManager.getConfiguredNetwork(wifiInfo.getNetworkId());
+ if (currentConfiguration == null) return false;
+ // This seems like it will only return true for really old routers!
+ if (currentConfiguration.allowedKeyManagement.cardinality() != 1) return false;
+ if (!currentConfiguration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
+ return false;
+ }
+ ScanDetailCache scanDetailCache =
+ mWifiConfigManager.getScanDetailCacheForNetwork(wifiInfo.getNetworkId());
+ if (scanDetailCache == null) return false;
+ if (scanDetailCache.size() <= HOME_VISIBLE_NETWORK_MAX_COUNT) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/service/java/com/android/server/wifi/OWNERS b/service/java/com/android/server/wifi/OWNERS
index 8487100..7cc644a 100644
--- a/service/java/com/android/server/wifi/OWNERS
+++ b/service/java/com/android/server/wifi/OWNERS
@@ -7,76 +7,116 @@
mett@google.com
mplass@google.com
nywang@google.com
-pstew@google.com
quiche@google.com
rpius@google.com
silberst@google.com
sohanirao@google.com
-zpan@google.com
zqiu@google.com
# configuration
per-file WifiBackupRestore*=rpius@google.com
+per-file WifiBackupRestore*=satk@google.com
per-file WifiConfigManager*=rpius@google.com
+per-file WifiConfigManager*=satk@google.com
per-file WifiConfigStore*=rpius@google.com
+per-file WifiConfigStore*=satk@google.com
per-file WifiSupplicantControl*=rpius@google.com
+per-file WifiSupplicantControl*=satk@google.com
# diagnostics
per-file BaseWifiDiagnostics*=quiche@google.com
+per-file BaseWifiDiagnostics*=satk@google.com
per-file LastMileLogger*=quiche@google.com
+per-file LastMileLogger*=satk@google.com
per-file WifiDiagnostics*=quiche@google.com
+per-file WifiDiagnostics*=satk@google.com
per-file WifiLoggerHal*=quiche@google.com
+per-file WifiLoggerHal*=satk@google.com
# HAL support
per-file HalDeviceManager*=etancohen@google.com
+per-file HalDeviceManager*=satk@google.com
per-file SupplicantStaNetworkHal*=kuh@google.com
per-file SupplicantStaNetworkHal*=rpius@google.com
+per-file SupplicantStaNetworkHal*=satk@google.com
per-file SupplicantStaIfaceHal*=kuh@google.com
per-file SupplicantStaIfaceHal*=rpius@google.com
+per-file SupplicantStaIfaceHal*=satk@google.com
per-file WifiVendorHal*=mplass@google.com
+per-file WifiVendorHal*=satk@google.com
# logging
per-file DummyLogMessage*=quiche@google.com
+per-file DummyLogMessage*=satk@google.com
per-file FakeWifiLog*=quiche@google.com
+per-file FakeWifiLog*=satk@google.com
per-file LogcatLog*=quiche@google.com
+per-file LogcatLog*=satk@google.com
per-file WifiLog*=quiche@google.com
+per-file WifiLog*=satk@google.com
# mode management
per-file ActiveModeManager*=silberst@google.com
+per-file ActiveModeManager*=satk@google.com
per-file ClientModeManager*=silberst@google.com
+per-file ClientModeManager*=satk@google.com
per-file ScanOnlyModeManager*=silberst@google.com
+per-file ScanOnlyModeManager*=satk@google.com
per-file WifiController*=silberst@google.com
+per-file WifiController*=satk@google.com
per-file WifiService*=silberst@google.com
+per-file WifiService*=satk@google.com
per-file WifiServiceImpl*=silberst@google.com
+per-file WifiServiceImpl*=satk@google.com
per-file WifiStateMachine*=silberst@google.com
+per-file WifiStateMachine*=satk@google.com
# network selection
-per-file SavedNetworkEvaluator*=zpan@google.com
-per-file WifiConnectivityManager*=zpan@google.com
-per-file WifiNetworkSelector*=zpan@google.com
+per-file SavedNetworkEvaluator*=quiche@google.com
+per-file SavedNetworkEvaluator*=silberst@google.com
+per-file SavedNetworkEvaluator*=satk@google.com
+per-file WifiConnectivityManager*=quiche@google.com
+per-file WifiConnectivityManager*=silberst@google.com
+per-file WifiConnectivityManager*=satk@google.com
+per-file WifiNetworkSelector*=quiche@google.com
+per-file WifiNetworkSelector*=silberst@google.com
+per-file WifiNetworkSelector*=satk@google.com
# random bits
per-file ByteBufferReader*=zqiu@google.com
+per-file ByteBufferReader*=satk@google.com
per-file IMSIParameter*=zqiu@google.com
+per-file IMSIParameter*=satk@google.com
per-file SIMAccessor*=zqiu@google.com
+per-file SIMAccessor*=satk@google.com
per-file WifiCountryCode*=nywang@google.com
+per-file WifiCountryCode*=satk@google.com
per-file WifiLastResortWatchdog*=kuh@google.com
per-file WifiLastResortWatchdog*=silberst@google.com
+per-file WifiLastResortWatchdog*=satk@google.com
per-file WifiMetrics*=kuh@google.com
+per-file WifiMetrics*=satk@google.com
per-file WifiScoreReport*=mplass@google.com
+per-file WifiScoreReport*=satk@google.com
# soft-ap/tethering
per-file SoftApManager*=silberst@google.com
+per-file SoftApManager*=satk@google.com
per-file WifiApConfigStore*=silberst@google.com
+per-file WifiApConfigStore*=satk@google.com
# test-support
per-file Clock*=quiche@google.com
+per-file Clock*=satk@google.com
per-file FrameworkFacade*=kuh@google.com
per-file FrameworkFacade*=rpius@google.com
per-file FrameworkFacade*=silberst@google.com
+per-file FrameworkFacade*=satk@google.com
per-file WifiInjector*=kuh@google.com
per-file WifiInjector*=rpius@google.com
per-file WifiInjector*=silberst@google.com
+per-file WifiInjector*=satk@google.com
# wificond
per-file WificondControl*=nywang@google.com
+per-file WificondControl*=satk@google.com
diff --git a/service/java/com/android/server/wifi/SavedNetworkEvaluator.java b/service/java/com/android/server/wifi/SavedNetworkEvaluator.java
index 9ac7068..59d4ab2 100644
--- a/service/java/com/android/server/wifi/SavedNetworkEvaluator.java
+++ b/service/java/com/android/server/wifi/SavedNetworkEvaluator.java
@@ -23,6 +23,7 @@
import android.util.Pair;
import com.android.internal.R;
+import com.android.server.wifi.util.TelephonyUtil;
import java.util.ArrayList;
import java.util.Arrays;
@@ -248,7 +249,7 @@
// the scores and use the highest one as the ScanResult's score.
List<WifiConfiguration> associatedConfigurations = null;
WifiConfiguration associatedConfiguration =
- mWifiConfigManager.getSavedNetworkForScanDetailAndCache(scanDetail);
+ mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail);
if (associatedConfiguration == null) {
continue;
@@ -282,6 +283,10 @@
+ " has specified BSSID " + network.BSSID + ". Skip "
+ scanResult.BSSID);
continue;
+ } else if (TelephonyUtil.isSimConfig(network)
+ && !mWifiConfigManager.isSimPresent()) {
+ // Don't select if security type is EAP SIM/AKA/AKA' when SIM is not present.
+ continue;
}
int score = calculateBssidScore(scanResult, network, currentNetwork, currentBssid,
diff --git a/service/java/com/android/server/wifi/ScanDetailCache.java b/service/java/com/android/server/wifi/ScanDetailCache.java
index a651379..3b69a64 100644
--- a/service/java/com/android/server/wifi/ScanDetailCache.java
+++ b/service/java/com/android/server/wifi/ScanDetailCache.java
@@ -67,12 +67,24 @@
mMap.put(scanDetail.getBSSIDString(), scanDetail);
}
- ScanResult get(String bssid) {
+ /**
+ * Get ScanResult object corresponding to the provided BSSID.
+ *
+ * @param bssid provided BSSID
+ * @return {@code null} if no match ScanResult is found.
+ */
+ public ScanResult get(String bssid) {
ScanDetail scanDetail = getScanDetail(bssid);
return scanDetail == null ? null : scanDetail.getScanResult();
}
- ScanDetail getScanDetail(String bssid) {
+ /**
+ * Get ScanDetail object corresponding to the provided BSSID.
+ *
+ * @param bssid provided BSSID
+ * @return {@code null} if no match ScanDetail is found.
+ */
+ public ScanDetail getScanDetail(String bssid) {
return mMap.get(bssid);
}
diff --git a/service/java/com/android/server/wifi/ScanResultMatchInfo.java b/service/java/com/android/server/wifi/ScanResultMatchInfo.java
new file mode 100644
index 0000000..ad29c23
--- /dev/null
+++ b/service/java/com/android/server/wifi/ScanResultMatchInfo.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.wifi;
+
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+
+import com.android.server.wifi.util.ScanResultUtil;
+
+import java.util.Objects;
+
+/**
+ * Class to store the info needed to match a scan result to the provided network configuration.
+ */
+public class ScanResultMatchInfo {
+ public static final int NETWORK_TYPE_OPEN = 0;
+ public static final int NETWORK_TYPE_WEP = 1;
+ public static final int NETWORK_TYPE_PSK = 2;
+ public static final int NETWORK_TYPE_EAP = 3;
+
+ /**
+ * SSID of the network.
+ */
+ public String networkSsid;
+ /**
+ * Security Type of the network.
+ */
+ public int networkType;
+
+ /**
+ * Get the ScanResultMatchInfo for the given WifiConfiguration
+ */
+ public static ScanResultMatchInfo fromWifiConfiguration(WifiConfiguration config) {
+ ScanResultMatchInfo info = new ScanResultMatchInfo();
+ info.networkSsid = config.SSID;
+ if (WifiConfigurationUtil.isConfigForPskNetwork(config)) {
+ info.networkType = NETWORK_TYPE_PSK;
+ } else if (WifiConfigurationUtil.isConfigForEapNetwork(config)) {
+ info.networkType = NETWORK_TYPE_EAP;
+ } else if (WifiConfigurationUtil.isConfigForWepNetwork(config)) {
+ info.networkType = NETWORK_TYPE_WEP;
+ } else if (WifiConfigurationUtil.isConfigForOpenNetwork(config)) {
+ info.networkType = NETWORK_TYPE_OPEN;
+ } else {
+ throw new IllegalArgumentException("Invalid WifiConfiguration: " + config);
+ }
+ return info;
+ }
+
+ /**
+ * Get the ScanResultMatchInfo for the given ScanResult
+ */
+ public static ScanResultMatchInfo fromScanResult(ScanResult scanResult) {
+ ScanResultMatchInfo info = new ScanResultMatchInfo();
+ // Scan result ssid's are not quoted, hence add quotes.
+ // TODO: This matching algo works only if the scan result contains a string SSID.
+ // However, according to our public documentation ths {@link WifiConfiguration#SSID} can
+ // either have a hex string or quoted ASCII string SSID.
+ info.networkSsid = ScanResultUtil.createQuotedSSID(scanResult.SSID);
+ if (ScanResultUtil.isScanResultForPskNetwork(scanResult)) {
+ info.networkType = NETWORK_TYPE_PSK;
+ } else if (ScanResultUtil.isScanResultForEapNetwork(scanResult)) {
+ info.networkType = NETWORK_TYPE_EAP;
+ } else if (ScanResultUtil.isScanResultForWepNetwork(scanResult)) {
+ info.networkType = NETWORK_TYPE_WEP;
+ } else if (ScanResultUtil.isScanResultForOpenNetwork(scanResult)) {
+ info.networkType = NETWORK_TYPE_OPEN;
+ } else {
+ throw new IllegalArgumentException("Invalid ScanResult: " + scanResult);
+ }
+ return info;
+ }
+
+ @Override
+ public boolean equals(Object otherObj) {
+ if (this == otherObj) {
+ return true;
+ } else if (!(otherObj instanceof ScanResultMatchInfo)) {
+ return false;
+ }
+ ScanResultMatchInfo other = (ScanResultMatchInfo) otherObj;
+ return Objects.equals(networkSsid, other.networkSsid)
+ && networkType == other.networkType;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(networkSsid, networkType);
+ }
+
+ @Override
+ public String toString() {
+ return "ScanResultMatchInfo: " + networkSsid + ", type: " + networkType;
+ }
+}
diff --git a/service/java/com/android/server/wifi/ScoredNetworkEvaluator.java b/service/java/com/android/server/wifi/ScoredNetworkEvaluator.java
index 483da99..e36cf66 100644
--- a/service/java/com/android/server/wifi/ScoredNetworkEvaluator.java
+++ b/service/java/com/android/server/wifi/ScoredNetworkEvaluator.java
@@ -125,7 +125,7 @@
continue;
}
final WifiConfiguration configuredNetwork =
- mWifiConfigManager.getSavedNetworkForScanDetailAndCache(scanDetail);
+ mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail);
boolean untrustedScanResult = configuredNetwork == null || configuredNetwork.ephemeral;
if (!untrustedNetworkAllowed && untrustedScanResult) {
diff --git a/service/java/com/android/server/wifi/SelfRecovery.java b/service/java/com/android/server/wifi/SelfRecovery.java
index b35b7cc..21a3e0a 100644
--- a/service/java/com/android/server/wifi/SelfRecovery.java
+++ b/service/java/com/android/server/wifi/SelfRecovery.java
@@ -18,6 +18,9 @@
import android.util.Log;
+import java.util.Iterator;
+import java.util.LinkedList;
+
/**
* This class is used to recover the wifi stack from a fatal failure. The recovery mechanism
* involves triggering a stack restart (essentially simulating an airplane mode toggle) using
@@ -36,7 +39,8 @@
public static final int REASON_LAST_RESORT_WATCHDOG = 0;
public static final int REASON_HAL_CRASH = 1;
public static final int REASON_WIFICOND_CRASH = 2;
-
+ public static final long MAX_RESTARTS_IN_TIME_WINDOW = 2; // 2 restarts per hour
+ public static final long MAX_RESTARTS_TIME_WINDOW_MILLIS = 60 * 60 * 1000; // 1 hour
private static final String[] REASON_STRINGS = {
"Last Resort Watchdog", // REASON_LAST_RESORT_WATCHDOG
"Hal Crash", // REASON_HAL_CRASH
@@ -44,9 +48,13 @@
};
private final WifiController mWifiController;
-
- SelfRecovery(WifiController wifiController) {
+ private final Clock mClock;
+ // Time since boot (in millis) that restart occurred
+ private final LinkedList<Long> mPastRestartTimes;
+ public SelfRecovery(WifiController wifiController, Clock clock) {
mWifiController = wifiController;
+ mClock = clock;
+ mPastRestartTimes = new LinkedList<Long>();
}
/**
@@ -59,11 +67,38 @@
* @param reason One of the above |REASON_*| codes.
*/
public void trigger(int reason) {
- if (reason < REASON_LAST_RESORT_WATCHDOG || reason > REASON_WIFICOND_CRASH) {
+ if (!(reason == REASON_LAST_RESORT_WATCHDOG || reason == REASON_HAL_CRASH
+ || reason == REASON_WIFICOND_CRASH)) {
Log.e(TAG, "Invalid trigger reason. Ignoring...");
return;
}
Log.wtf(TAG, "Triggering recovery for reason: " + REASON_STRINGS[reason]);
+ if (reason == REASON_WIFICOND_CRASH || reason == REASON_HAL_CRASH) {
+ trimPastRestartTimes();
+ // Ensure there haven't been too many restarts within MAX_RESTARTS_TIME_WINDOW
+ if (mPastRestartTimes.size() >= MAX_RESTARTS_IN_TIME_WINDOW) {
+ Log.e(TAG, "Already restarted wifi (" + MAX_RESTARTS_IN_TIME_WINDOW + ") times in"
+ + " last (" + MAX_RESTARTS_TIME_WINDOW_MILLIS + "ms ). Ignoring...");
+ return;
+ }
+ mPastRestartTimes.add(mClock.getElapsedSinceBootMillis());
+ }
mWifiController.sendMessage(WifiController.CMD_RESTART_WIFI);
}
+
+ /**
+ * Process the mPastRestartTimes list, removing elements outside the max restarts time window
+ */
+ private void trimPastRestartTimes() {
+ Iterator<Long> iter = mPastRestartTimes.iterator();
+ long now = mClock.getElapsedSinceBootMillis();
+ while (iter.hasNext()) {
+ Long restartTimeMillis = iter.next();
+ if (now - restartTimeMillis > MAX_RESTARTS_TIME_WINDOW_MILLIS) {
+ iter.remove();
+ } else {
+ break;
+ }
+ }
+ }
}
diff --git a/service/java/com/android/server/wifi/SoftApManager.java b/service/java/com/android/server/wifi/SoftApManager.java
index 64f4a14..d4a1ea5 100644
--- a/service/java/com/android/server/wifi/SoftApManager.java
+++ b/service/java/com/android/server/wifi/SoftApManager.java
@@ -161,12 +161,16 @@
int encryptionType = getIApInterfaceEncryptionType(localConfig);
+ if (localConfig.hiddenSSID) {
+ Log.d(TAG, "SoftAP is a hidden network");
+ }
+
try {
// Note that localConfig.SSID is intended to be either a hex string or "double quoted".
// However, it seems that whatever is handing us these configurations does not obey
// this convention.
boolean success = mApInterface.writeHostapdConfig(
- localConfig.SSID.getBytes(StandardCharsets.UTF_8), false,
+ localConfig.SSID.getBytes(StandardCharsets.UTF_8), localConfig.hiddenSSID,
localConfig.apChannel, encryptionType,
(localConfig.preSharedKey != null)
? localConfig.preSharedKey.getBytes(StandardCharsets.UTF_8)
diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
index 703c798..d2182fc 100644
--- a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
+++ b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
@@ -69,14 +69,18 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import javax.annotation.concurrent.ThreadSafe;
+
/**
* Hal calls for bring up/shut down of the supplicant daemon and for
* sending requests to the supplicant daemon
+ * To maintain thread-safety, the locking protocol is that every non-static method (regardless of
+ * access level) acquires mLock.
*/
+@ThreadSafe
public class SupplicantStaIfaceHal {
private static final String TAG = "SupplicantStaIfaceHal";
/**
@@ -113,16 +117,16 @@
};
private final HwRemoteBinder.DeathRecipient mServiceManagerDeathRecipient =
cookie -> {
- Log.w(TAG, "IServiceManager died: cookie=" + cookie);
synchronized (mLock) {
+ Log.w(TAG, "IServiceManager died: cookie=" + cookie);
supplicantServiceDiedHandler();
mIServiceManager = null; // Will need to register a new ServiceNotification
}
};
private final HwRemoteBinder.DeathRecipient mSupplicantDeathRecipient =
cookie -> {
- Log.w(TAG, "ISupplicant/ISupplicantStaIface died: cookie=" + cookie);
synchronized (mLock) {
+ Log.w(TAG, "ISupplicant/ISupplicantStaIface died: cookie=" + cookie);
supplicantServiceDiedHandler();
}
};
@@ -145,23 +149,27 @@
* @param enable true to enable, false to disable.
*/
void enableVerboseLogging(boolean enable) {
- mVerboseLoggingEnabled = enable;
+ synchronized (mLock) {
+ mVerboseLoggingEnabled = enable;
+ }
}
private boolean linkToServiceManagerDeath() {
- if (mIServiceManager == null) return false;
- try {
- if (!mIServiceManager.linkToDeath(mServiceManagerDeathRecipient, 0)) {
- Log.wtf(TAG, "Error on linkToDeath on IServiceManager");
- supplicantServiceDiedHandler();
- mIServiceManager = null; // Will need to register a new ServiceNotification
+ synchronized (mLock) {
+ if (mIServiceManager == null) return false;
+ try {
+ if (!mIServiceManager.linkToDeath(mServiceManagerDeathRecipient, 0)) {
+ Log.wtf(TAG, "Error on linkToDeath on IServiceManager");
+ supplicantServiceDiedHandler();
+ mIServiceManager = null; // Will need to register a new ServiceNotification
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "IServiceManager.linkToDeath exception", e);
return false;
}
- } catch (RemoteException e) {
- Log.e(TAG, "IServiceManager.linkToDeath exception", e);
- return false;
+ return true;
}
- return true;
}
/**
@@ -170,8 +178,10 @@
* @return true if the service notification was successfully registered
*/
public boolean initialize() {
- if (mVerboseLoggingEnabled) Log.i(TAG, "Registering ISupplicant service ready callback.");
synchronized (mLock) {
+ if (mVerboseLoggingEnabled) {
+ Log.i(TAG, "Registering ISupplicant service ready callback.");
+ }
mISupplicant = null;
mISupplicantStaIface = null;
if (mIServiceManager != null) {
@@ -207,18 +217,20 @@
}
private boolean linkToSupplicantDeath() {
- if (mISupplicant == null) return false;
- try {
- if (!mISupplicant.linkToDeath(mSupplicantDeathRecipient, 0)) {
- Log.wtf(TAG, "Error on linkToDeath on ISupplicant");
- supplicantServiceDiedHandler();
+ synchronized (mLock) {
+ if (mISupplicant == null) return false;
+ try {
+ if (!mISupplicant.linkToDeath(mSupplicantDeathRecipient, 0)) {
+ Log.wtf(TAG, "Error on linkToDeath on ISupplicant");
+ supplicantServiceDiedHandler();
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicant.linkToDeath exception", e);
return false;
}
- } catch (RemoteException e) {
- Log.e(TAG, "ISupplicant.linkToDeath exception", e);
- return false;
+ return true;
}
- return true;
}
private boolean initSupplicantService() {
@@ -241,25 +253,29 @@
}
private boolean linkToSupplicantStaIfaceDeath() {
- if (mISupplicantStaIface == null) return false;
- try {
- if (!mISupplicantStaIface.linkToDeath(mSupplicantDeathRecipient, 0)) {
- Log.wtf(TAG, "Error on linkToDeath on ISupplicantStaIface");
- supplicantServiceDiedHandler();
+ synchronized (mLock) {
+ if (mISupplicantStaIface == null) return false;
+ try {
+ if (!mISupplicantStaIface.linkToDeath(mSupplicantDeathRecipient, 0)) {
+ Log.wtf(TAG, "Error on linkToDeath on ISupplicantStaIface");
+ supplicantServiceDiedHandler();
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantStaIface.linkToDeath exception", e);
return false;
}
- } catch (RemoteException e) {
- Log.e(TAG, "ISupplicantStaIface.linkToDeath exception", e);
- return false;
+ return true;
}
- return true;
}
private int getCurrentNetworkId() {
- if (mCurrentNetworkLocalConfig == null) {
- return WifiConfiguration.INVALID_NETWORK_ID;
+ synchronized (mLock) {
+ if (mCurrentNetworkLocalConfig == null) {
+ return WifiConfiguration.INVALID_NETWORK_ID;
+ }
+ return mCurrentNetworkLocalConfig.networkId;
}
- return mCurrentNetworkLocalConfig.networkId;
}
private boolean initSupplicantStaIface() {
@@ -332,29 +348,39 @@
* Signals whether Initialization completed successfully.
*/
public boolean isInitializationStarted() {
- return mIServiceManager != null;
+ synchronized (mLock) {
+ return mIServiceManager != null;
+ }
}
/**
* Signals whether Initialization completed successfully.
*/
public boolean isInitializationComplete() {
- return mISupplicantStaIface != null;
+ synchronized (mLock) {
+ return mISupplicantStaIface != null;
+ }
}
/**
* Wrapper functions to access static HAL methods, created to be mockable in unit tests
*/
protected IServiceManager getServiceManagerMockable() throws RemoteException {
- return IServiceManager.getService();
+ synchronized (mLock) {
+ return IServiceManager.getService();
+ }
}
protected ISupplicant getSupplicantMockable() throws RemoteException {
- return ISupplicant.getService();
+ synchronized (mLock) {
+ return ISupplicant.getService();
+ }
}
protected ISupplicantStaIface getStaIfaceMockable(ISupplicantIface iface) {
- return ISupplicantStaIface.asInterface(iface.asBinder());
+ synchronized (mLock) {
+ return ISupplicantStaIface.asInterface(iface.asBinder());
+ }
}
/**
@@ -366,30 +392,32 @@
*/
private Pair<SupplicantStaNetworkHal, WifiConfiguration>
addNetworkAndSaveConfig(WifiConfiguration config) {
- logi("addSupplicantStaNetwork via HIDL");
- if (config == null) {
- loge("Cannot add NULL network!");
- return null;
- }
- SupplicantStaNetworkHal network = addNetwork();
- if (network == null) {
- loge("Failed to add a network!");
- return null;
- }
- boolean saveSuccess = false;
- try {
- saveSuccess = network.saveWifiConfiguration(config);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Exception while saving config params: " + config, e);
- }
- if (!saveSuccess) {
- loge("Failed to save variables for: " + config.configKey());
- if (!removeAllNetworks()) {
- loge("Failed to remove all networks on failure.");
+ synchronized (mLock) {
+ logi("addSupplicantStaNetwork via HIDL");
+ if (config == null) {
+ loge("Cannot add NULL network!");
+ return null;
}
- return null;
+ SupplicantStaNetworkHal network = addNetwork();
+ if (network == null) {
+ loge("Failed to add a network!");
+ return null;
+ }
+ boolean saveSuccess = false;
+ try {
+ saveSuccess = network.saveWifiConfiguration(config);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Exception while saving config params: " + config, e);
+ }
+ if (!saveSuccess) {
+ loge("Failed to save variables for: " + config.configKey());
+ if (!removeAllNetworks()) {
+ loge("Failed to remove all networks on failure.");
+ }
+ return null;
+ }
+ return new Pair(network, new WifiConfiguration(config));
}
- return new Pair(network, new WifiConfiguration(config));
}
/**
@@ -403,45 +431,33 @@
* @return {@code true} if it succeeds, {@code false} otherwise
*/
public boolean connectToNetwork(@NonNull WifiConfiguration config) {
- logd("connectToNetwork " + config.configKey());
- if (WifiConfigurationUtil.isSameNetwork(config, mCurrentNetworkLocalConfig)) {
- String networkSelectionBSSID = config.getNetworkSelectionStatus()
- .getNetworkSelectionBSSID();
- String networkSelectionBSSIDCurrent =
- mCurrentNetworkLocalConfig.getNetworkSelectionStatus()
- .getNetworkSelectionBSSID();
- if (Objects.equals(networkSelectionBSSID, networkSelectionBSSIDCurrent)) {
+ synchronized (mLock) {
+ logd("connectToNetwork " + config.configKey());
+ if (WifiConfigurationUtil.isSameNetwork(config, mCurrentNetworkLocalConfig)) {
logd("Network is already saved, will not trigger remove and add operation.");
} else {
- logd("Network is already saved, but need to update BSSID.");
- if (!setCurrentNetworkBssid(
- config.getNetworkSelectionStatus().getNetworkSelectionBSSID())) {
- loge("Failed to set current network BSSID.");
+ mCurrentNetworkRemoteHandle = null;
+ mCurrentNetworkLocalConfig = null;
+ if (!removeAllNetworks()) {
+ loge("Failed to remove existing networks");
return false;
}
- mCurrentNetworkLocalConfig = new WifiConfiguration(config);
+ Pair<SupplicantStaNetworkHal, WifiConfiguration> pair =
+ addNetworkAndSaveConfig(config);
+ if (pair == null) {
+ loge("Failed to add/save network configuration: " + config.configKey());
+ return false;
+ }
+ mCurrentNetworkRemoteHandle = pair.first;
+ mCurrentNetworkLocalConfig = pair.second;
}
- } else {
- mCurrentNetworkRemoteHandle = null;
- mCurrentNetworkLocalConfig = null;
- if (!removeAllNetworks()) {
- loge("Failed to remove existing networks");
- return false;
- }
- Pair<SupplicantStaNetworkHal, WifiConfiguration> pair = addNetworkAndSaveConfig(config);
- if (pair == null) {
- loge("Failed to add/save network configuration: " + config.configKey());
- return false;
- }
- mCurrentNetworkRemoteHandle = pair.first;
- mCurrentNetworkLocalConfig = pair.second;
- }
- if (!mCurrentNetworkRemoteHandle.select()) {
- loge("Failed to select network configuration: " + config.configKey());
- return false;
+ if (!mCurrentNetworkRemoteHandle.select()) {
+ loge("Failed to select network configuration: " + config.configKey());
+ return false;
+ }
+ return true;
}
- return true;
}
/**
@@ -457,22 +473,24 @@
* @return {@code true} if it succeeds, {@code false} otherwise
*/
public boolean roamToNetwork(WifiConfiguration config) {
- if (getCurrentNetworkId() != config.networkId) {
- Log.w(TAG, "Cannot roam to a different network, initiate new connection. "
- + "Current network ID: " + getCurrentNetworkId());
- return connectToNetwork(config);
+ synchronized (mLock) {
+ if (getCurrentNetworkId() != config.networkId) {
+ Log.w(TAG, "Cannot roam to a different network, initiate new connection. "
+ + "Current network ID: " + getCurrentNetworkId());
+ return connectToNetwork(config);
+ }
+ String bssid = config.getNetworkSelectionStatus().getNetworkSelectionBSSID();
+ logd("roamToNetwork" + config.configKey() + " (bssid " + bssid + ")");
+ if (!mCurrentNetworkRemoteHandle.setBssid(bssid)) {
+ loge("Failed to set new bssid on network: " + config.configKey());
+ return false;
+ }
+ if (!reassociate()) {
+ loge("Failed to trigger reassociate");
+ return false;
+ }
+ return true;
}
- String bssid = config.getNetworkSelectionStatus().getNetworkSelectionBSSID();
- logd("roamToNetwork" + config.configKey() + " (bssid " + bssid + ")");
- if (!mCurrentNetworkRemoteHandle.setBssid(bssid)) {
- loge("Failed to set new bssid on network: " + config.configKey());
- return false;
- }
- if (!reassociate()) {
- loge("Failed to trigger reassociate");
- return false;
- }
- return true;
}
/**
@@ -485,45 +503,48 @@
*/
public boolean loadNetworks(Map<String, WifiConfiguration> configs,
SparseArray<Map<String, String>> networkExtras) {
- List<Integer> networkIds = listNetworks();
- if (networkIds == null) {
- Log.e(TAG, "Failed to list networks");
- return false;
- }
- for (Integer networkId : networkIds) {
- SupplicantStaNetworkHal network = getNetwork(networkId);
- if (network == null) {
- Log.e(TAG, "Failed to get network with ID: " + networkId);
+ synchronized (mLock) {
+ List<Integer> networkIds = listNetworks();
+ if (networkIds == null) {
+ Log.e(TAG, "Failed to list networks");
return false;
}
- WifiConfiguration config = new WifiConfiguration();
- Map<String, String> networkExtra = new HashMap<>();
- boolean loadSuccess = false;
- try {
- loadSuccess = network.loadWifiConfiguration(config, networkExtra);
- } catch (IllegalArgumentException e) {
- Log.wtf(TAG, "Exception while loading config params: " + config, e);
- }
- if (!loadSuccess) {
- Log.e(TAG, "Failed to load wifi configuration for network with ID: " + networkId
- + ". Skipping...");
- continue;
- }
- // Set the default IP assignments.
- config.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
- config.setProxySettings(IpConfiguration.ProxySettings.NONE);
+ for (Integer networkId : networkIds) {
+ SupplicantStaNetworkHal network = getNetwork(networkId);
+ if (network == null) {
+ Log.e(TAG, "Failed to get network with ID: " + networkId);
+ return false;
+ }
+ WifiConfiguration config = new WifiConfiguration();
+ Map<String, String> networkExtra = new HashMap<>();
+ boolean loadSuccess = false;
+ try {
+ loadSuccess = network.loadWifiConfiguration(config, networkExtra);
+ } catch (IllegalArgumentException e) {
+ Log.wtf(TAG, "Exception while loading config params: " + config, e);
+ }
+ if (!loadSuccess) {
+ Log.e(TAG, "Failed to load wifi configuration for network with ID: " + networkId
+ + ". Skipping...");
+ continue;
+ }
+ // Set the default IP assignments.
+ config.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
+ config.setProxySettings(IpConfiguration.ProxySettings.NONE);
- networkExtras.put(networkId, networkExtra);
- String configKey = networkExtra.get(SupplicantStaNetworkHal.ID_STRING_KEY_CONFIG_KEY);
- final WifiConfiguration duplicateConfig = configs.put(configKey, config);
- if (duplicateConfig != null) {
- // The network is already known. Overwrite the duplicate entry.
- Log.i(TAG, "Replacing duplicate network: " + duplicateConfig.networkId);
- removeNetwork(duplicateConfig.networkId);
- networkExtras.remove(duplicateConfig.networkId);
+ networkExtras.put(networkId, networkExtra);
+ String configKey =
+ networkExtra.get(SupplicantStaNetworkHal.ID_STRING_KEY_CONFIG_KEY);
+ final WifiConfiguration duplicateConfig = configs.put(configKey, config);
+ if (duplicateConfig != null) {
+ // The network is already known. Overwrite the duplicate entry.
+ Log.i(TAG, "Replacing duplicate network: " + duplicateConfig.networkId);
+ removeNetwork(duplicateConfig.networkId);
+ networkExtras.remove(duplicateConfig.networkId);
+ }
}
+ return true;
}
- return true;
}
/**
@@ -557,12 +578,12 @@
return false;
}
}
+ // Reset current network info. Probably not needed once we add support to remove/reset
+ // current network on receiving disconnection event from supplicant (b/32898136).
+ mCurrentNetworkLocalConfig = null;
+ mCurrentNetworkRemoteHandle = null;
+ return true;
}
- // Reset current network info. Probably not needed once we add support to remove/reset
- // current network on receiving disconnection event from supplicant (b/32898136).
- mCurrentNetworkLocalConfig = null;
- mCurrentNetworkRemoteHandle = null;
- return true;
}
/**
@@ -572,8 +593,10 @@
* @return true if succeeds, false otherwise.
*/
public boolean setCurrentNetworkBssid(String bssidStr) {
- if (mCurrentNetworkRemoteHandle == null) return false;
- return mCurrentNetworkRemoteHandle.setBssid(bssidStr);
+ synchronized (mLock) {
+ if (mCurrentNetworkRemoteHandle == null) return false;
+ return mCurrentNetworkRemoteHandle.setBssid(bssidStr);
+ }
}
/**
@@ -582,8 +605,10 @@
* @return Hex string corresponding to the WPS NFC token.
*/
public String getCurrentNetworkWpsNfcConfigurationToken() {
- if (mCurrentNetworkRemoteHandle == null) return null;
- return mCurrentNetworkRemoteHandle.getWpsNfcConfigurationToken();
+ synchronized (mLock) {
+ if (mCurrentNetworkRemoteHandle == null) return null;
+ return mCurrentNetworkRemoteHandle.getWpsNfcConfigurationToken();
+ }
}
/**
@@ -592,8 +617,10 @@
* @return anonymous identity string if succeeds, null otherwise.
*/
public String getCurrentNetworkEapAnonymousIdentity() {
- if (mCurrentNetworkRemoteHandle == null) return null;
- return mCurrentNetworkRemoteHandle.fetchEapAnonymousIdentity();
+ synchronized (mLock) {
+ if (mCurrentNetworkRemoteHandle == null) return null;
+ return mCurrentNetworkRemoteHandle.fetchEapAnonymousIdentity();
+ }
}
/**
@@ -603,8 +630,10 @@
* @return true if succeeds, false otherwise.
*/
public boolean sendCurrentNetworkEapIdentityResponse(String identityStr) {
- if (mCurrentNetworkRemoteHandle == null) return false;
- return mCurrentNetworkRemoteHandle.sendNetworkEapIdentityResponse(identityStr);
+ synchronized (mLock) {
+ if (mCurrentNetworkRemoteHandle == null) return false;
+ return mCurrentNetworkRemoteHandle.sendNetworkEapIdentityResponse(identityStr);
+ }
}
/**
@@ -614,8 +643,10 @@
* @return true if succeeds, false otherwise.
*/
public boolean sendCurrentNetworkEapSimGsmAuthResponse(String paramsStr) {
- if (mCurrentNetworkRemoteHandle == null) return false;
- return mCurrentNetworkRemoteHandle.sendNetworkEapSimGsmAuthResponse(paramsStr);
+ synchronized (mLock) {
+ if (mCurrentNetworkRemoteHandle == null) return false;
+ return mCurrentNetworkRemoteHandle.sendNetworkEapSimGsmAuthResponse(paramsStr);
+ }
}
/**
@@ -624,8 +655,10 @@
* @return true if succeeds, false otherwise.
*/
public boolean sendCurrentNetworkEapSimGsmAuthFailure() {
- if (mCurrentNetworkRemoteHandle == null) return false;
- return mCurrentNetworkRemoteHandle.sendNetworkEapSimGsmAuthFailure();
+ synchronized (mLock) {
+ if (mCurrentNetworkRemoteHandle == null) return false;
+ return mCurrentNetworkRemoteHandle.sendNetworkEapSimGsmAuthFailure();
+ }
}
/**
@@ -635,8 +668,10 @@
* @return true if succeeds, false otherwise.
*/
public boolean sendCurrentNetworkEapSimUmtsAuthResponse(String paramsStr) {
- if (mCurrentNetworkRemoteHandle == null) return false;
- return mCurrentNetworkRemoteHandle.sendNetworkEapSimUmtsAuthResponse(paramsStr);
+ synchronized (mLock) {
+ if (mCurrentNetworkRemoteHandle == null) return false;
+ return mCurrentNetworkRemoteHandle.sendNetworkEapSimUmtsAuthResponse(paramsStr);
+ }
}
/**
@@ -646,8 +681,10 @@
* @return true if succeeds, false otherwise.
*/
public boolean sendCurrentNetworkEapSimUmtsAutsResponse(String paramsStr) {
- if (mCurrentNetworkRemoteHandle == null) return false;
- return mCurrentNetworkRemoteHandle.sendNetworkEapSimUmtsAutsResponse(paramsStr);
+ synchronized (mLock) {
+ if (mCurrentNetworkRemoteHandle == null) return false;
+ return mCurrentNetworkRemoteHandle.sendNetworkEapSimUmtsAutsResponse(paramsStr);
+ }
}
/**
@@ -656,8 +693,10 @@
* @return true if succeeds, false otherwise.
*/
public boolean sendCurrentNetworkEapSimUmtsAuthFailure() {
- if (mCurrentNetworkRemoteHandle == null) return false;
- return mCurrentNetworkRemoteHandle.sendNetworkEapSimUmtsAuthFailure();
+ synchronized (mLock) {
+ if (mCurrentNetworkRemoteHandle == null) return false;
+ return mCurrentNetworkRemoteHandle.sendNetworkEapSimUmtsAuthFailure();
+ }
}
/**
@@ -717,13 +756,15 @@
*/
protected SupplicantStaNetworkHal getStaNetworkMockable(
ISupplicantStaNetwork iSupplicantStaNetwork) {
- SupplicantStaNetworkHal network =
- new SupplicantStaNetworkHal(iSupplicantStaNetwork, mIfaceName, mContext,
- mWifiMonitor);
- if (network != null) {
- network.enableVerboseLogging(mVerboseLoggingEnabled);
+ synchronized (mLock) {
+ SupplicantStaNetworkHal network =
+ new SupplicantStaNetworkHal(iSupplicantStaNetwork, mIfaceName, mContext,
+ mWifiMonitor);
+ if (network != null) {
+ network.enableVerboseLogging(mVerboseLoggingEnabled);
+ }
+ return network;
}
- return network;
}
/**
@@ -819,25 +860,27 @@
* @return true if request is sent successfully, false otherwise.
*/
public boolean setWpsDeviceType(String typeStr) {
- try {
- Matcher match = WPS_DEVICE_TYPE_PATTERN.matcher(typeStr);
- if (!match.find() || match.groupCount() != 3) {
- Log.e(TAG, "Malformed WPS device type " + typeStr);
+ synchronized (mLock) {
+ try {
+ Matcher match = WPS_DEVICE_TYPE_PATTERN.matcher(typeStr);
+ if (!match.find() || match.groupCount() != 3) {
+ Log.e(TAG, "Malformed WPS device type " + typeStr);
+ return false;
+ }
+ short categ = Short.parseShort(match.group(1));
+ byte[] oui = NativeUtil.hexStringToByteArray(match.group(2));
+ short subCateg = Short.parseShort(match.group(3));
+
+ byte[] bytes = new byte[8];
+ ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
+ byteBuffer.putShort(categ);
+ byteBuffer.put(oui);
+ byteBuffer.putShort(subCateg);
+ return setWpsDeviceType(bytes);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + typeStr, e);
return false;
}
- short categ = Short.parseShort(match.group(1));
- byte[] oui = NativeUtil.hexStringToByteArray(match.group(2));
- short subCateg = Short.parseShort(match.group(3));
-
- byte[] bytes = new byte[8];
- ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
- byteBuffer.putShort(categ);
- byteBuffer.put(oui);
- byteBuffer.putShort(subCateg);
- return setWpsDeviceType(bytes);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Illegal argument " + typeStr, e);
- return false;
}
}
@@ -942,12 +985,14 @@
* @return true if request is sent successfully, false otherwise.
*/
public boolean setWpsConfigMethods(String configMethodsStr) {
- short configMethodsMask = 0;
- String[] configMethodsStrArr = configMethodsStr.split("\\s+");
- for (int i = 0; i < configMethodsStrArr.length; i++) {
- configMethodsMask |= stringToWpsConfigMethod(configMethodsStrArr[i]);
+ synchronized (mLock) {
+ short configMethodsMask = 0;
+ String[] configMethodsStrArr = configMethodsStr.split("\\s+");
+ for (int i = 0; i < configMethodsStrArr.length; i++) {
+ configMethodsMask |= stringToWpsConfigMethod(configMethodsStrArr[i]);
+ }
+ return setWpsConfigMethods(configMethodsMask);
}
- return setWpsConfigMethods(configMethodsMask);
}
private boolean setWpsConfigMethods(short configMethods) {
@@ -1048,11 +1093,13 @@
* @return true if request is sent successfully, false otherwise.
*/
public boolean initiateTdlsDiscover(String macAddress) {
- try {
- return initiateTdlsDiscover(NativeUtil.macAddressToByteArray(macAddress));
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Illegal argument " + macAddress, e);
- return false;
+ synchronized (mLock) {
+ try {
+ return initiateTdlsDiscover(NativeUtil.macAddressToByteArray(macAddress));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + macAddress, e);
+ return false;
+ }
}
}
/** See ISupplicantStaIface.hal for documentation */
@@ -1077,11 +1124,13 @@
* @return true if request is sent successfully, false otherwise.
*/
public boolean initiateTdlsSetup(String macAddress) {
- try {
- return initiateTdlsSetup(NativeUtil.macAddressToByteArray(macAddress));
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Illegal argument " + macAddress, e);
- return false;
+ synchronized (mLock) {
+ try {
+ return initiateTdlsSetup(NativeUtil.macAddressToByteArray(macAddress));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + macAddress, e);
+ return false;
+ }
}
}
/** See ISupplicantStaIface.hal for documentation */
@@ -1105,11 +1154,13 @@
* @return true if request is sent successfully, false otherwise.
*/
public boolean initiateTdlsTeardown(String macAddress) {
- try {
- return initiateTdlsTeardown(NativeUtil.macAddressToByteArray(macAddress));
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Illegal argument " + macAddress, e);
- return false;
+ synchronized (mLock) {
+ try {
+ return initiateTdlsTeardown(NativeUtil.macAddressToByteArray(macAddress));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + macAddress, e);
+ return false;
+ }
}
}
@@ -1138,12 +1189,14 @@
*/
public boolean initiateAnqpQuery(String bssid, ArrayList<Short> infoElements,
ArrayList<Integer> hs20SubTypes) {
- try {
- return initiateAnqpQuery(
- NativeUtil.macAddressToByteArray(bssid), infoElements, hs20SubTypes);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Illegal argument " + bssid, e);
- return false;
+ synchronized (mLock) {
+ try {
+ return initiateAnqpQuery(
+ NativeUtil.macAddressToByteArray(bssid), infoElements, hs20SubTypes);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + bssid, e);
+ return false;
+ }
}
}
@@ -1172,11 +1225,13 @@
* @return true if request is sent successfully, false otherwise.
*/
public boolean initiateHs20IconQuery(String bssid, String fileName) {
- try {
- return initiateHs20IconQuery(NativeUtil.macAddressToByteArray(bssid), fileName);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Illegal argument " + bssid, e);
- return false;
+ synchronized (mLock) {
+ try {
+ return initiateHs20IconQuery(NativeUtil.macAddressToByteArray(bssid), fileName);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + bssid, e);
+ return false;
+ }
}
}
@@ -1266,19 +1321,21 @@
* @return true if request is sent successfully, false otherwise.
*/
public boolean addRxFilter(int type) {
- byte halType;
- switch (type) {
- case WifiNative.RX_FILTER_TYPE_V4_MULTICAST:
- halType = ISupplicantStaIface.RxFilterType.V4_MULTICAST;
- break;
- case WifiNative.RX_FILTER_TYPE_V6_MULTICAST:
- halType = ISupplicantStaIface.RxFilterType.V6_MULTICAST;
- break;
- default:
- Log.e(TAG, "Invalid Rx Filter type: " + type);
- return false;
+ synchronized (mLock) {
+ byte halType;
+ switch (type) {
+ case WifiNative.RX_FILTER_TYPE_V4_MULTICAST:
+ halType = ISupplicantStaIface.RxFilterType.V4_MULTICAST;
+ break;
+ case WifiNative.RX_FILTER_TYPE_V6_MULTICAST:
+ halType = ISupplicantStaIface.RxFilterType.V6_MULTICAST;
+ break;
+ default:
+ Log.e(TAG, "Invalid Rx Filter type: " + type);
+ return false;
+ }
+ return addRxFilter(halType);
}
- return addRxFilter(halType);
}
public boolean addRxFilter(byte type) {
@@ -1303,19 +1360,21 @@
* @return true if request is sent successfully, false otherwise.
*/
public boolean removeRxFilter(int type) {
- byte halType;
- switch (type) {
- case WifiNative.RX_FILTER_TYPE_V4_MULTICAST:
- halType = ISupplicantStaIface.RxFilterType.V4_MULTICAST;
- break;
- case WifiNative.RX_FILTER_TYPE_V6_MULTICAST:
- halType = ISupplicantStaIface.RxFilterType.V6_MULTICAST;
- break;
- default:
- Log.e(TAG, "Invalid Rx Filter type: " + type);
- return false;
+ synchronized (mLock) {
+ byte halType;
+ switch (type) {
+ case WifiNative.RX_FILTER_TYPE_V4_MULTICAST:
+ halType = ISupplicantStaIface.RxFilterType.V4_MULTICAST;
+ break;
+ case WifiNative.RX_FILTER_TYPE_V6_MULTICAST:
+ halType = ISupplicantStaIface.RxFilterType.V6_MULTICAST;
+ break;
+ default:
+ Log.e(TAG, "Invalid Rx Filter type: " + type);
+ return false;
+ }
+ return removeRxFilter(halType);
}
- return removeRxFilter(halType);
}
public boolean removeRxFilter(byte type) {
@@ -1341,22 +1400,24 @@
* @return true if request is sent successfully, false otherwise.
*/
public boolean setBtCoexistenceMode(int mode) {
- byte halMode;
- switch (mode) {
- case WifiNative.BLUETOOTH_COEXISTENCE_MODE_ENABLED:
- halMode = ISupplicantStaIface.BtCoexistenceMode.ENABLED;
- break;
- case WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED:
- halMode = ISupplicantStaIface.BtCoexistenceMode.DISABLED;
- break;
- case WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE:
- halMode = ISupplicantStaIface.BtCoexistenceMode.SENSE;
- break;
- default:
- Log.e(TAG, "Invalid Bt Coex mode: " + mode);
- return false;
+ synchronized (mLock) {
+ byte halMode;
+ switch (mode) {
+ case WifiNative.BLUETOOTH_COEXISTENCE_MODE_ENABLED:
+ halMode = ISupplicantStaIface.BtCoexistenceMode.ENABLED;
+ break;
+ case WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED:
+ halMode = ISupplicantStaIface.BtCoexistenceMode.DISABLED;
+ break;
+ case WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE:
+ halMode = ISupplicantStaIface.BtCoexistenceMode.SENSE;
+ break;
+ default:
+ Log.e(TAG, "Invalid Bt Coex mode: " + mode);
+ return false;
+ }
+ return setBtCoexistenceMode(halMode);
}
- return setBtCoexistenceMode(halMode);
}
private boolean setBtCoexistenceMode(byte mode) {
@@ -1420,8 +1481,10 @@
* @return true if request is sent successfully, false otherwise.
*/
public boolean setCountryCode(String codeStr) {
- if (TextUtils.isEmpty(codeStr)) return false;
- return setCountryCode(NativeUtil.stringToByteArray(codeStr));
+ synchronized (mLock) {
+ if (TextUtils.isEmpty(codeStr)) return false;
+ return setCountryCode(NativeUtil.stringToByteArray(codeStr));
+ }
}
/** See ISupplicantStaIface.hal for documentation */
@@ -1447,12 +1510,14 @@
* @return true if request is sent successfully, false otherwise.
*/
public boolean startWpsRegistrar(String bssidStr, String pin) {
- if (TextUtils.isEmpty(bssidStr) || TextUtils.isEmpty(pin)) return false;
- try {
- return startWpsRegistrar(NativeUtil.macAddressToByteArray(bssidStr), pin);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Illegal argument " + bssidStr, e);
- return false;
+ synchronized (mLock) {
+ if (TextUtils.isEmpty(bssidStr) || TextUtils.isEmpty(pin)) return false;
+ try {
+ return startWpsRegistrar(NativeUtil.macAddressToByteArray(bssidStr), pin);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + bssidStr, e);
+ return false;
+ }
}
}
@@ -1478,11 +1543,13 @@
* @return true if request is sent successfully, false otherwise.
*/
public boolean startWpsPbc(String bssidStr) {
- try {
- return startWpsPbc(NativeUtil.macAddressToByteArray(bssidStr));
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Illegal argument " + bssidStr, e);
- return false;
+ synchronized (mLock) {
+ try {
+ return startWpsPbc(NativeUtil.macAddressToByteArray(bssidStr));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + bssidStr, e);
+ return false;
+ }
}
}
@@ -1529,11 +1596,13 @@
* @return new pin generated on success, null otherwise.
*/
public String startWpsPinDisplay(String bssidStr) {
- try {
- return startWpsPinDisplay(NativeUtil.macAddressToByteArray(bssidStr));
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Illegal argument " + bssidStr, e);
- return null;
+ synchronized (mLock) {
+ try {
+ return startWpsPinDisplay(NativeUtil.macAddressToByteArray(bssidStr));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + bssidStr, e);
+ return null;
+ }
}
}
@@ -1618,10 +1687,12 @@
* @return true if request is sent successfully, false otherwise.
*/
public boolean setLogLevel(boolean turnOnVerbose) {
- int logLevel = turnOnVerbose
- ? ISupplicant.DebugLevel.DEBUG
- : ISupplicant.DebugLevel.INFO;
- return setDebugParams(logLevel, false, false);
+ synchronized (mLock) {
+ int logLevel = turnOnVerbose
+ ? ISupplicant.DebugLevel.DEBUG
+ : ISupplicant.DebugLevel.INFO;
+ return setDebugParams(logLevel, false, false);
+ }
}
/** See ISupplicant.hal for documentation */
@@ -1648,10 +1719,12 @@
* @return true if request is sent successfully, false otherwise.
*/
public boolean setConcurrencyPriority(boolean isStaHigherPriority) {
- if (isStaHigherPriority) {
- return setConcurrencyPriority(IfaceType.STA);
- } else {
- return setConcurrencyPriority(IfaceType.P2P);
+ synchronized (mLock) {
+ if (isStaHigherPriority) {
+ return setConcurrencyPriority(IfaceType.STA);
+ } else {
+ return setConcurrencyPriority(IfaceType.P2P);
+ }
}
}
@@ -1674,22 +1747,26 @@
* Returns false if Supplicant is null, and logs failure to call methodStr
*/
private boolean checkSupplicantAndLogFailure(final String methodStr) {
- if (mISupplicant == null) {
- Log.e(TAG, "Can't call " + methodStr + ", ISupplicant is null");
- return false;
+ synchronized (mLock) {
+ if (mISupplicant == null) {
+ Log.e(TAG, "Can't call " + methodStr + ", ISupplicant is null");
+ return false;
+ }
+ return true;
}
- return true;
}
/**
* Returns false if SupplicantStaIface is null, and logs failure to call methodStr
*/
private boolean checkSupplicantStaIfaceAndLogFailure(final String methodStr) {
- if (mISupplicantStaIface == null) {
- Log.e(TAG, "Can't call " + methodStr + ", ISupplicantStaIface is null");
- return false;
+ synchronized (mLock) {
+ if (mISupplicantStaIface == null) {
+ Log.e(TAG, "Can't call " + methodStr + ", ISupplicantStaIface is null");
+ return false;
+ }
+ return true;
}
- return true;
}
/**
@@ -1698,15 +1775,17 @@
*/
private boolean checkStatusAndLogFailure(SupplicantStatus status,
final String methodStr) {
- if (status.code != SupplicantStatusCode.SUCCESS) {
- Log.e(TAG, "ISupplicantStaIface." + methodStr + " failed: "
- + supplicantStatusCodeToString(status.code) + ", " + status.debugMessage);
- return false;
- } else {
- if (mVerboseLoggingEnabled) {
- Log.d(TAG, "ISupplicantStaIface." + methodStr + " succeeded");
+ synchronized (mLock) {
+ if (status.code != SupplicantStatusCode.SUCCESS) {
+ Log.e(TAG, "ISupplicantStaIface." + methodStr + " failed: "
+ + supplicantStatusCodeToString(status.code) + ", " + status.debugMessage);
+ return false;
+ } else {
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "ISupplicantStaIface." + methodStr + " succeeded");
+ }
+ return true;
}
- return true;
}
}
@@ -1714,15 +1793,19 @@
* Helper function to log callbacks.
*/
private void logCallback(final String methodStr) {
- if (mVerboseLoggingEnabled) {
- Log.d(TAG, "ISupplicantStaIfaceCallback." + methodStr + " received");
+ synchronized (mLock) {
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "ISupplicantStaIfaceCallback." + methodStr + " received");
+ }
}
}
private void handleRemoteException(RemoteException e, String methodStr) {
- supplicantServiceDiedHandler();
- Log.e(TAG, "ISupplicantStaIface." + methodStr + " failed with exception", e);
+ synchronized (mLock) {
+ supplicantServiceDiedHandler();
+ Log.e(TAG, "ISupplicantStaIface." + methodStr + " failed with exception", e);
+ }
}
/**
@@ -1851,15 +1934,17 @@
*/
private ANQPElement parseAnqpElement(Constants.ANQPElementType infoID,
ArrayList<Byte> payload) {
- try {
- return Constants.getANQPElementID(infoID) != null
- ? ANQPParser.parseElement(
- infoID, ByteBuffer.wrap(NativeUtil.byteArrayFromArrayList(payload)))
- : ANQPParser.parseHS20Element(
- infoID, ByteBuffer.wrap(NativeUtil.byteArrayFromArrayList(payload)));
- } catch (IOException | BufferUnderflowException e) {
- Log.e(TAG, "Failed parsing ANQP element payload: " + infoID, e);
- return null;
+ synchronized (mLock) {
+ try {
+ return Constants.getANQPElementID(infoID) != null
+ ? ANQPParser.parseElement(
+ infoID, ByteBuffer.wrap(NativeUtil.byteArrayFromArrayList(payload)))
+ : ANQPParser.parseHS20Element(
+ infoID, ByteBuffer.wrap(NativeUtil.byteArrayFromArrayList(payload)));
+ } catch (IOException | BufferUnderflowException e) {
+ Log.e(TAG, "Failed parsing ANQP element payload: " + infoID, e);
+ return null;
+ }
}
}
@@ -1873,28 +1958,34 @@
private void addAnqpElementToMap(Map<Constants.ANQPElementType, ANQPElement> elementsMap,
Constants.ANQPElementType infoID,
ArrayList<Byte> payload) {
- if (payload == null || payload.isEmpty()) return;
- ANQPElement element = parseAnqpElement(infoID, payload);
- if (element != null) {
- elementsMap.put(infoID, element);
+ synchronized (mLock) {
+ if (payload == null || payload.isEmpty()) return;
+ ANQPElement element = parseAnqpElement(infoID, payload);
+ if (element != null) {
+ elementsMap.put(infoID, element);
+ }
}
}
@Override
public void onNetworkAdded(int id) {
- logCallback("onNetworkAdded");
+ synchronized (mLock) {
+ logCallback("onNetworkAdded");
+ }
}
@Override
public void onNetworkRemoved(int id) {
- logCallback("onNetworkRemoved");
+ synchronized (mLock) {
+ logCallback("onNetworkRemoved");
+ }
}
@Override
public void onStateChanged(int newState, byte[/* 6 */] bssid, int id,
ArrayList<Byte> ssid) {
- logCallback("onStateChanged");
synchronized (mLock) {
+ logCallback("onStateChanged");
SupplicantState newSupplicantState = supplicantHidlStateToFrameworkState(newState);
WifiSsid wifiSsid =
WifiSsid.createFromByteArray(NativeUtil.byteArrayFromArrayList(ssid));
@@ -1913,8 +2004,8 @@
public void onAnqpQueryDone(byte[/* 6 */] bssid,
ISupplicantStaIfaceCallback.AnqpData data,
ISupplicantStaIfaceCallback.Hs20AnqpData hs20Data) {
- logCallback("onAnqpQueryDone");
synchronized (mLock) {
+ logCallback("onAnqpQueryDone");
Map<Constants.ANQPElementType, ANQPElement> elementsMap = new HashMap<>();
addAnqpElementToMap(elementsMap, ANQPVenueName, data.venueName);
addAnqpElementToMap(elementsMap, ANQPRoamingConsortium, data.roamingConsortium);
@@ -1935,8 +2026,8 @@
@Override
public void onHs20IconQueryDone(byte[/* 6 */] bssid, String fileName,
ArrayList<Byte> data) {
- logCallback("onHs20IconQueryDone");
synchronized (mLock) {
+ logCallback("onHs20IconQueryDone");
mWifiMonitor.broadcastIconDoneEvent(
mIfaceName,
new IconEvent(NativeUtil.macAddressToLong(bssid), fileName, data.size(),
@@ -1946,8 +2037,8 @@
@Override
public void onHs20SubscriptionRemediation(byte[/* 6 */] bssid, byte osuMethod, String url) {
- logCallback("onHs20SubscriptionRemediation");
synchronized (mLock) {
+ logCallback("onHs20SubscriptionRemediation");
mWifiMonitor.broadcastWnmEvent(
mIfaceName,
new WnmData(NativeUtil.macAddressToLong(bssid), url, osuMethod));
@@ -1957,8 +2048,8 @@
@Override
public void onHs20DeauthImminentNotice(byte[/* 6 */] bssid, int reasonCode,
int reAuthDelayInSec, String url) {
- logCallback("onHs20DeauthImminentNotice");
synchronized (mLock) {
+ logCallback("onHs20DeauthImminentNotice");
mWifiMonitor.broadcastWnmEvent(
mIfaceName,
new WnmData(NativeUtil.macAddressToLong(bssid), url,
@@ -1968,8 +2059,8 @@
@Override
public void onDisconnected(byte[/* 6 */] bssid, boolean locallyGenerated, int reasonCode) {
- logCallback("onDisconnected");
synchronized (mLock) {
+ logCallback("onDisconnected");
if (mVerboseLoggingEnabled) {
Log.e(TAG, "onDisconnected 4way=" + mStateIsFourway
+ " locallyGenerated=" + locallyGenerated
@@ -1988,8 +2079,8 @@
@Override
public void onAssociationRejected(byte[/* 6 */] bssid, int statusCode, boolean timedOut) {
- logCallback("onAssociationRejected");
synchronized (mLock) {
+ logCallback("onAssociationRejected");
mWifiMonitor.broadcastAssociationRejectionEvent(mIfaceName, statusCode, timedOut,
NativeUtil.macAddressFromByteArray(bssid));
}
@@ -1997,8 +2088,8 @@
@Override
public void onAuthenticationTimeout(byte[/* 6 */] bssid) {
- logCallback("onAuthenticationTimeout");
synchronized (mLock) {
+ logCallback("onAuthenticationTimeout");
mWifiMonitor.broadcastAuthenticationFailureEvent(
mIfaceName, WifiManager.ERROR_AUTH_FAILURE_TIMEOUT);
}
@@ -2006,8 +2097,8 @@
@Override
public void onBssidChanged(byte reason, byte[/* 6 */] bssid) {
- logCallback("onBssidChanged");
synchronized (mLock) {
+ logCallback("onBssidChanged");
if (reason == BssidChangeReason.ASSOC_START) {
mWifiMonitor.broadcastTargetBssidEvent(
mIfaceName, NativeUtil.macAddressFromByteArray(bssid));
@@ -2020,8 +2111,8 @@
@Override
public void onEapFailure() {
- logCallback("onEapFailure");
synchronized (mLock) {
+ logCallback("onEapFailure");
mWifiMonitor.broadcastAuthenticationFailureEvent(
mIfaceName, WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE);
}
@@ -2037,8 +2128,8 @@
@Override
public void onWpsEventFail(byte[/* 6 */] bssid, short configError, short errorInd) {
- logCallback("onWpsEventFail");
synchronized (mLock) {
+ logCallback("onWpsEventFail");
if (configError == WpsConfigError.MSG_TIMEOUT
&& errorInd == WpsErrorIndication.NO_ERROR) {
mWifiMonitor.broadcastWpsTimeoutEvent(mIfaceName);
@@ -2050,32 +2141,36 @@
@Override
public void onWpsEventPbcOverlap() {
- logCallback("onWpsEventPbcOverlap");
synchronized (mLock) {
+ logCallback("onWpsEventPbcOverlap");
mWifiMonitor.broadcastWpsOverlapEvent(mIfaceName);
}
}
@Override
public void onExtRadioWorkStart(int id) {
- logCallback("onExtRadioWorkStart");
+ synchronized (mLock) {
+ logCallback("onExtRadioWorkStart");
+ }
}
@Override
public void onExtRadioWorkTimeout(int id) {
- logCallback("onExtRadioWorkTimeout");
+ synchronized (mLock) {
+ logCallback("onExtRadioWorkTimeout");
+ }
}
}
- private void logd(String s) {
+ private static void logd(String s) {
Log.d(TAG, s);
}
- private void logi(String s) {
+ private static void logi(String s) {
Log.i(TAG, s);
}
- private void loge(String s) {
+ private static void loge(String s) {
Log.e(TAG, s);
}
}
diff --git a/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java b/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java
index 6e7d98c..61ec9b3 100644
--- a/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java
+++ b/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java
@@ -46,13 +46,18 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import javax.annotation.concurrent.ThreadSafe;
+
/**
* Wrapper class for ISupplicantStaNetwork HAL calls. Gets and sets supplicant sta network variables
* and interacts with networks.
* Public fields should be treated as invalid until their 'get' method is called, which will set the
* value if it returns true
+ * To maintain thread-safety, the locking protocol is that every non-static method (regardless of
+ * access level) acquires mLock.
*/
+@ThreadSafe
public class SupplicantStaNetworkHal {
private static final String TAG = "SupplicantStaNetworkHal";
@VisibleForTesting
@@ -137,7 +142,9 @@
* @param enable true to enable, false to disable.
*/
void enableVerboseLogging(boolean enable) {
- mVerboseLoggingEnabled = enable;
+ synchronized (mLock) {
+ mVerboseLoggingEnabled = enable;
+ }
}
/**
@@ -150,90 +157,92 @@
*/
public boolean loadWifiConfiguration(WifiConfiguration config,
Map<String, String> networkExtras) {
- if (config == null) return false;
- /** SSID */
- config.SSID = null;
- if (getSsid() && !ArrayUtils.isEmpty(mSsid)) {
- config.SSID = NativeUtil.encodeSsid(mSsid);
- } else {
- Log.e(TAG, "failed to read ssid");
- return false;
- }
- /** Network Id */
- config.networkId = -1;
- if (getId()) {
- config.networkId = mNetworkId;
- } else {
- Log.e(TAG, "getId failed");
- return false;
- }
- /** BSSID */
- config.getNetworkSelectionStatus().setNetworkSelectionBSSID(null);
- if (getBssid() && !ArrayUtils.isEmpty(mBssid)) {
- config.getNetworkSelectionStatus().setNetworkSelectionBSSID(
- NativeUtil.macAddressFromByteArray(mBssid));
- }
- /** Scan SSID (Is Hidden Network?) */
- config.hiddenSSID = false;
- if (getScanSsid()) {
- config.hiddenSSID = mScanSsid;
- }
- /** Require PMF*/
- config.requirePMF = false;
- if (getRequirePmf()) {
- config.requirePMF = mRequirePmf;
- }
- /** WEP keys **/
- config.wepTxKeyIndex = -1;
- if (getWepTxKeyIdx()) {
- config.wepTxKeyIndex = mWepTxKeyIdx;
- }
- for (int i = 0; i < 4; i++) {
- config.wepKeys[i] = null;
- if (getWepKey(i) && !ArrayUtils.isEmpty(mWepKey)) {
- config.wepKeys[i] = NativeUtil.bytesToHexOrQuotedAsciiString(mWepKey);
+ synchronized (mLock) {
+ if (config == null) return false;
+ /** SSID */
+ config.SSID = null;
+ if (getSsid() && !ArrayUtils.isEmpty(mSsid)) {
+ config.SSID = NativeUtil.encodeSsid(mSsid);
+ } else {
+ Log.e(TAG, "failed to read ssid");
+ return false;
}
+ /** Network Id */
+ config.networkId = -1;
+ if (getId()) {
+ config.networkId = mNetworkId;
+ } else {
+ Log.e(TAG, "getId failed");
+ return false;
+ }
+ /** BSSID */
+ config.getNetworkSelectionStatus().setNetworkSelectionBSSID(null);
+ if (getBssid() && !ArrayUtils.isEmpty(mBssid)) {
+ config.getNetworkSelectionStatus().setNetworkSelectionBSSID(
+ NativeUtil.macAddressFromByteArray(mBssid));
+ }
+ /** Scan SSID (Is Hidden Network?) */
+ config.hiddenSSID = false;
+ if (getScanSsid()) {
+ config.hiddenSSID = mScanSsid;
+ }
+ /** Require PMF*/
+ config.requirePMF = false;
+ if (getRequirePmf()) {
+ config.requirePMF = mRequirePmf;
+ }
+ /** WEP keys **/
+ config.wepTxKeyIndex = -1;
+ if (getWepTxKeyIdx()) {
+ config.wepTxKeyIndex = mWepTxKeyIdx;
+ }
+ for (int i = 0; i < 4; i++) {
+ config.wepKeys[i] = null;
+ if (getWepKey(i) && !ArrayUtils.isEmpty(mWepKey)) {
+ config.wepKeys[i] = NativeUtil.bytesToHexOrQuotedAsciiString(mWepKey);
+ }
+ }
+ /** PSK pass phrase */
+ config.preSharedKey = null;
+ if (getPskPassphrase() && !TextUtils.isEmpty(mPskPassphrase)) {
+ config.preSharedKey = NativeUtil.addEnclosingQuotes(mPskPassphrase);
+ } else if (getPsk() && !ArrayUtils.isEmpty(mPsk)) {
+ config.preSharedKey = NativeUtil.hexStringFromByteArray(mPsk);
+ }
+ /** allowedKeyManagement */
+ if (getKeyMgmt()) {
+ BitSet keyMgmtMask = supplicantToWifiConfigurationKeyMgmtMask(mKeyMgmtMask);
+ config.allowedKeyManagement = removeFastTransitionFlags(keyMgmtMask);
+ }
+ /** allowedProtocols */
+ if (getProto()) {
+ config.allowedProtocols =
+ supplicantToWifiConfigurationProtoMask(mProtoMask);
+ }
+ /** allowedAuthAlgorithms */
+ if (getAuthAlg()) {
+ config.allowedAuthAlgorithms =
+ supplicantToWifiConfigurationAuthAlgMask(mAuthAlgMask);
+ }
+ /** allowedGroupCiphers */
+ if (getGroupCipher()) {
+ config.allowedGroupCiphers =
+ supplicantToWifiConfigurationGroupCipherMask(mGroupCipherMask);
+ }
+ /** allowedPairwiseCiphers */
+ if (getPairwiseCipher()) {
+ config.allowedPairwiseCiphers =
+ supplicantToWifiConfigurationPairwiseCipherMask(mPairwiseCipherMask);
+ }
+ /** metadata: idstr */
+ if (getIdStr() && !TextUtils.isEmpty(mIdStr)) {
+ Map<String, String> metadata = parseNetworkExtra(mIdStr);
+ networkExtras.putAll(metadata);
+ } else {
+ Log.w(TAG, "getIdStr failed or empty");
+ }
+ return loadWifiEnterpriseConfig(config.SSID, config.enterpriseConfig);
}
- /** PSK pass phrase */
- config.preSharedKey = null;
- if (getPskPassphrase() && !TextUtils.isEmpty(mPskPassphrase)) {
- config.preSharedKey = NativeUtil.addEnclosingQuotes(mPskPassphrase);
- } else if (getPsk() && !ArrayUtils.isEmpty(mPsk)) {
- config.preSharedKey = NativeUtil.hexStringFromByteArray(mPsk);
- }
- /** allowedKeyManagement */
- if (getKeyMgmt()) {
- BitSet keyMgmtMask = supplicantToWifiConfigurationKeyMgmtMask(mKeyMgmtMask);
- config.allowedKeyManagement = removeFastTransitionFlags(keyMgmtMask);
- }
- /** allowedProtocols */
- if (getProto()) {
- config.allowedProtocols =
- supplicantToWifiConfigurationProtoMask(mProtoMask);
- }
- /** allowedAuthAlgorithms */
- if (getAuthAlg()) {
- config.allowedAuthAlgorithms =
- supplicantToWifiConfigurationAuthAlgMask(mAuthAlgMask);
- }
- /** allowedGroupCiphers */
- if (getGroupCipher()) {
- config.allowedGroupCiphers =
- supplicantToWifiConfigurationGroupCipherMask(mGroupCipherMask);
- }
- /** allowedPairwiseCiphers */
- if (getPairwiseCipher()) {
- config.allowedPairwiseCiphers =
- supplicantToWifiConfigurationPairwiseCipherMask(mPairwiseCipherMask);
- }
- /** metadata: idstr */
- if (getIdStr() && !TextUtils.isEmpty(mIdStr)) {
- Map<String, String> metadata = parseNetworkExtra(mIdStr);
- networkExtras.putAll(metadata);
- } else {
- Log.w(TAG, "getIdStr failed or empty");
- }
- return loadWifiEnterpriseConfig(config.SSID, config.enterpriseConfig);
}
/**
@@ -244,138 +253,141 @@
* @throws IllegalArgumentException on malformed configuration params.
*/
public boolean saveWifiConfiguration(WifiConfiguration config) {
- if (config == null) return false;
- /** SSID */
- if (config.SSID != null) {
- if (!setSsid(NativeUtil.decodeSsid(config.SSID))) {
- Log.e(TAG, "failed to set SSID: " + config.SSID);
- return false;
- }
- }
- /** BSSID */
- String bssidStr = config.getNetworkSelectionStatus().getNetworkSelectionBSSID();
- if (bssidStr != null) {
- byte[] bssid = NativeUtil.macAddressToByteArray(bssidStr);
- if (!setBssid(bssid)) {
- Log.e(TAG, "failed to set BSSID: " + bssidStr);
- return false;
- }
- }
- /** Pre Shared Key. This can either be quoted ASCII passphrase or hex string for raw psk */
- if (config.preSharedKey != null) {
- if (config.preSharedKey.startsWith("\"")) {
- if (!setPskPassphrase(NativeUtil.removeEnclosingQuotes(config.preSharedKey))) {
- Log.e(TAG, "failed to set psk passphrase");
- return false;
- }
- } else {
- if (!setPsk(NativeUtil.hexStringToByteArray(config.preSharedKey))) {
- Log.e(TAG, "failed to set psk");
+ synchronized (mLock) {
+ if (config == null) return false;
+ /** SSID */
+ if (config.SSID != null) {
+ if (!setSsid(NativeUtil.decodeSsid(config.SSID))) {
+ Log.e(TAG, "failed to set SSID: " + config.SSID);
return false;
}
}
- }
-
- /** Wep Keys */
- boolean hasSetKey = false;
- if (config.wepKeys != null) {
- for (int i = 0; i < config.wepKeys.length; i++) {
- if (config.wepKeys[i] != null) {
- if (!setWepKey(
- i, NativeUtil.hexOrQuotedAsciiStringToBytes(config.wepKeys[i]))) {
- Log.e(TAG, "failed to set wep_key " + i);
+ /** BSSID */
+ String bssidStr = config.getNetworkSelectionStatus().getNetworkSelectionBSSID();
+ if (bssidStr != null) {
+ byte[] bssid = NativeUtil.macAddressToByteArray(bssidStr);
+ if (!setBssid(bssid)) {
+ Log.e(TAG, "failed to set BSSID: " + bssidStr);
+ return false;
+ }
+ }
+ /** Pre Shared Key */
+ // This can either be quoted ASCII passphrase or hex string for raw psk.
+ if (config.preSharedKey != null) {
+ if (config.preSharedKey.startsWith("\"")) {
+ if (!setPskPassphrase(NativeUtil.removeEnclosingQuotes(config.preSharedKey))) {
+ Log.e(TAG, "failed to set psk passphrase");
return false;
}
- hasSetKey = true;
+ } else {
+ if (!setPsk(NativeUtil.hexStringToByteArray(config.preSharedKey))) {
+ Log.e(TAG, "failed to set psk");
+ return false;
+ }
}
}
- }
- /** Wep Tx Key Idx */
- if (hasSetKey) {
- if (!setWepTxKeyIdx(config.wepTxKeyIndex)) {
- Log.e(TAG, "failed to set wep_tx_keyidx: " + config.wepTxKeyIndex);
- return false;
- }
- }
- /** HiddenSSID */
- if (!setScanSsid(config.hiddenSSID)) {
- Log.e(TAG, config.SSID + ": failed to set hiddenSSID: " + config.hiddenSSID);
- return false;
- }
- /** RequirePMF */
- if (!setRequirePmf(config.requirePMF)) {
- Log.e(TAG, config.SSID + ": failed to set requirePMF: " + config.requirePMF);
- return false;
- }
- /** Key Management Scheme */
- if (config.allowedKeyManagement.cardinality() != 0) {
- // Add FT flags if supported.
- BitSet keyMgmtMask = addFastTransitionFlags(config.allowedKeyManagement);
- if (!setKeyMgmt(wifiConfigurationToSupplicantKeyMgmtMask(keyMgmtMask))) {
- Log.e(TAG, "failed to set Key Management");
- return false;
- }
- }
- /** Security Protocol */
- if (config.allowedProtocols.cardinality() != 0
- && !setProto(wifiConfigurationToSupplicantProtoMask(config.allowedProtocols))) {
- Log.e(TAG, "failed to set Security Protocol");
- return false;
- }
- /** Auth Algorithm */
- if (config.allowedAuthAlgorithms.cardinality() != 0
- && !setAuthAlg(wifiConfigurationToSupplicantAuthAlgMask(
- config.allowedAuthAlgorithms))) {
- Log.e(TAG, "failed to set AuthAlgorithm");
- return false;
- }
- /** Group Cipher */
- if (config.allowedGroupCiphers.cardinality() != 0
- && !setGroupCipher(wifiConfigurationToSupplicantGroupCipherMask(
- config.allowedGroupCiphers))) {
- Log.e(TAG, "failed to set Group Cipher");
- return false;
- }
- /** Pairwise Cipher*/
- if (config.allowedPairwiseCiphers.cardinality() != 0
- && !setPairwiseCipher(wifiConfigurationToSupplicantPairwiseCipherMask(
- config.allowedPairwiseCiphers))) {
- Log.e(TAG, "failed to set PairwiseCipher");
- return false;
- }
- /** metadata: FQDN + ConfigKey + CreatorUid */
- final Map<String, String> metadata = new HashMap<String, String>();
- if (config.isPasspoint()) {
- metadata.put(ID_STRING_KEY_FQDN, config.FQDN);
- }
- metadata.put(ID_STRING_KEY_CONFIG_KEY, config.configKey());
- metadata.put(ID_STRING_KEY_CREATOR_UID, Integer.toString(config.creatorUid));
- if (!setIdStr(createNetworkExtra(metadata))) {
- Log.e(TAG, "failed to set id string");
- return false;
- }
- /** UpdateIdentifier */
- if (config.updateIdentifier != null
- && !setUpdateIdentifier(Integer.parseInt(config.updateIdentifier))) {
- Log.e(TAG, "failed to set update identifier");
- return false;
- }
- // Finish here if no EAP config to set
- if (config.enterpriseConfig != null
- && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
- if (!saveWifiEnterpriseConfig(config.SSID, config.enterpriseConfig)) {
- return false;
- }
- }
- // Now that the network is configured fully, start listening for callback events.
- mISupplicantStaNetworkCallback =
- new SupplicantStaNetworkHalCallback(config.networkId, config.SSID);
- if (!registerCallback(mISupplicantStaNetworkCallback)) {
- Log.e(TAG, "Failed to register callback");
- return false;
+ /** Wep Keys */
+ boolean hasSetKey = false;
+ if (config.wepKeys != null) {
+ for (int i = 0; i < config.wepKeys.length; i++) {
+ if (config.wepKeys[i] != null) {
+ if (!setWepKey(
+ i, NativeUtil.hexOrQuotedAsciiStringToBytes(config.wepKeys[i]))) {
+ Log.e(TAG, "failed to set wep_key " + i);
+ return false;
+ }
+ hasSetKey = true;
+ }
+ }
+ }
+ /** Wep Tx Key Idx */
+ if (hasSetKey) {
+ if (!setWepTxKeyIdx(config.wepTxKeyIndex)) {
+ Log.e(TAG, "failed to set wep_tx_keyidx: " + config.wepTxKeyIndex);
+ return false;
+ }
+ }
+ /** HiddenSSID */
+ if (!setScanSsid(config.hiddenSSID)) {
+ Log.e(TAG, config.SSID + ": failed to set hiddenSSID: " + config.hiddenSSID);
+ return false;
+ }
+ /** RequirePMF */
+ if (!setRequirePmf(config.requirePMF)) {
+ Log.e(TAG, config.SSID + ": failed to set requirePMF: " + config.requirePMF);
+ return false;
+ }
+ /** Key Management Scheme */
+ if (config.allowedKeyManagement.cardinality() != 0) {
+ // Add FT flags if supported.
+ BitSet keyMgmtMask = addFastTransitionFlags(config.allowedKeyManagement);
+ if (!setKeyMgmt(wifiConfigurationToSupplicantKeyMgmtMask(keyMgmtMask))) {
+ Log.e(TAG, "failed to set Key Management");
+ return false;
+ }
+ }
+ /** Security Protocol */
+ if (config.allowedProtocols.cardinality() != 0
+ && !setProto(wifiConfigurationToSupplicantProtoMask(config.allowedProtocols))) {
+ Log.e(TAG, "failed to set Security Protocol");
+ return false;
+ }
+ /** Auth Algorithm */
+ if (config.allowedAuthAlgorithms.cardinality() != 0
+ && !setAuthAlg(wifiConfigurationToSupplicantAuthAlgMask(
+ config.allowedAuthAlgorithms))) {
+ Log.e(TAG, "failed to set AuthAlgorithm");
+ return false;
+ }
+ /** Group Cipher */
+ if (config.allowedGroupCiphers.cardinality() != 0
+ && !setGroupCipher(wifiConfigurationToSupplicantGroupCipherMask(
+ config.allowedGroupCiphers))) {
+ Log.e(TAG, "failed to set Group Cipher");
+ return false;
+ }
+ /** Pairwise Cipher*/
+ if (config.allowedPairwiseCiphers.cardinality() != 0
+ && !setPairwiseCipher(wifiConfigurationToSupplicantPairwiseCipherMask(
+ config.allowedPairwiseCiphers))) {
+ Log.e(TAG, "failed to set PairwiseCipher");
+ return false;
+ }
+ /** metadata: FQDN + ConfigKey + CreatorUid */
+ final Map<String, String> metadata = new HashMap<String, String>();
+ if (config.isPasspoint()) {
+ metadata.put(ID_STRING_KEY_FQDN, config.FQDN);
+ }
+ metadata.put(ID_STRING_KEY_CONFIG_KEY, config.configKey());
+ metadata.put(ID_STRING_KEY_CREATOR_UID, Integer.toString(config.creatorUid));
+ if (!setIdStr(createNetworkExtra(metadata))) {
+ Log.e(TAG, "failed to set id string");
+ return false;
+ }
+ /** UpdateIdentifier */
+ if (config.updateIdentifier != null
+ && !setUpdateIdentifier(Integer.parseInt(config.updateIdentifier))) {
+ Log.e(TAG, "failed to set update identifier");
+ return false;
+ }
+ // Finish here if no EAP config to set
+ if (config.enterpriseConfig != null
+ && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
+ if (!saveWifiEnterpriseConfig(config.SSID, config.enterpriseConfig)) {
+ return false;
+ }
+ }
+
+ // Now that the network is configured fully, start listening for callback events.
+ mISupplicantStaNetworkCallback =
+ new SupplicantStaNetworkHalCallback(config.networkId, config.SSID);
+ if (!registerCallback(mISupplicantStaNetworkCallback)) {
+ Log.e(TAG, "Failed to register callback");
+ return false;
+ }
+ return true;
}
- return true;
}
/**
@@ -386,84 +398,87 @@
* @return true if succeeds, false otherwise.
*/
private boolean loadWifiEnterpriseConfig(String ssid, WifiEnterpriseConfig eapConfig) {
- if (eapConfig == null) return false;
- /** EAP method */
- if (getEapMethod()) {
- eapConfig.setEapMethod(supplicantToWifiConfigurationEapMethod(mEapMethod));
- } else {
- // Invalid eap method could be because it's not an enterprise config.
- Log.e(TAG, "failed to get eap method. Assumimg not an enterprise network");
+ synchronized (mLock) {
+ if (eapConfig == null) return false;
+ /** EAP method */
+ if (getEapMethod()) {
+ eapConfig.setEapMethod(supplicantToWifiConfigurationEapMethod(mEapMethod));
+ } else {
+ // Invalid eap method could be because it's not an enterprise config.
+ Log.e(TAG, "failed to get eap method. Assumimg not an enterprise network");
+ return true;
+ }
+ /** EAP Phase 2 method */
+ if (getEapPhase2Method()) {
+ eapConfig.setPhase2Method(
+ supplicantToWifiConfigurationEapPhase2Method(mEapPhase2Method));
+ } else {
+ // We cannot have an invalid eap phase 2 method. Return failure.
+ Log.e(TAG, "failed to get eap phase2 method");
+ return false;
+ }
+ /** EAP Identity */
+ if (getEapIdentity() && !ArrayUtils.isEmpty(mEapIdentity)) {
+ eapConfig.setFieldValue(
+ WifiEnterpriseConfig.IDENTITY_KEY,
+ NativeUtil.stringFromByteArrayList(mEapIdentity));
+ }
+ /** EAP Anonymous Identity */
+ if (getEapAnonymousIdentity() && !ArrayUtils.isEmpty(mEapAnonymousIdentity)) {
+ eapConfig.setFieldValue(
+ WifiEnterpriseConfig.ANON_IDENTITY_KEY,
+ NativeUtil.stringFromByteArrayList(mEapAnonymousIdentity));
+ }
+ /** EAP Password */
+ if (getEapPassword() && !ArrayUtils.isEmpty(mEapPassword)) {
+ eapConfig.setFieldValue(
+ WifiEnterpriseConfig.PASSWORD_KEY,
+ NativeUtil.stringFromByteArrayList(mEapPassword));
+ }
+ /** EAP Client Cert */
+ if (getEapClientCert() && !TextUtils.isEmpty(mEapClientCert)) {
+ eapConfig.setFieldValue(WifiEnterpriseConfig.CLIENT_CERT_KEY, mEapClientCert);
+ }
+ /** EAP CA Cert */
+ if (getEapCACert() && !TextUtils.isEmpty(mEapCACert)) {
+ eapConfig.setFieldValue(WifiEnterpriseConfig.CA_CERT_KEY, mEapCACert);
+ }
+ /** EAP Subject Match */
+ if (getEapSubjectMatch() && !TextUtils.isEmpty(mEapSubjectMatch)) {
+ eapConfig.setFieldValue(WifiEnterpriseConfig.SUBJECT_MATCH_KEY, mEapSubjectMatch);
+ }
+ /** EAP Engine ID */
+ if (getEapEngineID() && !TextUtils.isEmpty(mEapEngineID)) {
+ eapConfig.setFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, mEapEngineID);
+ }
+ /** EAP Engine. Set this only if the engine id is non null. */
+ if (getEapEngine() && !TextUtils.isEmpty(mEapEngineID)) {
+ eapConfig.setFieldValue(
+ WifiEnterpriseConfig.ENGINE_KEY,
+ mEapEngine
+ ? WifiEnterpriseConfig.ENGINE_ENABLE
+ : WifiEnterpriseConfig.ENGINE_DISABLE);
+ }
+ /** EAP Private Key */
+ if (getEapPrivateKeyId() && !TextUtils.isEmpty(mEapPrivateKeyId)) {
+ eapConfig.setFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, mEapPrivateKeyId);
+ }
+ /** EAP Alt Subject Match */
+ if (getEapAltSubjectMatch() && !TextUtils.isEmpty(mEapAltSubjectMatch)) {
+ eapConfig.setFieldValue(
+ WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY, mEapAltSubjectMatch);
+ }
+ /** EAP Domain Suffix Match */
+ if (getEapDomainSuffixMatch() && !TextUtils.isEmpty(mEapDomainSuffixMatch)) {
+ eapConfig.setFieldValue(
+ WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY, mEapDomainSuffixMatch);
+ }
+ /** EAP CA Path*/
+ if (getEapCAPath() && !TextUtils.isEmpty(mEapCAPath)) {
+ eapConfig.setFieldValue(WifiEnterpriseConfig.CA_PATH_KEY, mEapCAPath);
+ }
return true;
}
- /** EAP Phase 2 method */
- if (getEapPhase2Method()) {
- eapConfig.setPhase2Method(
- supplicantToWifiConfigurationEapPhase2Method(mEapPhase2Method));
- } else {
- // We cannot have an invalid eap phase 2 method. Return failure.
- Log.e(TAG, "failed to get eap phase2 method");
- return false;
- }
- /** EAP Identity */
- if (getEapIdentity() && !ArrayUtils.isEmpty(mEapIdentity)) {
- eapConfig.setFieldValue(
- WifiEnterpriseConfig.IDENTITY_KEY,
- NativeUtil.stringFromByteArrayList(mEapIdentity));
- }
- /** EAP Anonymous Identity */
- if (getEapAnonymousIdentity() && !ArrayUtils.isEmpty(mEapAnonymousIdentity)) {
- eapConfig.setFieldValue(
- WifiEnterpriseConfig.ANON_IDENTITY_KEY,
- NativeUtil.stringFromByteArrayList(mEapAnonymousIdentity));
- }
- /** EAP Password */
- if (getEapPassword() && !ArrayUtils.isEmpty(mEapPassword)) {
- eapConfig.setFieldValue(
- WifiEnterpriseConfig.PASSWORD_KEY,
- NativeUtil.stringFromByteArrayList(mEapPassword));
- }
- /** EAP Client Cert */
- if (getEapClientCert() && !TextUtils.isEmpty(mEapClientCert)) {
- eapConfig.setFieldValue(WifiEnterpriseConfig.CLIENT_CERT_KEY, mEapClientCert);
- }
- /** EAP CA Cert */
- if (getEapCACert() && !TextUtils.isEmpty(mEapCACert)) {
- eapConfig.setFieldValue(WifiEnterpriseConfig.CA_CERT_KEY, mEapCACert);
- }
- /** EAP Subject Match */
- if (getEapSubjectMatch() && !TextUtils.isEmpty(mEapSubjectMatch)) {
- eapConfig.setFieldValue(WifiEnterpriseConfig.SUBJECT_MATCH_KEY, mEapSubjectMatch);
- }
- /** EAP Engine ID */
- if (getEapEngineID() && !TextUtils.isEmpty(mEapEngineID)) {
- eapConfig.setFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, mEapEngineID);
- }
- /** EAP Engine. Set this only if the engine id is non null. */
- if (getEapEngine() && !TextUtils.isEmpty(mEapEngineID)) {
- eapConfig.setFieldValue(
- WifiEnterpriseConfig.ENGINE_KEY,
- mEapEngine
- ? WifiEnterpriseConfig.ENGINE_ENABLE
- : WifiEnterpriseConfig.ENGINE_DISABLE);
- }
- /** EAP Private Key */
- if (getEapPrivateKeyId() && !TextUtils.isEmpty(mEapPrivateKeyId)) {
- eapConfig.setFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, mEapPrivateKeyId);
- }
- /** EAP Alt Subject Match */
- if (getEapAltSubjectMatch() && !TextUtils.isEmpty(mEapAltSubjectMatch)) {
- eapConfig.setFieldValue(WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY, mEapAltSubjectMatch);
- }
- /** EAP Domain Suffix Match */
- if (getEapDomainSuffixMatch() && !TextUtils.isEmpty(mEapDomainSuffixMatch)) {
- eapConfig.setFieldValue(
- WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY, mEapDomainSuffixMatch);
- }
- /** EAP CA Path*/
- if (getEapCAPath() && !TextUtils.isEmpty(mEapCAPath)) {
- eapConfig.setFieldValue(WifiEnterpriseConfig.CA_PATH_KEY, mEapCAPath);
- }
- return true;
}
/**
@@ -474,104 +489,107 @@
* @return true if succeeds, false otherwise.
*/
private boolean saveWifiEnterpriseConfig(String ssid, WifiEnterpriseConfig eapConfig) {
- if (eapConfig == null) return false;
- /** EAP method */
- if (!setEapMethod(wifiConfigurationToSupplicantEapMethod(eapConfig.getEapMethod()))) {
- Log.e(TAG, ssid + ": failed to set eap method: " + eapConfig.getEapMethod());
- return false;
- }
- /** EAP Phase 2 method */
- if (!setEapPhase2Method(wifiConfigurationToSupplicantEapPhase2Method(
- eapConfig.getPhase2Method()))) {
- Log.e(TAG, ssid + ": failed to set eap phase 2 method: " + eapConfig.getPhase2Method());
- return false;
- }
- String eapParam = null;
- /** EAP Identity */
- eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.IDENTITY_KEY);
- if (!TextUtils.isEmpty(eapParam)
- && !setEapIdentity(NativeUtil.stringToByteArrayList(eapParam))) {
- Log.e(TAG, ssid + ": failed to set eap identity: " + eapParam);
- return false;
- }
- /** EAP Anonymous Identity */
- eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.ANON_IDENTITY_KEY);
- if (!TextUtils.isEmpty(eapParam)
- && !setEapAnonymousIdentity(NativeUtil.stringToByteArrayList(eapParam))) {
- Log.e(TAG, ssid + ": failed to set eap anonymous identity: " + eapParam);
- return false;
- }
- /** EAP Password */
- eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.PASSWORD_KEY);
- if (!TextUtils.isEmpty(eapParam)
- && !setEapPassword(NativeUtil.stringToByteArrayList(eapParam))) {
- Log.e(TAG, ssid + ": failed to set eap password");
- return false;
- }
- /** EAP Client Cert */
- eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.CLIENT_CERT_KEY);
- if (!TextUtils.isEmpty(eapParam) && !setEapClientCert(eapParam)) {
- Log.e(TAG, ssid + ": failed to set eap client cert: " + eapParam);
- return false;
- }
- /** EAP CA Cert */
- eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.CA_CERT_KEY);
- if (!TextUtils.isEmpty(eapParam) && !setEapCACert(eapParam)) {
- Log.e(TAG, ssid + ": failed to set eap ca cert: " + eapParam);
- return false;
- }
- /** EAP Subject Match */
- eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.SUBJECT_MATCH_KEY);
- if (!TextUtils.isEmpty(eapParam) && !setEapSubjectMatch(eapParam)) {
- Log.e(TAG, ssid + ": failed to set eap subject match: " + eapParam);
- return false;
- }
- /** EAP Engine ID */
- eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY);
- if (!TextUtils.isEmpty(eapParam) && !setEapEngineID(eapParam)) {
- Log.e(TAG, ssid + ": failed to set eap engine id: " + eapParam);
- return false;
- }
- /** EAP Engine */
- eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY);
- if (!TextUtils.isEmpty(eapParam) && !setEapEngine(
- eapParam.equals(WifiEnterpriseConfig.ENGINE_ENABLE) ? true : false)) {
- Log.e(TAG, ssid + ": failed to set eap engine: " + eapParam);
- return false;
- }
- /** EAP Private Key */
- eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY);
- if (!TextUtils.isEmpty(eapParam) && !setEapPrivateKeyId(eapParam)) {
- Log.e(TAG, ssid + ": failed to set eap private key: " + eapParam);
- return false;
- }
- /** EAP Alt Subject Match */
- eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY);
- if (!TextUtils.isEmpty(eapParam) && !setEapAltSubjectMatch(eapParam)) {
- Log.e(TAG, ssid + ": failed to set eap alt subject match: " + eapParam);
- return false;
- }
- /** EAP Domain Suffix Match */
- eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY);
- if (!TextUtils.isEmpty(eapParam) && !setEapDomainSuffixMatch(eapParam)) {
- Log.e(TAG, ssid + ": failed to set eap domain suffix match: " + eapParam);
- return false;
- }
- /** EAP CA Path*/
- eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.CA_PATH_KEY);
- if (!TextUtils.isEmpty(eapParam) && !setEapCAPath(eapParam)) {
- Log.e(TAG, ssid + ": failed to set eap ca path: " + eapParam);
- return false;
- }
+ synchronized (mLock) {
+ if (eapConfig == null) return false;
+ /** EAP method */
+ if (!setEapMethod(wifiConfigurationToSupplicantEapMethod(eapConfig.getEapMethod()))) {
+ Log.e(TAG, ssid + ": failed to set eap method: " + eapConfig.getEapMethod());
+ return false;
+ }
+ /** EAP Phase 2 method */
+ if (!setEapPhase2Method(wifiConfigurationToSupplicantEapPhase2Method(
+ eapConfig.getPhase2Method()))) {
+ Log.e(TAG, ssid + ": failed to set eap phase 2 method: "
+ + eapConfig.getPhase2Method());
+ return false;
+ }
+ String eapParam = null;
+ /** EAP Identity */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.IDENTITY_KEY);
+ if (!TextUtils.isEmpty(eapParam)
+ && !setEapIdentity(NativeUtil.stringToByteArrayList(eapParam))) {
+ Log.e(TAG, ssid + ": failed to set eap identity: " + eapParam);
+ return false;
+ }
+ /** EAP Anonymous Identity */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.ANON_IDENTITY_KEY);
+ if (!TextUtils.isEmpty(eapParam)
+ && !setEapAnonymousIdentity(NativeUtil.stringToByteArrayList(eapParam))) {
+ Log.e(TAG, ssid + ": failed to set eap anonymous identity: " + eapParam);
+ return false;
+ }
+ /** EAP Password */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.PASSWORD_KEY);
+ if (!TextUtils.isEmpty(eapParam)
+ && !setEapPassword(NativeUtil.stringToByteArrayList(eapParam))) {
+ Log.e(TAG, ssid + ": failed to set eap password");
+ return false;
+ }
+ /** EAP Client Cert */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.CLIENT_CERT_KEY);
+ if (!TextUtils.isEmpty(eapParam) && !setEapClientCert(eapParam)) {
+ Log.e(TAG, ssid + ": failed to set eap client cert: " + eapParam);
+ return false;
+ }
+ /** EAP CA Cert */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.CA_CERT_KEY);
+ if (!TextUtils.isEmpty(eapParam) && !setEapCACert(eapParam)) {
+ Log.e(TAG, ssid + ": failed to set eap ca cert: " + eapParam);
+ return false;
+ }
+ /** EAP Subject Match */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.SUBJECT_MATCH_KEY);
+ if (!TextUtils.isEmpty(eapParam) && !setEapSubjectMatch(eapParam)) {
+ Log.e(TAG, ssid + ": failed to set eap subject match: " + eapParam);
+ return false;
+ }
+ /** EAP Engine ID */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY);
+ if (!TextUtils.isEmpty(eapParam) && !setEapEngineID(eapParam)) {
+ Log.e(TAG, ssid + ": failed to set eap engine id: " + eapParam);
+ return false;
+ }
+ /** EAP Engine */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY);
+ if (!TextUtils.isEmpty(eapParam) && !setEapEngine(
+ eapParam.equals(WifiEnterpriseConfig.ENGINE_ENABLE) ? true : false)) {
+ Log.e(TAG, ssid + ": failed to set eap engine: " + eapParam);
+ return false;
+ }
+ /** EAP Private Key */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY);
+ if (!TextUtils.isEmpty(eapParam) && !setEapPrivateKeyId(eapParam)) {
+ Log.e(TAG, ssid + ": failed to set eap private key: " + eapParam);
+ return false;
+ }
+ /** EAP Alt Subject Match */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY);
+ if (!TextUtils.isEmpty(eapParam) && !setEapAltSubjectMatch(eapParam)) {
+ Log.e(TAG, ssid + ": failed to set eap alt subject match: " + eapParam);
+ return false;
+ }
+ /** EAP Domain Suffix Match */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY);
+ if (!TextUtils.isEmpty(eapParam) && !setEapDomainSuffixMatch(eapParam)) {
+ Log.e(TAG, ssid + ": failed to set eap domain suffix match: " + eapParam);
+ return false;
+ }
+ /** EAP CA Path*/
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.CA_PATH_KEY);
+ if (!TextUtils.isEmpty(eapParam) && !setEapCAPath(eapParam)) {
+ Log.e(TAG, ssid + ": failed to set eap ca path: " + eapParam);
+ return false;
+ }
- /** EAP Proactive Key Caching */
- eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.OPP_KEY_CACHING);
- if (!TextUtils.isEmpty(eapParam)
- && !setEapProactiveKeyCaching(eapParam.equals("1") ? true : false)) {
- Log.e(TAG, ssid + ": failed to set proactive key caching: " + eapParam);
- return false;
+ /** EAP Proactive Key Caching */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.OPP_KEY_CACHING);
+ if (!TextUtils.isEmpty(eapParam)
+ && !setEapProactiveKeyCaching(eapParam.equals("1") ? true : false)) {
+ Log.e(TAG, ssid + ": failed to set proactive key caching: " + eapParam);
+ return false;
+ }
+ return true;
}
- return true;
}
/**
@@ -981,11 +999,13 @@
* @return true if it succeeds, false otherwise.
*/
public boolean setBssid(String bssidStr) {
- try {
- return setBssid(NativeUtil.macAddressToByteArray(bssidStr));
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Illegal argument " + bssidStr, e);
- return false;
+ synchronized (mLock) {
+ try {
+ return setBssid(NativeUtil.macAddressToByteArray(bssidStr));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + bssidStr, e);
+ return false;
+ }
}
}
@@ -1793,10 +1813,12 @@
* @return anonymous identity string if succeeds, null otherwise.
*/
public String fetchEapAnonymousIdentity() {
- if (!getEapAnonymousIdentity()) {
- return null;
+ synchronized (mLock) {
+ if (!getEapAnonymousIdentity()) {
+ return null;
+ }
+ return NativeUtil.stringFromByteArrayList(mEapAnonymousIdentity);
}
- return NativeUtil.stringFromByteArrayList(mEapAnonymousIdentity);
}
/** See ISupplicantStaNetwork.hal for documentation */
@@ -2103,40 +2125,42 @@
* @return true if succeeds, false otherwise.
*/
public boolean sendNetworkEapSimGsmAuthResponse(String paramsStr) {
- try {
- Matcher match = GSM_AUTH_RESPONSE_PARAMS_PATTERN.matcher(paramsStr);
- ArrayList<ISupplicantStaNetwork.NetworkResponseEapSimGsmAuthParams> params =
- new ArrayList<>();
- while (match.find()) {
- if (match.groupCount() != 2) {
+ synchronized (mLock) {
+ try {
+ Matcher match = GSM_AUTH_RESPONSE_PARAMS_PATTERN.matcher(paramsStr);
+ ArrayList<ISupplicantStaNetwork.NetworkResponseEapSimGsmAuthParams> params =
+ new ArrayList<>();
+ while (match.find()) {
+ if (match.groupCount() != 2) {
+ Log.e(TAG, "Malformed gsm auth response params: " + paramsStr);
+ return false;
+ }
+ ISupplicantStaNetwork.NetworkResponseEapSimGsmAuthParams param =
+ new ISupplicantStaNetwork.NetworkResponseEapSimGsmAuthParams();
+ byte[] kc = NativeUtil.hexStringToByteArray(match.group(1));
+ if (kc == null || kc.length != param.kc.length) {
+ Log.e(TAG, "Invalid kc value: " + match.group(1));
+ return false;
+ }
+ byte[] sres = NativeUtil.hexStringToByteArray(match.group(2));
+ if (sres == null || sres.length != param.sres.length) {
+ Log.e(TAG, "Invalid sres value: " + match.group(2));
+ return false;
+ }
+ System.arraycopy(kc, 0, param.kc, 0, param.kc.length);
+ System.arraycopy(sres, 0, param.sres, 0, param.sres.length);
+ params.add(param);
+ }
+ // The number of kc/sres pairs can either be 2 or 3 depending on the request.
+ if (params.size() > 3 || params.size() < 2) {
Log.e(TAG, "Malformed gsm auth response params: " + paramsStr);
return false;
}
- ISupplicantStaNetwork.NetworkResponseEapSimGsmAuthParams param =
- new ISupplicantStaNetwork.NetworkResponseEapSimGsmAuthParams();
- byte[] kc = NativeUtil.hexStringToByteArray(match.group(1));
- if (kc == null || kc.length != param.kc.length) {
- Log.e(TAG, "Invalid kc value: " + match.group(1));
- return false;
- }
- byte[] sres = NativeUtil.hexStringToByteArray(match.group(2));
- if (sres == null || sres.length != param.sres.length) {
- Log.e(TAG, "Invalid sres value: " + match.group(2));
- return false;
- }
- System.arraycopy(kc, 0, param.kc, 0, param.kc.length);
- System.arraycopy(sres, 0, param.sres, 0, param.sres.length);
- params.add(param);
- }
- // The number of kc/sres pairs can either be 2 or 3 depending on the request.
- if (params.size() > 3 || params.size() < 2) {
- Log.e(TAG, "Malformed gsm auth response params: " + paramsStr);
+ return sendNetworkEapSimGsmAuthResponse(params);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + paramsStr, e);
return false;
}
- return sendNetworkEapSimGsmAuthResponse(params);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Illegal argument " + paramsStr, e);
- return false;
}
}
@@ -2177,38 +2201,40 @@
* @return true if succeeds, false otherwise.
*/
public boolean sendNetworkEapSimUmtsAuthResponse(String paramsStr) {
- try {
- Matcher match = UMTS_AUTH_RESPONSE_PARAMS_PATTERN.matcher(paramsStr);
- if (!match.find() || match.groupCount() != 3) {
- Log.e(TAG, "Malformed umts auth response params: " + paramsStr);
+ synchronized (mLock) {
+ try {
+ Matcher match = UMTS_AUTH_RESPONSE_PARAMS_PATTERN.matcher(paramsStr);
+ if (!match.find() || match.groupCount() != 3) {
+ Log.e(TAG, "Malformed umts auth response params: " + paramsStr);
+ return false;
+ }
+ ISupplicantStaNetwork.NetworkResponseEapSimUmtsAuthParams params =
+ new ISupplicantStaNetwork.NetworkResponseEapSimUmtsAuthParams();
+ byte[] ik = NativeUtil.hexStringToByteArray(match.group(1));
+ if (ik == null || ik.length != params.ik.length) {
+ Log.e(TAG, "Invalid ik value: " + match.group(1));
+ return false;
+ }
+ byte[] ck = NativeUtil.hexStringToByteArray(match.group(2));
+ if (ck == null || ck.length != params.ck.length) {
+ Log.e(TAG, "Invalid ck value: " + match.group(2));
+ return false;
+ }
+ byte[] res = NativeUtil.hexStringToByteArray(match.group(3));
+ if (res == null || res.length == 0) {
+ Log.e(TAG, "Invalid res value: " + match.group(3));
+ return false;
+ }
+ System.arraycopy(ik, 0, params.ik, 0, params.ik.length);
+ System.arraycopy(ck, 0, params.ck, 0, params.ck.length);
+ for (byte b : res) {
+ params.res.add(b);
+ }
+ return sendNetworkEapSimUmtsAuthResponse(params);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + paramsStr, e);
return false;
}
- ISupplicantStaNetwork.NetworkResponseEapSimUmtsAuthParams params =
- new ISupplicantStaNetwork.NetworkResponseEapSimUmtsAuthParams();
- byte[] ik = NativeUtil.hexStringToByteArray(match.group(1));
- if (ik == null || ik.length != params.ik.length) {
- Log.e(TAG, "Invalid ik value: " + match.group(1));
- return false;
- }
- byte[] ck = NativeUtil.hexStringToByteArray(match.group(2));
- if (ck == null || ck.length != params.ck.length) {
- Log.e(TAG, "Invalid ck value: " + match.group(2));
- return false;
- }
- byte[] res = NativeUtil.hexStringToByteArray(match.group(3));
- if (res == null || res.length == 0) {
- Log.e(TAG, "Invalid res value: " + match.group(3));
- return false;
- }
- System.arraycopy(ik, 0, params.ik, 0, params.ik.length);
- System.arraycopy(ck, 0, params.ck, 0, params.ck.length);
- for (byte b : res) {
- params.res.add(b);
- }
- return sendNetworkEapSimUmtsAuthResponse(params);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Illegal argument " + paramsStr, e);
- return false;
}
}
@@ -2235,21 +2261,23 @@
* @return true if succeeds, false otherwise.
*/
public boolean sendNetworkEapSimUmtsAutsResponse(String paramsStr) {
- try {
- Matcher match = UMTS_AUTS_RESPONSE_PARAMS_PATTERN.matcher(paramsStr);
- if (!match.find() || match.groupCount() != 1) {
- Log.e(TAG, "Malformed umts auts response params: " + paramsStr);
+ synchronized (mLock) {
+ try {
+ Matcher match = UMTS_AUTS_RESPONSE_PARAMS_PATTERN.matcher(paramsStr);
+ if (!match.find() || match.groupCount() != 1) {
+ Log.e(TAG, "Malformed umts auts response params: " + paramsStr);
+ return false;
+ }
+ byte[] auts = NativeUtil.hexStringToByteArray(match.group(1));
+ if (auts == null || auts.length != 14) {
+ Log.e(TAG, "Invalid auts value: " + match.group(1));
+ return false;
+ }
+ return sendNetworkEapSimUmtsAutsResponse(auts);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + paramsStr, e);
return false;
}
- byte[] auts = NativeUtil.hexStringToByteArray(match.group(1));
- if (auts == null || auts.length != 14) {
- Log.e(TAG, "Invalid auts value: " + match.group(1));
- return false;
- }
- return sendNetworkEapSimUmtsAutsResponse(auts);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Illegal argument " + paramsStr, e);
- return false;
}
}
/** See ISupplicantStaNetwork.hal for documentation */
@@ -2288,12 +2316,14 @@
* @return true if succeeds, false otherwise.
*/
public boolean sendNetworkEapIdentityResponse(String identityStr) {
- try {
- ArrayList<Byte> identity = NativeUtil.stringToByteArrayList(identityStr);
- return sendNetworkEapIdentityResponse(identity);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Illegal argument " + identityStr, e);
- return false;
+ synchronized (mLock) {
+ try {
+ ArrayList<Byte> identity = NativeUtil.stringToByteArrayList(identityStr);
+ return sendNetworkEapIdentityResponse(identity);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + identityStr, e);
+ return false;
+ }
}
}
/** See ISupplicantStaNetwork.hal for documentation */
@@ -2318,11 +2348,13 @@
* @return Hex string corresponding to the NFC token or null for failure.
*/
public String getWpsNfcConfigurationToken() {
- ArrayList<Byte> token = getWpsNfcConfigurationTokenInternal();
- if (token == null) {
- return null;
+ synchronized (mLock) {
+ ArrayList<Byte> token = getWpsNfcConfigurationTokenInternal();
+ if (token == null) {
+ return null;
+ }
+ return NativeUtil.hexStringFromByteArray(NativeUtil.byteArrayFromArrayList(token));
}
- return NativeUtil.hexStringFromByteArray(NativeUtil.byteArrayFromArrayList(token));
}
/** See ISupplicantStaNetwork.hal for documentation */
@@ -2350,16 +2382,18 @@
* otherwise
*/
private boolean checkStatusAndLogFailure(SupplicantStatus status, final String methodStr) {
- if (status.code != SupplicantStatusCode.SUCCESS) {
- Log.e(TAG, "ISupplicantStaNetwork." + methodStr + " failed: "
- + SupplicantStaIfaceHal.supplicantStatusCodeToString(status.code) + ", "
- + status.debugMessage);
- return false;
- } else {
- if (mVerboseLoggingEnabled) {
- Log.d(TAG, "ISupplicantStaNetwork." + methodStr + " succeeded");
+ synchronized (mLock) {
+ if (status.code != SupplicantStatusCode.SUCCESS) {
+ Log.e(TAG, "ISupplicantStaNetwork." + methodStr + " failed: "
+ + SupplicantStaIfaceHal.supplicantStatusCodeToString(status.code) + ", "
+ + status.debugMessage);
+ return false;
+ } else {
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "ISupplicantStaNetwork." + methodStr + " succeeded");
+ }
+ return true;
}
- return true;
}
}
@@ -2367,8 +2401,10 @@
* Helper function to log callbacks.
*/
private void logCallback(final String methodStr) {
- if (mVerboseLoggingEnabled) {
- Log.d(TAG, "ISupplicantStaNetworkCallback." + methodStr + " received");
+ synchronized (mLock) {
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "ISupplicantStaNetworkCallback." + methodStr + " received");
+ }
}
}
@@ -2376,43 +2412,51 @@
* Returns false if ISupplicantStaNetwork is null, and logs failure of methodStr
*/
private boolean checkISupplicantStaNetworkAndLogFailure(final String methodStr) {
- if (mISupplicantStaNetwork == null) {
- Log.e(TAG, "Can't call " + methodStr + ", ISupplicantStaNetwork is null");
- return false;
+ synchronized (mLock) {
+ if (mISupplicantStaNetwork == null) {
+ Log.e(TAG, "Can't call " + methodStr + ", ISupplicantStaNetwork is null");
+ return false;
+ }
+ return true;
}
- return true;
}
private void handleRemoteException(RemoteException e, String methodStr) {
- mISupplicantStaNetwork = null;
- Log.e(TAG, "ISupplicantStaNetwork." + methodStr + " failed with exception", e);
+ synchronized (mLock) {
+ mISupplicantStaNetwork = null;
+ Log.e(TAG, "ISupplicantStaNetwork." + methodStr + " failed with exception", e);
+ }
}
/**
* Adds FT flags for networks if the device supports it.
*/
private BitSet addFastTransitionFlags(BitSet keyManagementFlags) {
- if (!mSystemSupportsFastBssTransition) {
- return keyManagementFlags;
+ synchronized (mLock) {
+ if (!mSystemSupportsFastBssTransition) {
+ return keyManagementFlags;
+ }
+ BitSet modifiedFlags = (BitSet) keyManagementFlags.clone();
+ if (keyManagementFlags.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
+ modifiedFlags.set(WifiConfiguration.KeyMgmt.FT_PSK);
+ }
+ if (keyManagementFlags.get(WifiConfiguration.KeyMgmt.WPA_EAP)) {
+ modifiedFlags.set(WifiConfiguration.KeyMgmt.FT_EAP);
+ }
+ return modifiedFlags;
}
- BitSet modifiedFlags = (BitSet) keyManagementFlags.clone();
- if (keyManagementFlags.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
- modifiedFlags.set(WifiConfiguration.KeyMgmt.FT_PSK);
- }
- if (keyManagementFlags.get(WifiConfiguration.KeyMgmt.WPA_EAP)) {
- modifiedFlags.set(WifiConfiguration.KeyMgmt.FT_EAP);
- }
- return modifiedFlags;
}
/**
* Removes FT flags for networks if the device supports it.
*/
private BitSet removeFastTransitionFlags(BitSet keyManagementFlags) {
- BitSet modifiedFlags = (BitSet) keyManagementFlags.clone();
- modifiedFlags.clear(WifiConfiguration.KeyMgmt.FT_PSK);
- modifiedFlags.clear(WifiConfiguration.KeyMgmt.FT_EAP);
- return modifiedFlags;
+ synchronized (mLock) {
+ BitSet modifiedFlags = (BitSet) keyManagementFlags.clone();
+ modifiedFlags.clear(WifiConfiguration.KeyMgmt.FT_PSK);
+ modifiedFlags.clear(WifiConfiguration.KeyMgmt.FT_EAP);
+ return modifiedFlags;
+ }
}
/**
@@ -2496,8 +2540,8 @@
@Override
public void onNetworkEapSimGsmAuthRequest(
ISupplicantStaNetworkCallback.NetworkRequestEapSimGsmAuthParams params) {
- logCallback("onNetworkEapSimGsmAuthRequest");
synchronized (mLock) {
+ logCallback("onNetworkEapSimGsmAuthRequest");
String[] data = new String[params.rands.size()];
int i = 0;
for (byte[] rand : params.rands) {
@@ -2511,8 +2555,8 @@
@Override
public void onNetworkEapSimUmtsAuthRequest(
ISupplicantStaNetworkCallback.NetworkRequestEapSimUmtsAuthParams params) {
- logCallback("onNetworkEapSimUmtsAuthRequest");
synchronized (mLock) {
+ logCallback("onNetworkEapSimUmtsAuthRequest");
String randHex = NativeUtil.hexStringFromByteArray(params.rand);
String autnHex = NativeUtil.hexStringFromByteArray(params.autn);
String[] data = {randHex, autnHex};
@@ -2523,8 +2567,8 @@
@Override
public void onNetworkEapIdentityRequest() {
- logCallback("onNetworkEapIdentityRequest");
synchronized (mLock) {
+ logCallback("onNetworkEapIdentityRequest");
mWifiMonitor.broadcastNetworkIdentityRequestEvent(
mIfaceName, mFramewokNetworkId, mSsid);
}
diff --git a/service/java/com/android/server/wifi/WifiApConfigStore.java b/service/java/com/android/server/wifi/WifiApConfigStore.java
index 9c90bcf..98a5932 100644
--- a/service/java/com/android/server/wifi/WifiApConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiApConfigStore.java
@@ -16,13 +16,16 @@
package com.android.server.wifi;
+import android.annotation.NonNull;
import android.content.Context;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.os.Environment;
+import android.text.TextUtils;
import android.util.Log;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -31,6 +34,7 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Random;
import java.util.UUID;
@@ -50,6 +54,15 @@
private static final int RAND_SSID_INT_MIN = 1000;
private static final int RAND_SSID_INT_MAX = 9999;
+ @VisibleForTesting
+ static final int SSID_MIN_LEN = 1;
+ @VisibleForTesting
+ static final int SSID_MAX_LEN = 32;
+ @VisibleForTesting
+ static final int PSK_MIN_LEN = 8;
+ @VisibleForTesting
+ static final int PSK_MAX_LEN = 63;
+
private WifiConfiguration mWifiApConfig = null;
private ArrayList<Integer> mAllowed2GChannel = null;
@@ -224,4 +237,110 @@
config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9, 13);
return config;
}
+
+ /**
+ * Verify provided SSID for existence, length and conversion to bytes
+ *
+ * @param ssid String ssid name
+ * @return boolean indicating ssid met requirements
+ */
+ private static boolean validateApConfigSsid(String ssid) {
+ if (TextUtils.isEmpty(ssid)) {
+ Log.d(TAG, "SSID for softap configuration must be set.");
+ return false;
+ }
+
+ if (ssid.length() < SSID_MIN_LEN || ssid.length() > SSID_MAX_LEN) {
+ Log.d(TAG, "SSID for softap configuration string size must be at least "
+ + SSID_MIN_LEN + " and not more than " + SSID_MAX_LEN);
+ return false;
+ }
+
+ try {
+ ssid.getBytes(StandardCharsets.UTF_8);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "softap config SSID verification failed: malformed string " + ssid);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Verify provided preSharedKey in ap config for WPA2_PSK network meets requirements.
+ */
+ private static boolean validateApConfigPreSharedKey(String preSharedKey) {
+ if (preSharedKey.length() < PSK_MIN_LEN || preSharedKey.length() > PSK_MAX_LEN) {
+ Log.d(TAG, "softap network password string size must be at least " + PSK_MIN_LEN
+ + " and no more than " + PSK_MAX_LEN);
+ return false;
+ }
+
+ try {
+ preSharedKey.getBytes(StandardCharsets.UTF_8);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "softap network password verification failed: malformed string");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Validate a WifiConfiguration is properly configured for use by SoftApManager.
+ *
+ * This method checks the length of the SSID and for sanity between security settings (if it
+ * requires a password, was one provided?).
+ *
+ * @param apConfig {@link WifiConfiguration} to use for softap mode
+ * @return boolean true if the provided config meets the minimum set of details, false
+ * otherwise.
+ */
+ static boolean validateApWifiConfiguration(@NonNull WifiConfiguration apConfig) {
+ // first check the SSID
+ if (!validateApConfigSsid(apConfig.SSID)) {
+ // failed SSID verificiation checks
+ return false;
+ }
+
+ // now check security settings: settings app allows open and WPA2 PSK
+ if (apConfig.allowedKeyManagement == null) {
+ Log.d(TAG, "softap config key management bitset was null");
+ return false;
+ }
+
+ String preSharedKey = apConfig.preSharedKey;
+ boolean hasPreSharedKey = !TextUtils.isEmpty(preSharedKey);
+ int authType;
+
+ try {
+ authType = apConfig.getAuthType();
+ } catch (IllegalStateException e) {
+ Log.d(TAG, "Unable to get AuthType for softap config: " + e.getMessage());
+ return false;
+ }
+
+ if (authType == KeyMgmt.NONE) {
+ // open networks should not have a password
+ if (hasPreSharedKey) {
+ Log.d(TAG, "open softap network should not have a password");
+ return false;
+ }
+ } else if (authType == KeyMgmt.WPA2_PSK) {
+ // this is a config that should have a password - check that first
+ if (!hasPreSharedKey) {
+ Log.d(TAG, "softap network password must be set");
+ return false;
+ }
+
+ if (!validateApConfigPreSharedKey(preSharedKey)) {
+ // failed preSharedKey checks
+ return false;
+ }
+ } else {
+ // this is not a supported security type
+ Log.d(TAG, "softap configs must either be open or WPA2 PSK networks");
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java
index 25a5a20..d8b4237 100644
--- a/service/java/com/android/server/wifi/WifiConfigManager.java
+++ b/service/java/com/android/server/wifi/WifiConfigManager.java
@@ -49,7 +49,6 @@
import com.android.server.LocalServices;
import com.android.server.wifi.WifiConfigStoreLegacy.WifiConfigStoreDataLegacy;
import com.android.server.wifi.hotspot2.PasspointManager;
-import com.android.server.wifi.util.ScanResultUtil;
import com.android.server.wifi.util.TelephonyUtil;
import com.android.server.wifi.util.WifiPermissionsUtil;
import com.android.server.wifi.util.WifiPermissionsWrapper;
@@ -123,7 +122,8 @@
1, // threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS
1, // threshold for DISABLED_NO_INTERNET
1, // threshold for DISABLED_BY_WIFI_MANAGER
- 1 // threshold for DISABLED_BY_USER_SWITCH
+ 1, // threshold for DISABLED_BY_USER_SWITCH
+ 1 // threshold for DISABLED_BY_WRONG_PASSWORD
};
/**
* Network Selection disable timeout for each kind of error. After the timeout milliseconds,
@@ -144,7 +144,8 @@
Integer.MAX_VALUE, // threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS
Integer.MAX_VALUE, // threshold for DISABLED_NO_INTERNET
Integer.MAX_VALUE, // threshold for DISABLED_BY_WIFI_MANAGER
- Integer.MAX_VALUE // threshold for DISABLED_BY_USER_SWITCH
+ Integer.MAX_VALUE, // threshold for DISABLED_BY_USER_SWITCH
+ Integer.MAX_VALUE // threshold for DISABLED_BY_WRONG_PASSWORD
};
/**
* Interface for other modules to listen to the saved network updated
@@ -304,6 +305,10 @@
*/
private boolean mDeferredUserUnlockRead = false;
/**
+ * Flag to indicate if SIM is present.
+ */
+ private boolean mSimPresent = false;
+ /**
* This is keeping track of the next network ID to be assigned. Any new networks will be
* assigned |mNextNetworkId| as network ID.
*/
@@ -677,10 +682,10 @@
final boolean isCreator = (config.creatorUid == uid);
- // Check if the |uid| holds the |OVERRIDE_CONFIG_WIFI| permission if the caller asks us to
+ // Check if the |uid| holds the |NETWORK_SETTINGS| permission if the caller asks us to
// bypass the lockdown checks.
if (ignoreLockdown) {
- return mWifiPermissionsUtil.checkConfigOverridePermission(uid);
+ return mWifiPermissionsUtil.checkNetworkSettingsPermission(uid);
}
// Check if device has DPM capability. If it has and |dpmi| is still null, then we
@@ -695,13 +700,14 @@
final boolean isConfigEligibleForLockdown = dpmi != null && dpmi.isActiveAdminWithPolicy(
config.creatorUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
if (!isConfigEligibleForLockdown) {
- return isCreator || mWifiPermissionsUtil.checkConfigOverridePermission(uid);
+ return isCreator || mWifiPermissionsUtil.checkNetworkSettingsPermission(uid);
}
final ContentResolver resolver = mContext.getContentResolver();
final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
- return !isLockdownFeatureEnabled && mWifiPermissionsUtil.checkConfigOverridePermission(uid);
+ return !isLockdownFeatureEnabled
+ && mWifiPermissionsUtil.checkNetworkSettingsPermission(uid);
}
/**
@@ -942,6 +948,10 @@
WifiConfiguration existingInternalConfig = getInternalConfiguredNetwork(config);
// No existing network found. So, potentially a network add.
if (existingInternalConfig == null) {
+ if (!WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD)) {
+ Log.e(TAG, "Cannot add network with invalid config");
+ return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
+ }
newInternalConfig = createNewInternalWifiConfigurationFromExternal(config, uid);
// Since the original config provided may have had an empty
// {@link WifiConfiguration#allowedKeyMgmt} field, check again if we already have a
@@ -950,6 +960,11 @@
}
// Existing network found. So, a network update.
if (existingInternalConfig != null) {
+ if (!WifiConfigurationUtil.validate(
+ config, WifiConfigurationUtil.VALIDATE_FOR_UPDATE)) {
+ Log.e(TAG, "Cannot update network with invalid config");
+ return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
+ }
// Check for the app's permission before we let it update this network.
if (!canModifyNetwork(existingInternalConfig, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
@@ -965,7 +980,7 @@
if (WifiConfigurationUtil.hasProxyChanged(existingInternalConfig, newInternalConfig)
&& !canModifyProxySettings(uid)) {
Log.e(TAG, "UID " + uid + " does not have permission to modify proxy Settings "
- + config.configKey() + ". Must have OVERRIDE_WIFI_CONFIG,"
+ + config.configKey() + ". Must have NETWORK_SETTINGS,"
+ " or be device or profile owner.");
return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
}
@@ -998,7 +1013,12 @@
// Add it to our internal map. This will replace any existing network configuration for
// updates.
- mConfiguredNetworks.put(newInternalConfig);
+ try {
+ mConfiguredNetworks.put(newInternalConfig);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Failed to add network to config map", e);
+ return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
+ }
if (mDeletedEphemeralSSIDs.remove(config.SSID)) {
if (mVerboseLoggingEnabled) {
@@ -1188,6 +1208,33 @@
}
/**
+ * Iterates through the internal list of configured networks and removes any ephemeral or
+ * passpoint network configurations which are transient in nature.
+ *
+ * @return true if a network was removed, false otherwise.
+ */
+ public boolean removeAllEphemeralOrPasspointConfiguredNetworks() {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Removing all passpoint or ephemeral configured networks");
+ }
+ boolean didRemove = false;
+ WifiConfiguration[] copiedConfigs =
+ mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
+ for (WifiConfiguration config : copiedConfigs) {
+ if (config.isPasspoint()) {
+ Log.d(TAG, "Removing passpoint network config " + config.configKey());
+ removeNetwork(config.networkId, mSystemUiUid);
+ didRemove = true;
+ } else if (config.ephemeral) {
+ Log.d(TAG, "Removing ephemeral network config " + config.configKey());
+ removeNetwork(config.networkId, mSystemUiUid);
+ didRemove = true;
+ }
+ }
+ return didRemove;
+ }
+
+ /**
* Helper method to mark a network enabled for network selection.
*/
private void setNetworkSelectionEnabled(WifiConfiguration config) {
@@ -1479,7 +1526,7 @@
* @return true if |uid| has the necessary permission to trigger explicit connection to the
* network, false otherwise.
* Note: This returns true only for the system settings/sysui app which holds the
- * {@link android.Manifest.permission#OVERRIDE_WIFI_CONFIG} permission. We don't want to let
+ * {@link android.Manifest.permission#NETWORK_SETTINGS} permission. We don't want to let
* any other app force connection to a network.
*/
public boolean checkAndUpdateLastConnectUid(int networkId, int uid) {
@@ -1885,41 +1932,44 @@
}
/**
- * Retrieves a saved network corresponding to the provided scan detail if one exists.
+ * Retrieves a configured network corresponding to the provided scan detail if one exists.
*
* @param scanDetail ScanDetail instance to use for looking up the network.
* @return WifiConfiguration object representing the network corresponding to the scanDetail,
* null if none exists.
*/
- private WifiConfiguration getSavedNetworkForScanDetail(ScanDetail scanDetail) {
+ public WifiConfiguration getConfiguredNetworkForScanDetail(ScanDetail scanDetail) {
ScanResult scanResult = scanDetail.getScanResult();
if (scanResult == null) {
Log.e(TAG, "No scan result found in scan detail");
return null;
}
- for (WifiConfiguration config : getInternalConfiguredNetworks()) {
- if (ScanResultUtil.doesScanResultMatchWithNetwork(scanResult, config)) {
- if (mVerboseLoggingEnabled) {
- Log.v(TAG, "getSavedNetworkFromScanDetail Found " + config.configKey()
- + " for " + scanResult.SSID + "[" + scanResult.capabilities + "]");
- }
- return config;
+ WifiConfiguration config = null;
+ try {
+ config = mConfiguredNetworks.getByScanResultForCurrentUser(scanResult);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Failed to lookup network from config map", e);
+ }
+ if (config != null) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "getSavedNetworkFromScanDetail Found " + config.configKey()
+ + " for " + scanResult.SSID + "[" + scanResult.capabilities + "]");
}
}
- return null;
+ return config;
}
/**
- * Retrieves a saved network corresponding to the provided scan detail if one exists and caches
- * the provided |scanDetail| into the corresponding scan detail cache entry
+ * Retrieves a configured network corresponding to the provided scan detail if one exists and
+ * caches the provided |scanDetail| into the corresponding scan detail cache entry
* {@link #mScanDetailCaches} for the retrieved network.
*
* @param scanDetail input a scanDetail from the scan result
* @return WifiConfiguration object representing the network corresponding to the scanDetail,
* null if none exists.
*/
- public WifiConfiguration getSavedNetworkForScanDetailAndCache(ScanDetail scanDetail) {
- WifiConfiguration network = getSavedNetworkForScanDetail(scanDetail);
+ public WifiConfiguration getConfiguredNetworkForScanDetailAndCache(ScanDetail scanDetail) {
+ WifiConfiguration network = getConfiguredNetworkForScanDetail(scanDetail);
if (network == null) {
return null;
}
@@ -2270,7 +2320,8 @@
Iterator<WifiConfiguration> iter = networks.iterator();
while (iter.hasNext()) {
WifiConfiguration config = iter.next();
- if (config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) {
+ if (config.ephemeral || config.isPasspoint()
+ || config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) {
iter.remove();
}
}
@@ -2365,11 +2416,14 @@
/**
* Resets all sim networks state.
*/
- public void resetSimNetworks() {
+ public void resetSimNetworks(boolean simPresent) {
if (mVerboseLoggingEnabled) localLog("resetSimNetworks");
for (WifiConfiguration config : getInternalConfiguredNetworks()) {
if (TelephonyUtil.isSimConfig(config)) {
- String currentIdentity = TelephonyUtil.getSimIdentity(mTelephonyManager, config);
+ String currentIdentity = null;
+ if (simPresent) {
+ currentIdentity = TelephonyUtil.getSimIdentity(mTelephonyManager, config);
+ }
// Update the loaded config
config.enterpriseConfig.setIdentity(currentIdentity);
if (config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.PEAP) {
@@ -2377,6 +2431,16 @@
}
}
}
+ mSimPresent = simPresent;
+ }
+
+ /**
+ * Check if SIM is present.
+ *
+ * @return True if SIM is present, otherwise false.
+ */
+ public boolean isSimPresent() {
+ return mSimPresent;
}
/**
@@ -2554,7 +2618,11 @@
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Adding network from shared store " + configuration.configKey());
}
- mConfiguredNetworks.put(configuration);
+ try {
+ mConfiguredNetworks.put(configuration);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Failed to add network to config map", e);
+ }
}
}
@@ -2573,7 +2641,11 @@
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Adding network from user store " + configuration.configKey());
}
- mConfiguredNetworks.put(configuration);
+ try {
+ mConfiguredNetworks.put(configuration);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Failed to add network to config map", e);
+ }
}
for (String ssid : deletedEphemeralSSIDs) {
mDeletedEphemeralSSIDs.add(ssid);
@@ -2815,15 +2887,15 @@
DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- final boolean hasConfigOverridePermission =
- mWifiPermissionsUtil.checkConfigOverridePermission(uid);
+ final boolean hasNetworkSettingsPermission =
+ mWifiPermissionsUtil.checkNetworkSettingsPermission(uid);
// If |uid| corresponds to the device owner, allow all modifications.
- if (isUidDeviceOwner || isUidProfileOwner || hasConfigOverridePermission) {
+ if (isUidDeviceOwner || isUidProfileOwner || hasNetworkSettingsPermission) {
return true;
}
if (mVerboseLoggingEnabled) {
Log.v(TAG, "UID: " + uid + " cannot modify WifiConfiguration proxy settings."
- + " ConfigOverride=" + hasConfigOverridePermission
+ + " ConfigOverride=" + hasNetworkSettingsPermission
+ " DeviceOwner=" + isUidDeviceOwner
+ " ProfileOwner=" + isUidProfileOwner);
}
diff --git a/service/java/com/android/server/wifi/WifiConfigurationUtil.java b/service/java/com/android/server/wifi/WifiConfigurationUtil.java
index bbfde46..fef78aa 100644
--- a/service/java/com/android/server/wifi/WifiConfigurationUtil.java
+++ b/service/java/com/android/server/wifi/WifiConfigurationUtil.java
@@ -18,15 +18,20 @@
import android.content.pm.UserInfo;
import android.net.IpConfiguration;
+import android.net.StaticIpConfiguration;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiScanner;
import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.util.NativeUtil;
import java.security.cert.X509Certificate;
import java.util.Arrays;
+import java.util.BitSet;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
@@ -38,6 +43,22 @@
* > Helper methods to identify the encryption of a WifiConfiguration object.
*/
public class WifiConfigurationUtil {
+ private static final String TAG = "WifiConfigurationUtil";
+
+ /**
+ * Constants used for validating external config objects.
+ */
+ private static final int ENCLOSING_QUTOES_LEN = 2;
+ private static final int SSID_ASCII_MIN_LEN = 1 + ENCLOSING_QUTOES_LEN;
+ private static final int SSID_ASCII_MAX_LEN = 32 + ENCLOSING_QUTOES_LEN;
+ private static final int SSID_HEX_MIN_LEN = 2;
+ private static final int SSID_HEX_MAX_LEN = 64;
+ private static final int PSK_ASCII_MIN_LEN = 8 + ENCLOSING_QUTOES_LEN;
+ private static final int PSK_ASCII_MAX_LEN = 63 + ENCLOSING_QUTOES_LEN;
+ private static final int PSK_HEX_LEN = 64;
+ @VisibleForTesting
+ public static final String PASSWORD_MASK = "*";
+
/**
* Check whether a network configuration is visible to a user or any of its managed profiles.
*
@@ -172,6 +193,12 @@
!= newEnterpriseConfig.getPhase2Method()) {
return true;
}
+ if (!TextUtils.equals(existingEnterpriseConfig.getIdentity(),
+ newEnterpriseConfig.getIdentity())
+ || !TextUtils.equals(existingEnterpriseConfig.getAnonymousIdentity(),
+ newEnterpriseConfig.getAnonymousIdentity())) {
+ return true;
+ }
X509Certificate[] existingCaCerts = existingEnterpriseConfig.getCaCertificates();
X509Certificate[] newCaCerts = newEnterpriseConfig.getCaCertificates();
if (!Arrays.equals(existingCaCerts, newCaCerts)) {
@@ -234,12 +261,185 @@
return false;
}
+ private static boolean validateSsid(String ssid, boolean isAdd) {
+ if (isAdd) {
+ if (ssid == null) {
+ Log.e(TAG, "validateSsid : null string");
+ return false;
+ }
+ } else {
+ if (ssid == null) {
+ // This is an update, so the SSID can be null if that is not being changed.
+ return true;
+ }
+ }
+ if (ssid.isEmpty()) {
+ Log.e(TAG, "validateSsid failed: empty string");
+ return false;
+ }
+ if (ssid.startsWith("\"")) {
+ // ASCII SSID string
+ if (ssid.length() < SSID_ASCII_MIN_LEN) {
+ Log.e(TAG, "validateSsid failed: ascii string size too small: " + ssid.length());
+ return false;
+ }
+ if (ssid.length() > SSID_ASCII_MAX_LEN) {
+ Log.e(TAG, "validateSsid failed: ascii string size too large: " + ssid.length());
+ return false;
+ }
+ } else {
+ // HEX SSID string
+ if (ssid.length() < SSID_HEX_MIN_LEN) {
+ Log.e(TAG, "validateSsid failed: hex string size too small: " + ssid.length());
+ return false;
+ }
+ if (ssid.length() > SSID_HEX_MAX_LEN) {
+ Log.e(TAG, "validateSsid failed: hex string size too large: " + ssid.length());
+ return false;
+ }
+ }
+ try {
+ NativeUtil.decodeSsid(ssid);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "validateSsid failed: malformed string: " + ssid);
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean validatePsk(String psk, boolean isAdd) {
+ if (isAdd) {
+ if (psk == null) {
+ Log.e(TAG, "validatePsk: null string");
+ return false;
+ }
+ } else {
+ if (psk == null) {
+ // This is an update, so the psk can be null if that is not being changed.
+ return true;
+ } else if (psk.equals(PASSWORD_MASK)) {
+ // This is an update, so the app might have returned back the masked password, let
+ // it thru. WifiConfigManager will handle it.
+ return true;
+ }
+ }
+ if (psk.isEmpty()) {
+ Log.e(TAG, "validatePsk failed: empty string");
+ return false;
+ }
+ if (psk.startsWith("\"")) {
+ // ASCII PSK string
+ if (psk.length() < PSK_ASCII_MIN_LEN) {
+ Log.e(TAG, "validatePsk failed: ascii string size too small: " + psk.length());
+ return false;
+ }
+ if (psk.length() > PSK_ASCII_MAX_LEN) {
+ Log.e(TAG, "validatePsk failed: ascii string size too large: " + psk.length());
+ return false;
+ }
+ } else {
+ // HEX PSK string
+ if (psk.length() != PSK_HEX_LEN) {
+ Log.e(TAG, "validatePsk failed: hex string size mismatch: " + psk.length());
+ return false;
+ }
+ }
+ try {
+ NativeUtil.hexOrQuotedAsciiStringToBytes(psk);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "validatePsk failed: malformed string: " + psk);
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean validateKeyMgmt(BitSet keyMgmnt) {
+ if (keyMgmnt == null) {
+ Log.e(TAG, "validateKeyMgmt failed: null bitset");
+ return false;
+ }
+ if (keyMgmnt.cardinality() > 1) {
+ if (keyMgmnt.cardinality() != 2) {
+ Log.e(TAG, "validateKeyMgmt failed: cardinality != 2");
+ return false;
+ }
+ if (!keyMgmnt.get(WifiConfiguration.KeyMgmt.WPA_EAP)) {
+ Log.e(TAG, "validateKeyMgmt failed: not WPA_EAP");
+ return false;
+ }
+ if (!keyMgmnt.get(WifiConfiguration.KeyMgmt.IEEE8021X)
+ && !keyMgmnt.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
+ Log.e(TAG, "validateKeyMgmt failed: not PSK or 8021X");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean validateIpConfiguration(IpConfiguration ipConfig) {
+ if (ipConfig == null) {
+ Log.e(TAG, "validateIpConfiguration failed: null IpConfiguration");
+ return false;
+ }
+ if (ipConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC) {
+ StaticIpConfiguration staticIpConfig = ipConfig.getStaticIpConfiguration();
+ if (staticIpConfig == null) {
+ Log.e(TAG, "validateIpConfiguration failed: null StaticIpConfiguration");
+ return false;
+ }
+ if (staticIpConfig.ipAddress == null) {
+ Log.e(TAG, "validateIpConfiguration failed: null static ip Address");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Enums to specify if the provided config is being validated for add or update.
+ */
+ public static final boolean VALIDATE_FOR_ADD = true;
+ public static final boolean VALIDATE_FOR_UPDATE = false;
+
+ /**
+ * Validate the configuration received from an external application.
+ *
+ * This method checks for the following parameters:
+ * 1. {@link WifiConfiguration#SSID}
+ * 2. {@link WifiConfiguration#preSharedKey}
+ * 3. {@link WifiConfiguration#allowedKeyManagement}
+ * 4. {@link WifiConfiguration#getIpConfiguration()}
+ *
+ * @param config {@link WifiConfiguration} received from an external application.
+ * @param isAdd {@link #VALIDATE_FOR_ADD} to indicate a network config received for an add,
+ * {@link #VALIDATE_FOR_UPDATE} for a network config received for an update.
+ * These 2 cases need to be handled differently because the config received for an
+ * update could contain only the fields that are being changed.
+ * @return true if the parameters are valid, false otherwise.
+ */
+ public static boolean validate(WifiConfiguration config, boolean isAdd) {
+ if (!validateSsid(config.SSID, isAdd)) {
+ return false;
+ }
+ if (!validateKeyMgmt(config.allowedKeyManagement)) {
+ return false;
+ }
+ if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)
+ && !validatePsk(config.preSharedKey, isAdd)) {
+ return false;
+ }
+ if (!validateIpConfiguration(config.getIpConfiguration())) {
+ return false;
+ }
+ // TBD: Validate some enterprise params as well in the future here.
+ return true;
+ }
+
/**
* Check if the provided two networks are the same.
- * Note: This does not check if network selection BSSID's are the same.
*
- * @param config Configuration corresponding to a network.
- * @param config1 Configuration corresponding to another network.
+ * @param config Configuration corresponding to a network.
+ * @param config1 Configuration corresponding to another network.
*
* @return true if |config| and |config1| are the same network.
* false otherwise.
@@ -257,6 +457,13 @@
if (!Objects.equals(config.SSID, config1.SSID)) {
return false;
}
+ String networkSelectionBSSID = config.getNetworkSelectionStatus()
+ .getNetworkSelectionBSSID();
+ String networkSelectionBSSID1 = config1.getNetworkSelectionStatus()
+ .getNetworkSelectionBSSID();
+ if (!Objects.equals(networkSelectionBSSID, networkSelectionBSSID1)) {
+ return false;
+ }
if (WifiConfigurationUtil.hasCredentialChanged(config, config1)) {
return false;
}
diff --git a/service/java/com/android/server/wifi/WifiConnectivityManager.java b/service/java/com/android/server/wifi/WifiConnectivityManager.java
index f45d17b..344242a 100644
--- a/service/java/com/android/server/wifi/WifiConnectivityManager.java
+++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java
@@ -20,6 +20,7 @@
import android.app.AlarmManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.net.wifi.ScanResult;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiConfiguration;
@@ -107,10 +108,6 @@
public static final int MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS = 4 * 60 * 1000; // 4 mins
// Max number of connection attempts in the above time interval.
public static final int MAX_CONNECTION_ATTEMPTS_RATE = 6;
- // Packet tx/rx rates to determine if we want to do partial vs full scans.
- // TODO(b/31180330): Make these device configs.
- public static final int MAX_TX_PACKET_FOR_FULL_SCANS = 8;
- public static final int MAX_RX_PACKET_FOR_FULL_SCANS = 16;
// WifiStateMachine has a bunch of states. From the
// WifiConnectivityManager's perspective it only cares
@@ -136,6 +133,7 @@
private final WifiConnectivityHelper mConnectivityHelper;
private final WifiNetworkSelector mNetworkSelector;
private final WifiLastResortWatchdog mWifiLastResortWatchdog;
+ private final WifiNotificationController mWifiNotificationController;
private final WifiMetrics mWifiMetrics;
private final AlarmManager mAlarmManager;
private final Handler mEventHandler;
@@ -160,6 +158,8 @@
// Device configs
private boolean mEnableAutoJoinWhenAssociated;
private boolean mWaitForFullBandScanResults = false;
+ private int mFullScanMaxTxRate;
+ private int mFullScanMaxRxRate;
// PNO settings
private int mMin5GHzRssi;
@@ -262,13 +262,17 @@
mStateMachine.isConnected(), mStateMachine.isDisconnected(),
mUntrustedConnectionAllowed);
mWifiLastResortWatchdog.updateAvailableNetworks(
- mNetworkSelector.getFilteredScanDetails());
+ mNetworkSelector.getConnectableScanDetails());
mWifiMetrics.countScanResults(scanDetails);
if (candidate != null) {
localLog(listenerName + ": WNS candidate-" + candidate.SSID);
connectToNetwork(candidate);
return true;
} else {
+ if (mWifiState == WIFI_STATE_DISCONNECTED) {
+ mWifiNotificationController.handleScanResults(
+ mNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks());
+ }
return false;
}
}
@@ -317,7 +321,10 @@
mWaitForFullBandScanResults = false;
}
}
-
+ if (results.length > 0) {
+ mWifiMetrics.incrementAvailableNetworksHistograms(mScanDetails,
+ results[0].isAllChannelsScanned());
+ }
boolean wasConnectAttempted = handleScanResults(mScanDetails, "AllSingleScanListener");
clearScanDetails();
@@ -528,7 +535,8 @@
WifiConnectivityManager(Context context, WifiStateMachine stateMachine,
WifiScanner scanner, WifiConfigManager configManager, WifiInfo wifiInfo,
WifiNetworkSelector networkSelector, WifiConnectivityHelper connectivityHelper,
- WifiLastResortWatchdog wifiLastResortWatchdog, WifiMetrics wifiMetrics,
+ WifiLastResortWatchdog wifiLastResortWatchdog,
+ WifiNotificationController wifiNotificationController, WifiMetrics wifiMetrics,
Looper looper, Clock clock, LocalLog localLog, boolean enable,
FrameworkFacade frameworkFacade,
SavedNetworkEvaluator savedNetworkEvaluator,
@@ -542,6 +550,7 @@
mConnectivityHelper = connectivityHelper;
mLocalLog = localLog;
mWifiLastResortWatchdog = wifiLastResortWatchdog;
+ mWifiNotificationController = wifiNotificationController;
mWifiMetrics = wifiMetrics;
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mEventHandler = new Handler(looper);
@@ -570,6 +579,10 @@
R.integer.config_wifi_framework_RSSI_SCORE_OFFSET))
* context.getResources().getInteger(
R.integer.config_wifi_framework_RSSI_SCORE_SLOPE);
+ mFullScanMaxTxRate = context.getResources().getInteger(
+ R.integer.config_wifi_framework_max_tx_rate_for_full_scan);
+ mFullScanMaxRxRate = context.getResources().getInteger(
+ R.integer.config_wifi_framework_max_rx_rate_for_full_scan);
localLog("PNO settings:" + " min5GHzRssi " + mMin5GHzRssi
+ " min24GHzRssi " + mMin24GHzRssi
@@ -578,8 +591,8 @@
+ " secureNetworkBonus " + mSecureBonus
+ " initialScoreMax " + mInitialScoreMax);
- boolean hs2Enabled = context.getResources().getBoolean(
- R.bool.config_wifi_hotspot2_enabled);
+ boolean hs2Enabled = context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_PASSPOINT);
localLog("Passpoint is: " + (hs2Enabled ? "enabled" : "disabled"));
// Register the network evaluators
@@ -679,7 +692,7 @@
return;
}
- Long elapsedTimeMillis = mClock.getElapsedSinceBootMillis();
+ long elapsedTimeMillis = mClock.getElapsedSinceBootMillis();
if (!mScreenOn && shouldSkipConnectionAttempt(elapsedTimeMillis)) {
localLog("connectToNetwork: Too many connection attempts. Skipping this attempt!");
mTotalConnectivityAttemptsRateLimited++;
@@ -797,8 +810,8 @@
// If the WiFi traffic is heavy, only partial scan is initiated.
if (mWifiState == WIFI_STATE_CONNECTED
- && (mWifiInfo.txSuccessRate > MAX_TX_PACKET_FOR_FULL_SCANS
- || mWifiInfo.rxSuccessRate > MAX_RX_PACKET_FOR_FULL_SCANS)) {
+ && (mWifiInfo.txSuccessRate > mFullScanMaxTxRate
+ || mWifiInfo.rxSuccessRate > mFullScanMaxRxRate)) {
localLog("No full band scan due to ongoing traffic");
isFullBandScan = false;
}
@@ -1024,6 +1037,8 @@
mScreenOn = screenOn;
+ mWifiNotificationController.handleScreenStateChanged(screenOn);
+
startConnectivityScan(SCAN_ON_SCHEDULE);
}
@@ -1051,6 +1066,10 @@
mWifiState = state;
+ if (mWifiState == WIFI_STATE_CONNECTED) {
+ mWifiNotificationController.clearPendingNotification(false /* resetRepeatDelay */);
+ }
+
// Reset BSSID of last connection attempt and kick off
// the watchdog timer if entering disconnected state.
if (mWifiState == WIFI_STATE_DISCONNECTED) {
@@ -1284,6 +1303,7 @@
stopConnectivityScan();
clearBssidBlacklist();
resetLastPeriodicSingleScanTimeStamp();
+ mWifiNotificationController.clearPendingNotification(true /* resetRepeatDelay */);
mLastConnectionAttemptBssid = null;
mWaitForFullBandScanResults = false;
}
@@ -1343,5 +1363,6 @@
pw.println("WifiConnectivityManager - Log Begin ----");
mLocalLog.dump(fd, pw, args);
pw.println("WifiConnectivityManager - Log End ----");
+ mWifiNotificationController.dump(fd, pw, args);
}
}
diff --git a/service/java/com/android/server/wifi/WifiDiagnostics.java b/service/java/com/android/server/wifi/WifiDiagnostics.java
index b2acb2a..42078ef 100644
--- a/service/java/com/android/server/wifi/WifiDiagnostics.java
+++ b/service/java/com/android/server/wifi/WifiDiagnostics.java
@@ -497,7 +497,8 @@
}
private boolean enableVerboseLoggingForDogfood() {
- return false;
+ return true;
+
}
private BugReport captureBugreport(int errorCode, boolean captureFWDump) {
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index c517785..80daaea 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -44,6 +44,7 @@
import com.android.server.am.BatteryStatsService;
import com.android.server.net.DelayedDiskWrite;
import com.android.server.net.IpConfigStore;
+import com.android.server.wifi.aware.WifiAwareMetrics;
import com.android.server.wifi.hotspot2.LegacyPasspointConfigParser;
import com.android.server.wifi.hotspot2.PasspointManager;
import com.android.server.wifi.hotspot2.PasspointNetworkEvaluator;
@@ -160,7 +161,8 @@
mWifiStateMachineHandlerThread = new HandlerThread("WifiStateMachine");
mWifiStateMachineHandlerThread.start();
Looper wifiStateMachineLooper = mWifiStateMachineHandlerThread.getLooper();
- mWifiMetrics = new WifiMetrics(mClock, wifiStateMachineLooper);
+ WifiAwareMetrics awareMetrics = new WifiAwareMetrics(mClock);
+ mWifiMetrics = new WifiMetrics(mClock, wifiStateMachineLooper, awareMetrics);
// Modules interacting with Native.
mWifiMonitor = new WifiMonitor(this);
mHalDeviceManager = new HalDeviceManager();
@@ -203,10 +205,12 @@
mWifiKeyStore, mWifiConfigStore, mWifiConfigStoreLegacy, mWifiPermissionsUtil,
mWifiPermissionsWrapper, new NetworkListStoreData(),
new DeletedEphemeralSsidsStoreData());
+ mWifiMetrics.setWifiConfigManager(mWifiConfigManager);
mWifiConnectivityHelper = new WifiConnectivityHelper(mWifiNative);
mConnectivityLocalLog = new LocalLog(ActivityManager.isLowRamDeviceStatic() ? 256 : 512);
mWifiNetworkSelector = new WifiNetworkSelector(mContext, mWifiConfigManager, mClock,
mConnectivityLocalLog);
+ mWifiMetrics.setWifiNetworkSelector(mWifiNetworkSelector);
mSavedNetworkEvaluator = new SavedNetworkEvaluator(mContext,
mWifiConfigManager, mClock, mConnectivityLocalLog, mWifiConnectivityHelper);
mScoredNetworkEvaluator = new ScoredNetworkEvaluator(context, wifiStateMachineLooper,
@@ -214,21 +218,24 @@
mWifiNetworkScoreCache);
mSimAccessor = new SIMAccessor(mContext);
mPasspointManager = new PasspointManager(mContext, mWifiNative, mWifiKeyStore, mClock,
- mSimAccessor, new PasspointObjectFactory(), mWifiConfigManager, mWifiConfigStore);
+ mSimAccessor, new PasspointObjectFactory(), mWifiConfigManager, mWifiConfigStore,
+ mWifiMetrics);
mPasspointNetworkEvaluator = new PasspointNetworkEvaluator(
mPasspointManager, mWifiConfigManager, mConnectivityLocalLog);
+ mWifiMetrics.setPasspointManager(mPasspointManager);
// mWifiStateMachine has an implicit dependency on mJavaRuntime due to WifiDiagnostics.
mJavaRuntime = Runtime.getRuntime();
mWifiStateMachine = new WifiStateMachine(mContext, mFrameworkFacade,
wifiStateMachineLooper, UserManager.get(mContext),
- this, mBackupManagerProxy, mCountryCode, mWifiNative);
+ this, mBackupManagerProxy, mCountryCode, mWifiNative,
+ new WrongPasswordNotifier(mContext, mFrameworkFacade));
mCertManager = new WifiCertManager(mContext);
mNotificationController = new WifiNotificationController(mContext,
- mWifiServiceHandlerThread.getLooper(), mFrameworkFacade, null, this);
+ mWifiStateMachineHandlerThread.getLooper(), mFrameworkFacade, null);
mLockManager = new WifiLockManager(mContext, BatteryStatsService.getService());
mWifiController = new WifiController(mContext, mWifiStateMachine, mSettingsStore,
mLockManager, mWifiServiceHandlerThread.getLooper(), mFrameworkFacade);
- mSelfRecovery = new SelfRecovery(mWifiController);
+ mSelfRecovery = new SelfRecovery(mWifiController, mClock);
mWifiLastResortWatchdog = new WifiLastResortWatchdog(mSelfRecovery, mWifiMetrics);
mWifiMulticastLockManager = new WifiMulticastLockManager(mWifiStateMachine,
BatteryStatsService.getService());
@@ -300,10 +307,6 @@
return mCertManager;
}
- public WifiNotificationController getWifiNotificationController() {
- return mNotificationController;
- }
-
public WifiLockManager getWifiLockManager() {
return mLockManager;
}
@@ -427,9 +430,10 @@
boolean hasConnectionRequests) {
return new WifiConnectivityManager(mContext, mWifiStateMachine, getWifiScanner(),
mWifiConfigManager, wifiInfo, mWifiNetworkSelector, mWifiConnectivityHelper,
- mWifiLastResortWatchdog, mWifiMetrics, mWifiStateMachineHandlerThread.getLooper(),
- mClock, mConnectivityLocalLog, hasConnectionRequests, mFrameworkFacade,
- mSavedNetworkEvaluator, mScoredNetworkEvaluator, mPasspointNetworkEvaluator);
+ mWifiLastResortWatchdog, mNotificationController, mWifiMetrics,
+ mWifiStateMachineHandlerThread.getLooper(), mClock, mConnectivityLocalLog,
+ hasConnectionRequests, mFrameworkFacade, mSavedNetworkEvaluator,
+ mScoredNetworkEvaluator, mPasspointNetworkEvaluator);
}
public WifiPermissionsUtil getWifiPermissionsUtil() {
diff --git a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java
index 4cc8a20..0c1050c 100644
--- a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java
+++ b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java
@@ -365,7 +365,7 @@
/**
* Clear failure counts for each network in recentAvailableNetworks
*/
- private void clearAllFailureCounts() {
+ public void clearAllFailureCounts() {
if (mVerboseLoggingEnabled) Log.v(TAG, "clearAllFailureCounts.");
for (Map.Entry<String, AvailableNetworkFailureCount> entry
: mRecentAvailableNetworks.entrySet()) {
diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java
index 92a6fd6..7dfdb86 100644
--- a/service/java/com/android/server/wifi/WifiMetrics.java
+++ b/service/java/com/android/server/wifi/WifiMetrics.java
@@ -28,9 +28,14 @@
import android.os.Message;
import android.util.Base64;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseIntArray;
+import com.android.server.wifi.aware.WifiAwareMetrics;
import com.android.server.wifi.hotspot2.NetworkDetail;
+import com.android.server.wifi.hotspot2.PasspointManager;
+import com.android.server.wifi.hotspot2.PasspointMatch;
+import com.android.server.wifi.hotspot2.PasspointProvider;
import com.android.server.wifi.nano.WifiMetricsProto;
import com.android.server.wifi.nano.WifiMetricsProto.StaEvent;
import com.android.server.wifi.nano.WifiMetricsProto.StaEvent.ConfigInfo;
@@ -42,8 +47,10 @@
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Calendar;
+import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
/**
* Provides storage for wireless connectivity metrics, as they are generated.
@@ -69,10 +76,20 @@
private static final int MAX_WIFI_SCORE = NetworkAgent.WIFI_BASE_SCORE;
private final Object mLock = new Object();
private static final int MAX_CONNECTION_EVENTS = 256;
+ // Largest bucket in the NumConnectableNetworkCount histogram,
+ // anything large will be stored in this bucket
+ public static final int MAX_CONNECTABLE_SSID_NETWORK_BUCKET = 20;
+ public static final int MAX_CONNECTABLE_BSSID_NETWORK_BUCKET = 50;
+ public static final int MAX_TOTAL_SCAN_RESULT_SSIDS_BUCKET = 100;
+ public static final int MAX_TOTAL_SCAN_RESULTS_BUCKET = 250;
private Clock mClock;
private boolean mScreenOn;
private int mWifiState;
+ private WifiAwareMetrics mWifiAwareMetrics;
private Handler mHandler;
+ private WifiConfigManager mWifiConfigManager;
+ private WifiNetworkSelector mWifiNetworkSelector;
+ private PasspointManager mPasspointManager;
/**
* Metrics are stored within an instance of the WifiLog proto during runtime,
* The ConnectionEvent, SystemStateEntries & ScanReturnEntries metrics are stored during
@@ -117,6 +134,20 @@
private final SparseIntArray mWifiScoreCounts = new SparseIntArray();
/** Mapping of SoftApManager start SoftAp return codes to counts */
private final SparseIntArray mSoftApManagerReturnCodeCounts = new SparseIntArray();
+
+ private final SparseIntArray mTotalSsidsInScanHistogram = new SparseIntArray();
+ private final SparseIntArray mTotalBssidsInScanHistogram = new SparseIntArray();
+ private final SparseIntArray mAvailableOpenSsidsInScanHistogram = new SparseIntArray();
+ private final SparseIntArray mAvailableOpenBssidsInScanHistogram = new SparseIntArray();
+ private final SparseIntArray mAvailableSavedSsidsInScanHistogram = new SparseIntArray();
+ private final SparseIntArray mAvailableSavedBssidsInScanHistogram = new SparseIntArray();
+ private final SparseIntArray mAvailableOpenOrSavedSsidsInScanHistogram = new SparseIntArray();
+ private final SparseIntArray mAvailableOpenOrSavedBssidsInScanHistogram = new SparseIntArray();
+ private final SparseIntArray mAvailableSavedPasspointProviderProfilesInScanHistogram =
+ new SparseIntArray();
+ private final SparseIntArray mAvailableSavedPasspointProviderBssidsInScanHistogram =
+ new SparseIntArray();
+
class RouterFingerPrint {
private WifiMetricsProto.RouterFingerPrint mRouterFingerPrintProto;
RouterFingerPrint() {
@@ -344,12 +375,13 @@
}
}
- public WifiMetrics(Clock clock, Looper looper) {
+ public WifiMetrics(Clock clock, Looper looper, WifiAwareMetrics awareMetrics) {
mClock = clock;
mCurrentConnectionEvent = null;
mScreenOn = true;
mWifiState = WifiMetricsProto.WifiLog.WIFI_DISABLED;
mRecordStartTimeSec = mClock.getElapsedSinceBootMillis() / 1000;
+ mWifiAwareMetrics = awareMetrics;
mHandler = new Handler(looper) {
public void handleMessage(Message msg) {
@@ -360,6 +392,21 @@
};
}
+ /** Sets internal WifiConfigManager member */
+ public void setWifiConfigManager(WifiConfigManager wifiConfigManager) {
+ mWifiConfigManager = wifiConfigManager;
+ }
+
+ /** Sets internal WifiNetworkSelector member */
+ public void setWifiNetworkSelector(WifiNetworkSelector wifiNetworkSelector) {
+ mWifiNetworkSelector = wifiNetworkSelector;
+ }
+
+ /** Sets internal PasspointManager member */
+ public void setPasspointManager(PasspointManager passpointManager) {
+ mPasspointManager = passpointManager;
+ }
+
// Values used for indexing SystemStateEntries
private static final int SCREEN_ON = 1;
private static final int SCREEN_OFF = 0;
@@ -1022,6 +1069,126 @@
}
}
+ /**
+ * Increment number of times Passpoint provider being installed.
+ */
+ public void incrementNumPasspointProviderInstallation() {
+ synchronized (mLock) {
+ mWifiLogProto.numPasspointProviderInstallation++;
+ }
+ }
+
+ /**
+ * Increment number of times Passpoint provider is installed successfully.
+ */
+ public void incrementNumPasspointProviderInstallSuccess() {
+ synchronized (mLock) {
+ mWifiLogProto.numPasspointProviderInstallSuccess++;
+ }
+ }
+
+ /**
+ * Increment number of times Passpoint provider being uninstalled.
+ */
+ public void incrementNumPasspointProviderUninstallation() {
+ synchronized (mLock) {
+ mWifiLogProto.numPasspointProviderUninstallation++;
+ }
+ }
+
+ /**
+ * Increment number of times Passpoint provider is uninstalled successfully.
+ */
+ public void incrementNumPasspointProviderUninstallSuccess() {
+ synchronized (mLock) {
+ mWifiLogProto.numPasspointProviderUninstallSuccess++;
+ }
+ }
+
+ /**
+ * Increment N-Way network selection decision histograms:
+ * Counts the size of various sets of scanDetails within a scan, and increment the occurrence
+ * of that size for the associated histogram. There are ten histograms generated for each
+ * combination of: {SSID, BSSID} *{Total, Saved, Open, Saved_or_Open, Passpoint}
+ * Only performs this count if isFullBand is true, otherwise, increments the partial scan count
+ */
+ public void incrementAvailableNetworksHistograms(List<ScanDetail> scanDetails,
+ boolean isFullBand) {
+ synchronized (mLock) {
+ if (mWifiConfigManager == null || mWifiNetworkSelector == null
+ || mPasspointManager == null) {
+ return;
+ }
+ if (!isFullBand) {
+ mWifiLogProto.partialAllSingleScanListenerResults++;
+ return;
+ }
+ Set<ScanResultMatchInfo> ssids = new HashSet<ScanResultMatchInfo>();
+ int bssids = 0;
+ Set<ScanResultMatchInfo> openSsids = new HashSet<ScanResultMatchInfo>();
+ int openBssids = 0;
+ Set<ScanResultMatchInfo> savedSsids = new HashSet<ScanResultMatchInfo>();
+ int savedBssids = 0;
+ // openOrSavedSsids calculated from union of savedSsids & openSsids
+ int openOrSavedBssids = 0;
+ Set<PasspointProvider> savedPasspointProviderProfiles =
+ new HashSet<PasspointProvider>();
+ int savedPasspointProviderBssids = 0;
+ for (ScanDetail scanDetail : scanDetails) {
+ NetworkDetail networkDetail = scanDetail.getNetworkDetail();
+ ScanResult scanResult = scanDetail.getScanResult();
+ if (mWifiNetworkSelector.isSignalTooWeak(scanResult)) {
+ continue;
+ }
+ ScanResultMatchInfo matchInfo = ScanResultMatchInfo.fromScanResult(scanResult);
+ Pair<PasspointProvider, PasspointMatch> providerMatch = null;
+ PasspointProvider passpointProvider = null;
+ if (networkDetail.isInterworking()) {
+ providerMatch =
+ mPasspointManager.matchProvider(scanResult);
+ passpointProvider = providerMatch != null ? providerMatch.first : null;
+ }
+ ssids.add(matchInfo);
+ bssids++;
+ boolean isOpen = matchInfo.networkType == ScanResultMatchInfo.NETWORK_TYPE_OPEN;
+ WifiConfiguration config =
+ mWifiConfigManager.getConfiguredNetworkForScanDetail(scanDetail);
+ boolean isSaved = (config != null) && !config.isEphemeral()
+ && !config.isPasspoint();
+ boolean isSavedPasspoint = passpointProvider != null;
+ if (isOpen) {
+ openSsids.add(matchInfo);
+ openBssids++;
+ }
+ if (isSaved) {
+ savedSsids.add(matchInfo);
+ savedBssids++;
+ }
+ if (isOpen || isSaved) {
+ openOrSavedBssids++;
+ // Calculate openOrSavedSsids union later
+ }
+ if (isSavedPasspoint) {
+ savedPasspointProviderProfiles.add(passpointProvider);
+ savedPasspointProviderBssids++;
+ }
+ }
+ mWifiLogProto.fullBandAllSingleScanListenerResults++;
+ incrementTotalScanSsids(mTotalSsidsInScanHistogram, ssids.size());
+ incrementTotalScanResults(mTotalBssidsInScanHistogram, bssids);
+ incrementSsid(mAvailableOpenSsidsInScanHistogram, openSsids.size());
+ incrementBssid(mAvailableOpenBssidsInScanHistogram, openBssids);
+ incrementSsid(mAvailableSavedSsidsInScanHistogram, savedSsids.size());
+ incrementBssid(mAvailableSavedBssidsInScanHistogram, savedBssids);
+ openSsids.addAll(savedSsids); // openSsids = Union(openSsids, savedSsids)
+ incrementSsid(mAvailableOpenOrSavedSsidsInScanHistogram, openSsids.size());
+ incrementBssid(mAvailableOpenOrSavedBssidsInScanHistogram, openOrSavedBssids);
+ incrementSsid(mAvailableSavedPasspointProviderProfilesInScanHistogram,
+ savedPasspointProviderProfiles.size());
+ incrementBssid(mAvailableSavedPasspointProviderBssidsInScanHistogram,
+ savedPasspointProviderBssids);
+ }
+ }
public static final String PROTO_DUMP_ARG = "wifiMetricsProto";
public static final String CLEAN_DUMP_ARG = "clean";
@@ -1224,11 +1391,49 @@
for (StaEvent event : mStaEventList) {
pw.println(staEventToString(event));
}
+
+ pw.println("mWifiLogProto.numPasspointProviders="
+ + mWifiLogProto.numPasspointProviders);
+ pw.println("mWifiLogProto.numPasspointProviderInstallation="
+ + mWifiLogProto.numPasspointProviderInstallation);
+ pw.println("mWifiLogProto.numPasspointProviderInstallSuccess="
+ + mWifiLogProto.numPasspointProviderInstallSuccess);
+ pw.println("mWifiLogProto.numPasspointProviderUninstallation="
+ + mWifiLogProto.numPasspointProviderUninstallation);
+ pw.println("mWifiLogProto.numPasspointProviderUninstallSuccess="
+ + mWifiLogProto.numPasspointProviderUninstallSuccess);
+ pw.println("mWifiLogProto.numPasspointProvidersSuccessfullyConnected="
+ + mWifiLogProto.numPasspointProvidersSuccessfullyConnected);
+ pw.println("mTotalSsidsInScanHistogram:"
+ + mTotalSsidsInScanHistogram.toString());
+ pw.println("mTotalBssidsInScanHistogram:"
+ + mTotalBssidsInScanHistogram.toString());
+ pw.println("mAvailableOpenSsidsInScanHistogram:"
+ + mAvailableOpenSsidsInScanHistogram.toString());
+ pw.println("mAvailableOpenBssidsInScanHistogram:"
+ + mAvailableOpenBssidsInScanHistogram.toString());
+ pw.println("mAvailableSavedSsidsInScanHistogram:"
+ + mAvailableSavedSsidsInScanHistogram.toString());
+ pw.println("mAvailableSavedBssidsInScanHistogram:"
+ + mAvailableSavedBssidsInScanHistogram.toString());
+ pw.println("mAvailableOpenOrSavedSsidsInScanHistogram:"
+ + mAvailableOpenOrSavedSsidsInScanHistogram.toString());
+ pw.println("mAvailableOpenOrSavedBssidsInScanHistogram:"
+ + mAvailableOpenOrSavedBssidsInScanHistogram.toString());
+ pw.println("mAvailableSavedPasspointProviderProfilesInScanHistogram:"
+ + mAvailableSavedPasspointProviderProfilesInScanHistogram.toString());
+ pw.println("mAvailableSavedPasspointProviderBssidsInScanHistogram:"
+ + mAvailableSavedPasspointProviderBssidsInScanHistogram.toString());
+ pw.println("mWifiLogProto.partialAllSingleScanListenerResults="
+ + mWifiLogProto.partialAllSingleScanListenerResults);
+ pw.println("mWifiLogProto.fullBandAllSingleScanListenerResults="
+ + mWifiLogProto.fullBandAllSingleScanListenerResults);
+ pw.println("mWifiAwareMetrics:");
+ mWifiAwareMetrics.dump(fd, pw, args);
}
}
}
-
/**
* Update various counts of saved network types
* @param networks List of WifiConfigurations representing all saved networks, must not be null
@@ -1267,6 +1472,20 @@
}
/**
+ * Update metrics for saved Passpoint profiles.
+ *
+ * @param numSavedProfiles The number of saved Passpoint profiles
+ * @param numConnectedProfiles The number of saved Passpoint profiles that have ever resulted
+ * in a successful network connection
+ */
+ public void updateSavedPasspointProfiles(int numSavedProfiles, int numConnectedProfiles) {
+ synchronized (mLock) {
+ mWifiLogProto.numPasspointProviders = numSavedProfiles;
+ mWifiLogProto.numPasspointProvidersSuccessfullyConnected = numConnectedProfiles;
+ }
+ }
+
+ /**
* append the separate ConnectionEvent, SystemStateEntry and ScanReturnCode collections to their
* respective lists within mWifiLogProto
*
@@ -1386,11 +1605,49 @@
mWifiLogProto.softApReturnCode[sapCode].count =
mSoftApManagerReturnCodeCounts.valueAt(sapCode);
}
-
+ mWifiLogProto.totalSsidsInScanHistogram =
+ makeNumConnectableNetworksBucketArray(mTotalSsidsInScanHistogram);
+ mWifiLogProto.totalBssidsInScanHistogram =
+ makeNumConnectableNetworksBucketArray(mTotalBssidsInScanHistogram);
+ mWifiLogProto.availableOpenSsidsInScanHistogram =
+ makeNumConnectableNetworksBucketArray(mAvailableOpenSsidsInScanHistogram);
+ mWifiLogProto.availableOpenBssidsInScanHistogram =
+ makeNumConnectableNetworksBucketArray(mAvailableOpenBssidsInScanHistogram);
+ mWifiLogProto.availableSavedSsidsInScanHistogram =
+ makeNumConnectableNetworksBucketArray(mAvailableSavedSsidsInScanHistogram);
+ mWifiLogProto.availableSavedBssidsInScanHistogram =
+ makeNumConnectableNetworksBucketArray(mAvailableSavedBssidsInScanHistogram);
+ mWifiLogProto.availableOpenOrSavedSsidsInScanHistogram =
+ makeNumConnectableNetworksBucketArray(
+ mAvailableOpenOrSavedSsidsInScanHistogram);
+ mWifiLogProto.availableOpenOrSavedBssidsInScanHistogram =
+ makeNumConnectableNetworksBucketArray(
+ mAvailableOpenOrSavedBssidsInScanHistogram);
+ mWifiLogProto.availableSavedPasspointProviderProfilesInScanHistogram =
+ makeNumConnectableNetworksBucketArray(
+ mAvailableSavedPasspointProviderProfilesInScanHistogram);
+ mWifiLogProto.availableSavedPasspointProviderBssidsInScanHistogram =
+ makeNumConnectableNetworksBucketArray(
+ mAvailableSavedPasspointProviderBssidsInScanHistogram);
mWifiLogProto.staEventList = mStaEventList.toArray(mWifiLogProto.staEventList);
+ mWifiLogProto.wifiAwareLog = mWifiAwareMetrics.consolidateProto();
}
}
+ private WifiMetricsProto.NumConnectableNetworksBucket[] makeNumConnectableNetworksBucketArray(
+ SparseIntArray sia) {
+ WifiMetricsProto.NumConnectableNetworksBucket[] array =
+ new WifiMetricsProto.NumConnectableNetworksBucket[sia.size()];
+ for (int i = 0; i < sia.size(); i++) {
+ WifiMetricsProto.NumConnectableNetworksBucket keyVal =
+ new WifiMetricsProto.NumConnectableNetworksBucket();
+ keyVal.numConnectableNetworks = sia.keyAt(i);
+ keyVal.count = sia.valueAt(i);
+ array[i] = keyVal;
+ }
+ return array;
+ }
+
/**
* Clear all WifiMetrics, except for currentConnectionEvent.
*/
@@ -1411,6 +1668,17 @@
mScanResultRssiTimestampMillis = -1;
mSoftApManagerReturnCodeCounts.clear();
mStaEventList.clear();
+ mWifiAwareMetrics.clear();
+ mTotalSsidsInScanHistogram.clear();
+ mTotalBssidsInScanHistogram.clear();
+ mAvailableOpenSsidsInScanHistogram.clear();
+ mAvailableOpenBssidsInScanHistogram.clear();
+ mAvailableSavedSsidsInScanHistogram.clear();
+ mAvailableSavedBssidsInScanHistogram.clear();
+ mAvailableOpenOrSavedSsidsInScanHistogram.clear();
+ mAvailableOpenOrSavedBssidsInScanHistogram.clear();
+ mAvailableSavedPasspointProviderProfilesInScanHistogram.clear();
+ mAvailableSavedPasspointProviderBssidsInScanHistogram.clear();
}
}
@@ -1587,6 +1855,10 @@
return mHandler;
}
+ public WifiAwareMetrics getWifiAwareMetrics() {
+ return mWifiAwareMetrics;
+ }
+
// Rather than generate a StaEvent for each SUPPLICANT_STATE_CHANGE, cache these in a bitmask
// and attach it to the next event which is generated.
private int mSupplicantStateChangeBitmask = 0;
@@ -1797,4 +2069,20 @@
}
return value;
}
+ private void incrementSsid(SparseIntArray sia, int element) {
+ increment(sia, Math.min(element, MAX_CONNECTABLE_SSID_NETWORK_BUCKET));
+ }
+ private void incrementBssid(SparseIntArray sia, int element) {
+ increment(sia, Math.min(element, MAX_CONNECTABLE_BSSID_NETWORK_BUCKET));
+ }
+ private void incrementTotalScanResults(SparseIntArray sia, int element) {
+ increment(sia, Math.min(element, MAX_TOTAL_SCAN_RESULTS_BUCKET));
+ }
+ private void incrementTotalScanSsids(SparseIntArray sia, int element) {
+ increment(sia, Math.min(element, MAX_TOTAL_SCAN_RESULT_SSIDS_BUCKET));
+ }
+ private void increment(SparseIntArray sia, int element) {
+ int count = sia.get(element);
+ sia.put(element, count + 1);
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index 90f6ac1..973b659 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -29,6 +29,7 @@
import android.net.wifi.WifiWakeReasonAndCounts;
import android.os.SystemClock;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import com.android.internal.annotations.Immutable;
@@ -90,6 +91,9 @@
/********************************************************
* Native Initialization/Deinitialization
********************************************************/
+ public static final int SETUP_SUCCESS = 0;
+ public static final int SETUP_FAILURE_HAL = 1;
+ public static final int SETUP_FAILURE_WIFICOND = 2;
/**
* Setup wifi native for Client mode operations.
@@ -98,15 +102,19 @@
* 2. Setup Wificond to operate in client mode and retrieve the handle to use for client
* operations.
*
- * @return An IClientInterface as wificond client interface binder handler.
- * Returns null on failure.
+ * @return Pair of <Integer, IClientInterface> to indicate the status and the associated wificond
+ * client interface binder handler (will be null on failure).
*/
- public IClientInterface setupForClientMode() {
+ public Pair<Integer, IClientInterface> setupForClientMode() {
if (!startHalIfNecessary(true)) {
Log.e(mTAG, "Failed to start HAL for client mode");
- return null;
+ return Pair.create(SETUP_FAILURE_HAL, null);
}
- return mWificondControl.setupDriverForClientMode();
+ IClientInterface iClientInterface = mWificondControl.setupDriverForClientMode();
+ if (iClientInterface == null) {
+ return Pair.create(SETUP_FAILURE_WIFICOND, null);
+ }
+ return Pair.create(SETUP_SUCCESS, iClientInterface);
}
/**
@@ -115,33 +123,33 @@
* 1. Starts the Wifi HAL and configures it in AP mode.
* 2. Setup Wificond to operate in AP mode and retrieve the handle to use for ap operations.
*
- * @return An IApInterface as wificond Ap interface binder handler.
- * Returns null on failure.
+ * @return Pair of <Integer, IApInterface> to indicate the status and the associated wificond
+ * AP interface binder handler (will be null on failure).
*/
- public IApInterface setupForSoftApMode() {
+ public Pair<Integer, IApInterface> setupForSoftApMode() {
if (!startHalIfNecessary(false)) {
Log.e(mTAG, "Failed to start HAL for AP mode");
- return null;
+ return Pair.create(SETUP_FAILURE_HAL, null);
}
- return mWificondControl.setupDriverForSoftApMode();
+ IApInterface iApInterface = mWificondControl.setupDriverForSoftApMode();
+ if (iApInterface == null) {
+ return Pair.create(SETUP_FAILURE_WIFICOND, null);
+ }
+ return Pair.create(SETUP_SUCCESS, iApInterface);
}
/**
* Teardown all mode configurations in wifi native.
*
- * 1. Tears down all the interfaces from Wificond.
- * 2. Stops the Wifi HAL.
- *
- * @return Returns true on success.
+ * 1. Stops the Wifi HAL.
+ * 2. Tears down all the interfaces from Wificond.
*/
- public boolean tearDown() {
+ public void tearDown() {
+ stopHalIfNecessary();
if (!mWificondControl.tearDownInterfaces()) {
// TODO(b/34859006): Handle failures.
Log.e(mTAG, "Failed to teardown interfaces from Wificond");
- return false;
}
- stopHalIfNecessary();
- return true;
}
/********************************************************
@@ -1675,6 +1683,24 @@
return mWifiVendorHal.configureRoaming(new RoamingConfig());
}
+ /**
+ * Tx power level scenarios that can be selected.
+ */
+ public static final int TX_POWER_SCENARIO_NORMAL = 0;
+ public static final int TX_POWER_SCENARIO_VOICE_CALL = 1;
+
+ /**
+ * Select one of the pre-configured TX power level scenarios or reset it back to normal.
+ * Primarily used for meeting SAR requirements during voice calls.
+ *
+ * @param scenario Should be one {@link #TX_POWER_SCENARIO_NORMAL} or
+ * {@link #TX_POWER_SCENARIO_VOICE_CALL}.
+ * @return true for success; false for failure or if the HAL version does not support this API.
+ */
+ public boolean selectTxPowerScenario(int scenario) {
+ return mWifiVendorHal.selectTxPowerScenario(scenario);
+ }
+
/********************************************************
* JNI operations
********************************************************/
diff --git a/service/java/com/android/server/wifi/WifiNetworkSelector.java b/service/java/com/android/server/wifi/WifiNetworkSelector.java
index d24b5cc..89068a8 100644
--- a/service/java/com/android/server/wifi/WifiNetworkSelector.java
+++ b/service/java/com/android/server/wifi/WifiNetworkSelector.java
@@ -25,11 +25,11 @@
import android.net.wifi.WifiInfo;
import android.text.TextUtils;
import android.util.LocalLog;
-import android.util.Log;
import android.util.Pair;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.util.ScanResultUtil;
import java.util.ArrayList;
import java.util.HashSet;
@@ -56,10 +56,13 @@
// WifiConfiguration (if any).
private volatile List<Pair<ScanDetail, WifiConfiguration>> mConnectableNetworks =
new ArrayList<>();
+ private List<ScanDetail> mFilteredNetworks = new ArrayList<>();
private final int mThresholdQualifiedRssi24;
private final int mThresholdQualifiedRssi5;
private final int mThresholdMinimumRssi24;
private final int mThresholdMinimumRssi5;
+ private final int mStayOnNetworkMinimumTxRate;
+ private final int mStayOnNetworkMinimumRxRate;
private final boolean mEnableAutoJoinWhenAssociated;
/**
@@ -145,6 +148,18 @@
+ " , ID: " + network.networkId);
}
+ int currentRssi = wifiInfo.getRssi();
+ boolean hasQualifiedRssi =
+ (wifiInfo.is24GHz() && (currentRssi > mThresholdQualifiedRssi24))
+ || (wifiInfo.is5GHz() && (currentRssi > mThresholdQualifiedRssi5));
+ // getTxSuccessRate() and getRxSuccessRate() returns the packet rate in per 5 seconds unit.
+ boolean hasActiveStream = (wifiInfo.getTxSuccessRatePps() > mStayOnNetworkMinimumTxRate)
+ || (wifiInfo.getRxSuccessRatePps() > mStayOnNetworkMinimumRxRate);
+ if (hasQualifiedRssi && hasActiveStream) {
+ localLog("Stay on current network because of good RSSI and ongoing traffic");
+ return true;
+ }
+
// Ephemeral network is not qualified.
if (network.ephemeral) {
localLog("Current network is an ephemeral one.");
@@ -157,28 +172,15 @@
return false;
}
- int currentRssi = wifiInfo.getRssi();
if (wifiInfo.is24GHz()) {
// 2.4GHz networks is not qualified whenever 5GHz is available
if (is5GHzNetworkAvailable(scanDetails)) {
localLog("Current network is 2.4GHz. 5GHz networks available.");
return false;
}
- // When 5GHz is not available, we go through normal 2.4GHz qualification
- if (currentRssi < mThresholdQualifiedRssi24) {
- localLog("Current network band=2.4GHz, RSSI["
- + currentRssi + "]-acceptable but not qualified.");
- return false;
- }
- } else if (wifiInfo.is5GHz()) {
- // Must be 5GHz, so we always apply qualification checks
- if (currentRssi < mThresholdQualifiedRssi5) {
- localLog("Current network band=5GHz, RSSI["
- + currentRssi + "]-acceptable but not qualified.");
- return false;
- }
- } else {
- Log.e(TAG, "We're on a wifi network that's neither 2.4 or 5GHz... aliens!");
+ }
+ if (!hasQualifiedRssi) {
+ localLog("Current network RSSI[" + currentRssi + "]-acceptable but not qualified.");
return false;
}
@@ -257,6 +259,14 @@
return (network.SSID + ":" + network.networkId);
}
+ /**
+ * Compares ScanResult level against the minimum threshold for its band, returns true if lower
+ */
+ public boolean isSignalTooWeak(ScanResult scanResult) {
+ return ((scanResult.is24GHz() && scanResult.level < mThresholdMinimumRssi24)
+ || (scanResult.is5GHz() && scanResult.level < mThresholdMinimumRssi5));
+ }
+
private List<ScanDetail> filterScanResults(List<ScanDetail> scanDetails,
HashSet<String> bssidBlacklist, boolean isConnected, String currentBssid) {
ArrayList<NetworkKey> unscoredNetworks = new ArrayList<NetworkKey>();
@@ -287,10 +297,7 @@
}
// Skip network with too weak signals.
- if ((scanResult.is24GHz() && scanResult.level
- < mThresholdMinimumRssi24)
- || (scanResult.is5GHz() && scanResult.level
- < mThresholdMinimumRssi5)) {
+ if (isSignalTooWeak(scanResult)) {
lowRssi.append(scanId).append("(")
.append(scanResult.is24GHz() ? "2.4GHz" : "5GHz")
.append(")").append(scanResult.level).append(" / ");
@@ -327,12 +334,39 @@
}
/**
+ * This returns a list of ScanDetails that were filtered in the process of network selection.
+ * The list is further filtered for only open unsaved networks.
+ *
+ * @return the list of ScanDetails for open unsaved networks that do not have invalid SSIDS,
+ * blacklisted BSSIDS, or low signal strength. This will return an empty list when there are
+ * no open unsaved networks, or when network selection has not been run.
+ */
+ public List<ScanDetail> getFilteredScanDetailsForOpenUnsavedNetworks() {
+ List<ScanDetail> openUnsavedNetworks = new ArrayList<>();
+ for (ScanDetail scanDetail : mFilteredNetworks) {
+ ScanResult scanResult = scanDetail.getScanResult();
+
+ if (!ScanResultUtil.isScanResultForOpenNetwork(scanResult)) {
+ continue;
+ }
+
+ // Skip saved networks
+ if (mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail) != null) {
+ continue;
+ }
+
+ openUnsavedNetworks.add(scanDetail);
+ }
+ return openUnsavedNetworks;
+ }
+
+ /**
* @return the list of ScanDetails scored as potential candidates by the last run of
* selectNetwork, this will be empty if Network selector determined no selection was
* needed on last run. This includes scan details of sufficient signal strength, and
* had an associated WifiConfiguration.
*/
- public List<Pair<ScanDetail, WifiConfiguration>> getFilteredScanDetails() {
+ public List<Pair<ScanDetail, WifiConfiguration>> getConnectableScanDetails() {
return mConnectableNetworks;
}
@@ -450,6 +484,7 @@
public WifiConfiguration selectNetwork(List<ScanDetail> scanDetails,
HashSet<String> bssidBlacklist, WifiInfo wifiInfo,
boolean connected, boolean disconnected, boolean untrustedNetworkAllowed) {
+ mFilteredNetworks.clear();
mConnectableNetworks.clear();
if (scanDetails.size() == 0) {
localLog("Empty connectivity scan result");
@@ -476,9 +511,9 @@
}
// Filter out unwanted networks.
- List<ScanDetail> filteredScanDetails = filterScanResults(scanDetails, bssidBlacklist,
+ mFilteredNetworks = filterScanResults(scanDetails, bssidBlacklist,
connected, currentBssid);
- if (filteredScanDetails.size() == 0) {
+ if (mFilteredNetworks.size() == 0) {
return null;
}
@@ -488,9 +523,9 @@
for (NetworkEvaluator registeredEvaluator : mEvaluators) {
if (registeredEvaluator != null) {
localLog("About to run " + registeredEvaluator.getName() + " :");
- selectedNetwork = registeredEvaluator.evaluateNetworks(filteredScanDetails,
- currentNetwork, currentBssid, connected,
- untrustedNetworkAllowed, mConnectableNetworks);
+ selectedNetwork = registeredEvaluator.evaluateNetworks(
+ new ArrayList<>(mFilteredNetworks), currentNetwork, currentBssid, connected,
+ untrustedNetworkAllowed, mConnectableNetworks);
if (selectedNetwork != null) {
localLog(registeredEvaluator.getName() + " selects "
+ WifiNetworkSelector.toNetworkString(selectedNetwork) + " : "
@@ -549,5 +584,9 @@
R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz);
mEnableAutoJoinWhenAssociated = context.getResources().getBoolean(
R.bool.config_wifi_framework_enable_associated_network_selection);
+ mStayOnNetworkMinimumTxRate = context.getResources().getInteger(
+ R.integer.config_wifi_framework_min_tx_rate_for_staying_on_network);
+ mStayOnNetworkMinimumRxRate = context.getResources().getInteger(
+ R.integer.config_wifi_framework_min_rx_rate_for_staying_on_network);
}
}
diff --git a/service/java/com/android/server/wifi/WifiNotificationController.java b/service/java/com/android/server/wifi/WifiNotificationController.java
index c8e5e90..797fd43 100644
--- a/service/java/com/android/server/wifi/WifiNotificationController.java
+++ b/service/java/com/android/server/wifi/WifiNotificationController.java
@@ -16,18 +16,14 @@
package com.android.server.wifi;
+import android.annotation.NonNull;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.TaskStackBuilder;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.database.ContentObserver;
-import android.net.NetworkInfo;
-import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
-import android.net.wifi.WifiScanner;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -57,12 +53,11 @@
*/
private final long NOTIFICATION_REPEAT_DELAY_MS;
+ /** Whether the user has set the setting to show the 'available networks' notification. */
+ private boolean mSettingEnabled;
+
/**
- * Whether the user has set the setting to show the 'available networks' notification.
- */
- private boolean mNotificationEnabled;
- /**
- * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
+ * Observes the user setting to keep {@link #mSettingEnabled} in sync.
*/
private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
@@ -82,93 +77,21 @@
* notification is not showing.
*/
private boolean mNotificationShown;
- /**
- * The number of continuous scans that must occur before consider the
- * supplicant in a scanning state. This allows supplicant to associate with
- * remembered networks that are in the scan results.
- */
- private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
- /**
- * The number of scans since the last network state change. When this
- * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
- * supplicant to actually be scanning. When the network state changes to
- * something other than scanning, we reset this to 0.
- */
- private int mNumScansSinceNetworkStateChange;
+ /** Whether the screen is on or not. */
+ private boolean mScreenOn;
private final Context mContext;
- private NetworkInfo mNetworkInfo;
- private NetworkInfo.DetailedState mDetailedState;
- private volatile int mWifiState;
private FrameworkFacade mFrameworkFacade;
- private WifiInjector mWifiInjector;
- private WifiScanner mWifiScanner;
WifiNotificationController(Context context,
Looper looper,
FrameworkFacade framework,
- Notification.Builder builder,
- WifiInjector wifiInjector) {
+ Notification.Builder builder) {
mContext = context;
mFrameworkFacade = framework;
mNotificationBuilder = builder;
- mWifiInjector = wifiInjector;
- mWifiState = WifiManager.WIFI_STATE_UNKNOWN;
- mDetailedState = NetworkInfo.DetailedState.IDLE;
- IntentFilter filter = new IntentFilter();
- filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
- filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
-
- mContext.registerReceiver(
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction()
- .equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
- mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
- WifiManager.WIFI_STATE_UNKNOWN);
- resetNotification();
- } else if (intent.getAction().equals(
- WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
- mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
- WifiManager.EXTRA_NETWORK_INFO);
- NetworkInfo.DetailedState detailedState =
- mNetworkInfo.getDetailedState();
- if (detailedState != NetworkInfo.DetailedState.SCANNING
- && detailedState != mDetailedState) {
- mDetailedState = detailedState;
- // reset & clear notification on a network connect & disconnect
- switch(mDetailedState) {
- case CONNECTED:
- case DISCONNECTED:
- case CAPTIVE_PORTAL_CHECK:
- resetNotification();
- break;
-
- case IDLE:
- case SCANNING:
- case CONNECTING:
- case AUTHENTICATING:
- case OBTAINING_IPADDR:
- case SUSPENDED:
- case FAILED:
- case BLOCKED:
- case VERIFYING_POOR_LINK:
- break;
- }
- }
- } else if (intent.getAction().equals(
- WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
- if (mWifiScanner == null) {
- mWifiScanner = mWifiInjector.getWifiScanner();
- }
- checkAndSetNotification(mNetworkInfo,
- mWifiScanner.getSingleScanResults());
- }
- }
- }, filter);
+ mScreenOn = false;
// Setting is in seconds
NOTIFICATION_REPEAT_DELAY_MS = mFrameworkFacade.getIntegerSetting(context,
@@ -178,66 +101,53 @@
mNotificationEnabledSettingObserver.register();
}
- private synchronized void checkAndSetNotification(NetworkInfo networkInfo,
- List<ScanResult> scanResults) {
-
- // TODO: unregister broadcast so we do not have to check here
- // If we shouldn't place a notification on available networks, then
- // don't bother doing any of the following
- if (!mNotificationEnabled) return;
- if (mWifiState != WifiManager.WIFI_STATE_ENABLED) return;
- if (UserManager.get(mContext)
- .hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT)) {
- return;
+ /**
+ * Clears the pending notification. This is called by {@link WifiConnectivityManager} on stop.
+ *
+ * @param resetRepeatDelay resets the time delay for repeated notification if true.
+ */
+ public void clearPendingNotification(boolean resetRepeatDelay) {
+ if (resetRepeatDelay) {
+ mNotificationRepeatTime = 0;
}
-
- NetworkInfo.State state = NetworkInfo.State.DISCONNECTED;
- if (networkInfo != null)
- state = networkInfo.getState();
-
- if ((state == NetworkInfo.State.DISCONNECTED)
- || (state == NetworkInfo.State.UNKNOWN)) {
- if (scanResults != null) {
- int numOpenNetworks = 0;
- for (int i = scanResults.size() - 1; i >= 0; i--) {
- ScanResult scanResult = scanResults.get(i);
-
- //A capability of [ESS] represents an open access point
- //that is available for an STA to connect
- if (scanResult.capabilities != null &&
- scanResult.capabilities.equals("[ESS]")) {
- numOpenNetworks++;
- }
- }
-
- if (numOpenNetworks > 0) {
- if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
- /*
- * We've scanned continuously at least
- * NUM_SCANS_BEFORE_NOTIFICATION times. The user
- * probably does not have a remembered network in range,
- * since otherwise supplicant would have tried to
- * associate and thus resetting this counter.
- */
- setNotificationVisible(true, numOpenNetworks, false, 0);
- }
- return;
- }
- }
- }
-
- // No open networks in range, remove the notification
setNotificationVisible(false, 0, false, 0);
}
+ private boolean isControllerEnabled() {
+ return mSettingEnabled && !UserManager.get(mContext)
+ .hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT);
+ }
+
/**
- * Clears variables related to tracking whether a notification has been
- * shown recently and clears the current notification.
+ * If there are open networks, attempt to post an open network notification.
+ *
+ * @param availableNetworks Available networks from
+ * {@link WifiNetworkSelector.NetworkEvaluator#getFilteredScanDetailsForOpenUnsavedNetworks()}.
*/
- private synchronized void resetNotification() {
- mNotificationRepeatTime = 0;
- mNumScansSinceNetworkStateChange = 0;
- setNotificationVisible(false, 0, false, 0);
+ public void handleScanResults(@NonNull List<ScanDetail> availableNetworks) {
+ if (!isControllerEnabled()) {
+ clearPendingNotification(true /* resetRepeatDelay */);
+ return;
+ }
+ if (availableNetworks.isEmpty()) {
+ clearPendingNotification(false /* resetRepeatDelay */);
+ return;
+ }
+
+ // Do not show or update the notification if screen is off. We want to avoid a race that
+ // could occur between a user picking a network in settings and a network candidate picked
+ // through network selection, which will happen because screen on triggers a new
+ // connectivity scan.
+ if (mNotificationShown || !mScreenOn) {
+ return;
+ }
+
+ setNotificationVisible(true, availableNetworks.size(), false, 0);
+ }
+
+ /** Handles screen state changes. */
+ public void handleScreenStateChanged(boolean screenOn) {
+ mScreenOn = screenOn;
}
/**
@@ -308,34 +218,30 @@
mNotificationShown = visible;
}
- void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("mNotificationEnabled " + mNotificationEnabled);
+ /** Dump ONA controller state. */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("WifiNotificationController: ");
+ pw.println("mSettingEnabled " + mSettingEnabled);
pw.println("mNotificationRepeatTime " + mNotificationRepeatTime);
pw.println("mNotificationShown " + mNotificationShown);
- pw.println("mNumScansSinceNetworkStateChange " + mNumScansSinceNetworkStateChange);
}
private class NotificationEnabledSettingObserver extends ContentObserver {
- public NotificationEnabledSettingObserver(Handler handler) {
+ NotificationEnabledSettingObserver(Handler handler) {
super(handler);
}
public void register() {
mFrameworkFacade.registerContentObserver(mContext, Settings.Global.getUriFor(
Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
- synchronized (WifiNotificationController.this) {
- mNotificationEnabled = getValue();
- }
+ mSettingEnabled = getValue();
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
-
- synchronized (WifiNotificationController.this) {
- mNotificationEnabled = getValue();
- resetNotification();
- }
+ mSettingEnabled = getValue();
+ clearPendingNotification(true /* resetRepeatDelay */);
}
private boolean getValue() {
diff --git a/service/java/com/android/server/wifi/WifiScoreReport.java b/service/java/com/android/server/wifi/WifiScoreReport.java
index 8bc8bf3..894d57c 100644
--- a/service/java/com/android/server/wifi/WifiScoreReport.java
+++ b/service/java/com/android/server/wifi/WifiScoreReport.java
@@ -18,11 +18,15 @@
import android.content.Context;
import android.net.NetworkAgent;
-import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.util.Log;
-import com.android.internal.R;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.Locale;
/**
* Class used to calculate scores for connected wifi networks and report it to the associated
@@ -31,70 +35,25 @@
public class WifiScoreReport {
private static final String TAG = "WifiScoreReport";
- private static final int STARTING_SCORE = 56;
+ private static final int DUMPSYS_ENTRY_COUNT_LIMIT = 14400; // 12 hours on 3 second poll
- private static final int SCAN_CACHE_VISIBILITY_MS = 12000;
- private static final int HOME_VISIBLE_NETWORK_MAX_COUNT = 6;
- private static final int SCAN_CACHE_COUNT_PENALTY = 2;
- private static final int AGGRESSIVE_HANDOVER_PENALTY = 6;
- private static final int MAX_SUCCESS_RATE_OF_STUCK_LINK = 3; // proportional to packets per sec
- private static final int MAX_STUCK_LINK_COUNT = 5;
- private static final int MAX_BAD_RSSI_COUNT = 7;
- private static final int BAD_RSSI_COUNT_PENALTY = 2;
- private static final int MAX_LOW_RSSI_COUNT = 1;
- private static final double MIN_TX_FAILURE_RATE_FOR_WORKING_LINK = 0.3;
- private static final int MIN_SUSTAINED_LINK_STUCK_COUNT = 1;
- private static final int LINK_STUCK_PENALTY = 2;
- private static final int BAD_LINKSPEED_PENALTY = 4;
- private static final int GOOD_LINKSPEED_BONUS = 4;
-
- // Device configs. The values are examples.
- private final int mThresholdMinimumRssi5; // -82
- private final int mThresholdQualifiedRssi5; // -70
- private final int mThresholdSaturatedRssi5; // -57
- private final int mThresholdMinimumRssi24; // -85
- private final int mThresholdQualifiedRssi24; // -73
- private final int mThresholdSaturatedRssi24; // -60
- private final int mBadLinkSpeed24; // 6 Mbps
- private final int mBadLinkSpeed5; // 12 Mbps
- private final int mGoodLinkSpeed24; // 24 Mbps
- private final int mGoodLinkSpeed5; // 36 Mbps
-
- private final WifiConfigManager mWifiConfigManager;
private boolean mVerboseLoggingEnabled = false;
+ private static final long FIRST_REASONABLE_WALL_CLOCK = 1490000000000L; // mid-December 2016
// Cache of the last score report.
private String mReport;
private boolean mReportValid = false;
- // State set by updateScoringState
- private boolean mMultiBandScanResults;
- private boolean mIsHomeNetwork;
+ private final Clock mClock;
+ private int mSessionNumber = 0;
- WifiScoreReport(Context context, WifiConfigManager wifiConfigManager) {
- // Fetch all the device configs.
- mThresholdMinimumRssi5 = context.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz);
- mThresholdQualifiedRssi5 = context.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz);
- mThresholdSaturatedRssi5 = context.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz);
- mThresholdMinimumRssi24 = context.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz);
- mThresholdQualifiedRssi24 = context.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz);
- mThresholdSaturatedRssi24 = context.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz);
- mBadLinkSpeed24 = context.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_bad_link_speed_24);
- mBadLinkSpeed5 = context.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_bad_link_speed_5);
- mGoodLinkSpeed24 = context.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_good_link_speed_24);
- mGoodLinkSpeed5 = context.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_good_link_speed_5);
+ ConnectedScore mConnectedScore;
+ ConnectedScore mAggressiveConnectedScore;
- mWifiConfigManager = wifiConfigManager;
+ WifiScoreReport(Context context, WifiConfigManager wifiConfigManager, Clock clock) {
+ mClock = clock;
+ mConnectedScore = new LegacyConnectedScore(context, wifiConfigManager, clock);
+ mAggressiveConnectedScore = new AggressiveConnectedScore(context, clock);
}
/**
@@ -111,7 +70,13 @@
*/
public void reset() {
mReport = "";
- mReportValid = false;
+ if (mReportValid) {
+ mSessionNumber++;
+ mReportValid = false;
+ }
+ mConnectedScore.reset();
+ mAggressiveConnectedScore.reset();
+ if (mVerboseLoggingEnabled) Log.d(TAG, "reset");
}
/**
@@ -148,8 +113,19 @@
int aggressiveHandover, WifiMetrics wifiMetrics) {
int score;
- updateScoringState(wifiInfo, aggressiveHandover);
- score = calculateScore(wifiInfo, aggressiveHandover);
+ long millis = mConnectedScore.getMillis();
+
+ mConnectedScore.updateUsingWifiInfo(wifiInfo, millis);
+ mAggressiveConnectedScore.updateUsingWifiInfo(wifiInfo, millis);
+
+ int s0 = mConnectedScore.generateScore();
+ int s1 = mAggressiveConnectedScore.generateScore();
+
+ if (aggressiveHandover == 0) {
+ score = s0;
+ } else {
+ score = s1;
+ }
//sanitize boundaries
if (score > NetworkAgent.WIFI_BASE_SCORE) {
@@ -159,6 +135,8 @@
score = 0;
}
+ logLinkMetrics(wifiInfo, s0, s1);
+
//report score
if (score != wifiInfo.score) {
if (mVerboseLoggingEnabled) {
@@ -170,154 +148,62 @@
}
}
- mReport = String.format(" score=%d", score);
+ mReport = String.format(Locale.US, " score=%d", score);
mReportValid = true;
wifiMetrics.incrementWifiScoreCount(score);
}
/**
- * Updates the state.
+ * Data for dumpsys
+ *
+ * These are stored as csv formatted lines
*/
- private void updateScoringState(WifiInfo wifiInfo, int aggressiveHandover) {
- mMultiBandScanResults = multiBandScanResults(wifiInfo);
- mIsHomeNetwork = isHomeNetwork(wifiInfo);
-
- int rssiThreshBad = mThresholdMinimumRssi24;
- int rssiThreshLow = mThresholdQualifiedRssi24;
-
- if (wifiInfo.is5GHz() && !mMultiBandScanResults) {
- rssiThreshBad = mThresholdMinimumRssi5;
- rssiThreshLow = mThresholdQualifiedRssi5;
- }
-
- int rssi = wifiInfo.getRssi();
- if (aggressiveHandover != 0) {
- rssi -= AGGRESSIVE_HANDOVER_PENALTY * aggressiveHandover;
- }
- if (mIsHomeNetwork) {
- rssi += WifiConfiguration.HOME_NETWORK_RSSI_BOOST;
- }
-
- if ((wifiInfo.txBadRate >= 1)
- && (wifiInfo.txSuccessRate < MAX_SUCCESS_RATE_OF_STUCK_LINK)
- && rssi < rssiThreshLow) {
- // Link is stuck
- if (wifiInfo.linkStuckCount < MAX_STUCK_LINK_COUNT) {
- wifiInfo.linkStuckCount += 1;
- }
- } else if (wifiInfo.txBadRate < MIN_TX_FAILURE_RATE_FOR_WORKING_LINK) {
- if (wifiInfo.linkStuckCount > 0) {
- wifiInfo.linkStuckCount -= 1;
- }
- }
-
- if (rssi < rssiThreshBad) {
- if (wifiInfo.badRssiCount < MAX_BAD_RSSI_COUNT) {
- wifiInfo.badRssiCount += 1;
- }
- } else if (rssi < rssiThreshLow) {
- wifiInfo.lowRssiCount = MAX_LOW_RSSI_COUNT; // Dont increment the lowRssi count above 1
- if (wifiInfo.badRssiCount > 0) {
- // Decrement bad Rssi count
- wifiInfo.badRssiCount -= 1;
- }
- } else {
- wifiInfo.badRssiCount = 0;
- wifiInfo.lowRssiCount = 0;
- }
-
- }
+ private LinkedList<String> mLinkMetricsHistory = new LinkedList<String>();
/**
- * Calculates the score, without all the cruft.
+ * Data logging for dumpsys
*/
- private int calculateScore(WifiInfo wifiInfo, int aggressiveHandover) {
- int score = STARTING_SCORE;
-
- int rssiThreshSaturated = mThresholdSaturatedRssi24;
- int linkspeedThreshBad = mBadLinkSpeed24;
- int linkspeedThreshGood = mGoodLinkSpeed24;
-
- if (wifiInfo.is5GHz()) {
- if (!mMultiBandScanResults) {
- rssiThreshSaturated = mThresholdSaturatedRssi5;
- }
- linkspeedThreshBad = mBadLinkSpeed5;
- linkspeedThreshGood = mGoodLinkSpeed5;
- }
-
- int rssi = wifiInfo.getRssi();
- if (aggressiveHandover != 0) {
- rssi -= AGGRESSIVE_HANDOVER_PENALTY * aggressiveHandover;
- }
- if (mIsHomeNetwork) {
- rssi += WifiConfiguration.HOME_NETWORK_RSSI_BOOST;
- }
-
+ private void logLinkMetrics(WifiInfo wifiInfo, int s0, int s1) {
+ long now = mClock.getWallClockMillis();
+ if (now < FIRST_REASONABLE_WALL_CLOCK) return;
+ double rssi = wifiInfo.getRssi();
+ int freq = wifiInfo.getFrequency();
int linkSpeed = wifiInfo.getLinkSpeed();
-
- if (wifiInfo.linkStuckCount > MIN_SUSTAINED_LINK_STUCK_COUNT) {
- // Once link gets stuck for more than 3 seconds, start reducing the score
- score = score - LINK_STUCK_PENALTY * (wifiInfo.linkStuckCount - 1);
+ double txSuccessRate = wifiInfo.txSuccessRate;
+ double txRetriesRate = wifiInfo.txRetriesRate;
+ double txBadRate = wifiInfo.txBadRate;
+ double rxSuccessRate = wifiInfo.rxSuccessRate;
+ try {
+ String timestamp = new SimpleDateFormat("MM-dd HH:mm:ss.SSS").format(new Date(now));
+ String s = String.format(Locale.US, // Use US to avoid comma/decimal confusion
+ "%s,%d,%.1f,%d,%d,%.2f,%.2f,%.2f,%.2f,%d,%d",
+ timestamp, mSessionNumber, rssi, freq, linkSpeed,
+ txSuccessRate, txRetriesRate, txBadRate, rxSuccessRate,
+ s0, s1);
+ mLinkMetricsHistory.add(s);
+ } catch (Exception e) {
+ Log.e(TAG, "format problem", e);
}
-
- if (linkSpeed < linkspeedThreshBad) {
- score -= BAD_LINKSPEED_PENALTY;
- } else if ((linkSpeed >= linkspeedThreshGood) && (wifiInfo.txSuccessRate > 5)) {
- score += GOOD_LINKSPEED_BONUS; // So as bad rssi alone doesn't kill us
+ while (mLinkMetricsHistory.size() > DUMPSYS_ENTRY_COUNT_LIMIT) {
+ mLinkMetricsHistory.removeFirst();
}
-
- score -= wifiInfo.badRssiCount * BAD_RSSI_COUNT_PENALTY + wifiInfo.lowRssiCount;
-
- if (rssi >= rssiThreshSaturated) score += 5;
-
- if (score > NetworkAgent.WIFI_BASE_SCORE) score = NetworkAgent.WIFI_BASE_SCORE;
- if (score < 0) score = 0;
-
- return score;
}
/**
- * Determines if we can see both 2.4GHz and 5GHz for current config
+ * Tag to be used in dumpsys request
*/
- private boolean multiBandScanResults(WifiInfo wifiInfo) {
- WifiConfiguration currentConfiguration =
- mWifiConfigManager.getConfiguredNetwork(wifiInfo.getNetworkId());
- if (currentConfiguration == null) return false;
- ScanDetailCache scanDetailCache =
- mWifiConfigManager.getScanDetailCacheForNetwork(wifiInfo.getNetworkId());
- if (scanDetailCache == null) return false;
- // Nasty that we change state here...
- currentConfiguration.setVisibility(scanDetailCache.getVisibility(SCAN_CACHE_VISIBILITY_MS));
- if (currentConfiguration.visibility == null) return false;
- if (currentConfiguration.visibility.rssi24 == WifiConfiguration.INVALID_RSSI) return false;
- if (currentConfiguration.visibility.rssi5 == WifiConfiguration.INVALID_RSSI) return false;
- // N.B. this does not do exactly what is claimed!
- if (currentConfiguration.visibility.rssi24
- >= currentConfiguration.visibility.rssi5 - SCAN_CACHE_COUNT_PENALTY) {
- return true;
- }
- return false;
- }
+ public static final String DUMP_ARG = "WifiScoreReport";
/**
- * Decides whether the current network is a "home" network
+ * Dump logged signal strength and traffic measurements.
+ * @param fd unused
+ * @param pw PrintWriter for writing dump to
+ * @param args unused
*/
- private boolean isHomeNetwork(WifiInfo wifiInfo) {
- WifiConfiguration currentConfiguration =
- mWifiConfigManager.getConfiguredNetwork(wifiInfo.getNetworkId());
- if (currentConfiguration == null) return false;
- // This seems like it will only return true for really old routers!
- if (currentConfiguration.allowedKeyManagement.cardinality() != 1) return false;
- if (!currentConfiguration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
- return false;
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("time,session,rssi,freq,linkspeed,tx_good,tx_retry,tx_bad,rx,s0,s1");
+ for (String line : mLinkMetricsHistory) {
+ pw.println(line);
}
- ScanDetailCache scanDetailCache =
- mWifiConfigManager.getScanDetailCacheForNetwork(wifiInfo.getNetworkId());
- if (scanDetailCache == null) return false;
- if (scanDetailCache.size() <= HOME_VISIBLE_NETWORK_MAX_COUNT) {
- return true;
- }
- return false;
}
}
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index 5c9548a..d6faf9b 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -73,6 +73,7 @@
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.LocalOnlyHotspotCallback;
import android.net.wifi.WifiScanner;
+import android.net.wifi.hotspot2.OsuProvider;
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.os.AsyncTask;
import android.os.BatteryStats;
@@ -88,6 +89,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
@@ -167,8 +169,6 @@
// Debug counter tracking scan requests sent by WifiManager
private int scanRequestCounter = 0;
- /* Tracks the open wi-fi network notification */
- private WifiNotificationController mNotificationController;
/* Polls traffic stats and notifies clients */
private WifiTrafficPoller mTrafficPoller;
/* Tracks the persisted states for wi-fi & airplane mode */
@@ -260,55 +260,81 @@
break;
}
case WifiManager.CONNECT_NETWORK: {
- WifiConfiguration config = (WifiConfiguration) msg.obj;
- int networkId = msg.arg1;
- Slog.d("WiFiServiceImpl ", "CONNECT "
- + " nid=" + Integer.toString(networkId)
- + " uid=" + msg.sendingUid
- + " name="
- + mContext.getPackageManager().getNameForUid(msg.sendingUid));
- if (config != null && isValid(config)) {
- if (DBG) Slog.d(TAG, "Connect with config " + config);
- /* Command is forwarded to state machine */
- mWifiStateMachine.sendMessage(Message.obtain(msg));
- } else if (config == null
- && networkId != WifiConfiguration.INVALID_NETWORK_ID) {
- if (DBG) Slog.d(TAG, "Connect with networkId " + networkId);
- mWifiStateMachine.sendMessage(Message.obtain(msg));
- } else {
- Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg=" + msg);
- replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED,
- WifiManager.INVALID_ARGS);
+ if (checkChangePermissionAndReplyIfNotAuthorized(
+ msg, WifiManager.CONNECT_NETWORK_FAILED)) {
+ WifiConfiguration config = (WifiConfiguration) msg.obj;
+ int networkId = msg.arg1;
+ Slog.d(TAG, "CONNECT "
+ + " nid=" + Integer.toString(networkId)
+ + " uid=" + msg.sendingUid
+ + " name="
+ + mContext.getPackageManager().getNameForUid(msg.sendingUid));
+ if (config != null) {
+ if (DBG) Slog.d(TAG, "Connect with config " + config);
+ /* Command is forwarded to state machine */
+ mWifiStateMachine.sendMessage(Message.obtain(msg));
+ } else if (config == null
+ && networkId != WifiConfiguration.INVALID_NETWORK_ID) {
+ if (DBG) Slog.d(TAG, "Connect with networkId " + networkId);
+ mWifiStateMachine.sendMessage(Message.obtain(msg));
+ } else {
+ Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg=" + msg);
+ replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED,
+ WifiManager.INVALID_ARGS);
+ }
}
break;
}
case WifiManager.SAVE_NETWORK: {
- WifiConfiguration config = (WifiConfiguration) msg.obj;
- int networkId = msg.arg1;
- Slog.d("WiFiServiceImpl ", "SAVE"
- + " nid=" + Integer.toString(networkId)
- + " uid=" + msg.sendingUid
- + " name="
- + mContext.getPackageManager().getNameForUid(msg.sendingUid));
- if (config != null && isValid(config)) {
- if (DBG) Slog.d(TAG, "Save network with config " + config);
- /* Command is forwarded to state machine */
- mWifiStateMachine.sendMessage(Message.obtain(msg));
- } else {
- Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg=" + msg);
- replyFailed(msg, WifiManager.SAVE_NETWORK_FAILED,
- WifiManager.INVALID_ARGS);
+ if (checkChangePermissionAndReplyIfNotAuthorized(
+ msg, WifiManager.SAVE_NETWORK_FAILED)) {
+ WifiConfiguration config = (WifiConfiguration) msg.obj;
+ int networkId = msg.arg1;
+ Slog.d(TAG, "SAVE"
+ + " nid=" + Integer.toString(networkId)
+ + " uid=" + msg.sendingUid
+ + " name="
+ + mContext.getPackageManager().getNameForUid(msg.sendingUid));
+ if (config != null) {
+ if (DBG) Slog.d(TAG, "Save network with config " + config);
+ /* Command is forwarded to state machine */
+ mWifiStateMachine.sendMessage(Message.obtain(msg));
+ } else {
+ Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg=" + msg);
+ replyFailed(msg, WifiManager.SAVE_NETWORK_FAILED,
+ WifiManager.INVALID_ARGS);
+ }
}
break;
}
case WifiManager.FORGET_NETWORK:
- mWifiStateMachine.sendMessage(Message.obtain(msg));
+ if (checkChangePermissionAndReplyIfNotAuthorized(
+ msg, WifiManager.FORGET_NETWORK_FAILED)) {
+ mWifiStateMachine.sendMessage(Message.obtain(msg));
+ }
break;
case WifiManager.START_WPS:
+ if (checkChangePermissionAndReplyIfNotAuthorized(msg, WifiManager.WPS_FAILED)) {
+ mWifiStateMachine.sendMessage(Message.obtain(msg));
+ }
+ break;
case WifiManager.CANCEL_WPS:
+ if (checkChangePermissionAndReplyIfNotAuthorized(
+ msg, WifiManager.CANCEL_WPS_FAILED)) {
+ mWifiStateMachine.sendMessage(Message.obtain(msg));
+ }
+ break;
case WifiManager.DISABLE_NETWORK:
+ if (checkChangePermissionAndReplyIfNotAuthorized(
+ msg, WifiManager.DISABLE_NETWORK_FAILED)) {
+ mWifiStateMachine.sendMessage(Message.obtain(msg));
+ }
+ break;
case WifiManager.RSSI_PKTCNT_FETCH: {
- mWifiStateMachine.sendMessage(Message.obtain(msg));
+ if (checkChangePermissionAndReplyIfNotAuthorized(
+ msg, WifiManager.RSSI_PKTCNT_FETCH_FAILED)) {
+ mWifiStateMachine.sendMessage(Message.obtain(msg));
+ }
break;
}
default: {
@@ -318,6 +344,25 @@
}
}
+ /**
+ * Helper method to check if the sender of the message holds the
+ * {@link Manifest.permission#CHANGE_WIFI_STATE} permission, and reply with a failure if it
+ * doesn't
+ *
+ * @param msg Incoming message.
+ * @param replyWhat Param to be filled in the {@link Message#what} field of the failure
+ * reply.
+ * @return true if the sender holds the permission, false otherwise.
+ */
+ private boolean checkChangePermissionAndReplyIfNotAuthorized(Message msg, int replyWhat) {
+ if (!mWifiPermissionsUtil.checkChangePermission(msg.sendingUid)) {
+ Slog.e(TAG, "ClientHandler.handleMessage ignoring unauthorized msg=" + msg);
+ replyFailed(msg, replyWhat, WifiManager.NOT_AUTHORIZED);
+ return false;
+ }
+ return true;
+ }
+
private void replyFailed(Message msg, int what, int why) {
if (msg.replyTo == null) return;
Message reply = Message.obtain();
@@ -394,7 +439,6 @@
mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
mCertManager = mWifiInjector.getWifiCertManager();
- mNotificationController = mWifiInjector.getWifiNotificationController();
mWifiLockManager = mWifiInjector.getWifiLockManager();
mWifiMulticastLockManager = mWifiInjector.getWifiMulticastLockManager();
HandlerThread wifiServiceHandlerThread = mWifiInjector.getWifiServiceHandlerThread();
@@ -721,12 +765,6 @@
mWifiPermissionsUtil.enforceLocationPermission(pkgName, uid);
}
- private boolean checkNetworkSettingsPermission() {
- int result = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.NETWORK_SETTINGS);
- return result == PackageManager.PERMISSION_GRANTED;
- }
-
/**
* see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
* @param enable {@code true} to enable, {@code false} to disable.
@@ -742,10 +780,19 @@
mLog.trace("setWifiEnabled package=% uid=% enable=%").c(packageName)
.c(Binder.getCallingUid()).c(enable).flush();
+ boolean isFromSettings =
+ mWifiPermissionsUtil.checkNetworkSettingsPermission(Binder.getCallingUid());
+
+ // If Airplane mode is enabled, only Settings is allowed to toggle Wifi
+ if (mSettingsStore.isAirplaneModeOn() && !isFromSettings) {
+ mLog.trace("setWifiEnabled in Airplane mode: only Settings can enable wifi").flush();
+ return false;
+ }
+
// If SoftAp is enabled, only Settings is allowed to toggle wifi
boolean apEnabled =
mWifiStateMachine.syncGetWifiApState() != WifiManager.WIFI_AP_STATE_DISABLED;
- boolean isFromSettings = checkNetworkSettingsPermission();
+
if (apEnabled && !isFromSettings) {
mLog.trace("setWifiEnabled SoftAp not disabled: only Settings can enable wifi").flush();
return false;
@@ -1528,14 +1575,31 @@
public WifiConfiguration getMatchingWifiConfig(ScanResult scanResult) {
enforceAccessPermission();
mLog.trace("getMatchingWifiConfig uid=%").c(Binder.getCallingUid()).flush();
- if (!mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_wifi_hotspot2_enabled)) {
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_PASSPOINT)) {
throw new UnsupportedOperationException("Passpoint not enabled");
}
return mWifiStateMachine.syncGetMatchingWifiConfig(scanResult, mWifiStateMachineChannel);
}
/**
+ * Returns list of OSU (Online Sign-Up) providers associated with the given Passpoint network.
+ *
+ * @param scanResult scanResult of the Passpoint AP
+ * @return List of {@link OsuProvider}
+ */
+ @Override
+ public List<OsuProvider> getMatchingOsuProviders(ScanResult scanResult) {
+ enforceAccessPermission();
+ mLog.trace("getMatchingOsuProviders uid=%").c(Binder.getCallingUid()).flush();
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_PASSPOINT)) {
+ throw new UnsupportedOperationException("Passpoint not enabled");
+ }
+ return mWifiStateMachine.syncGetMatchingOsuProviders(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
@@ -1569,7 +1633,7 @@
return 0;
}
- if (isValid(config)) {
+ if (config != null) {
//TODO: pass the Uid the WifiStateMachine as a message parameter
Slog.i("addOrUpdateNetwork", " uid = " + Integer.toString(Binder.getCallingUid())
+ " SSID " + config.SSID
@@ -1717,8 +1781,8 @@
public boolean addOrUpdatePasspointConfiguration(PasspointConfiguration config) {
enforceChangePermission();
mLog.trace("addorUpdatePasspointConfiguration uid=%").c(Binder.getCallingUid()).flush();
- if (!mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_wifi_hotspot2_enabled)) {
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_PASSPOINT)) {
throw new UnsupportedOperationException("Passpoint not enabled");
}
return mWifiStateMachine.syncAddOrUpdatePasspointConfig(mWifiStateMachineChannel, config,
@@ -1735,8 +1799,8 @@
public boolean removePasspointConfiguration(String fqdn) {
enforceChangePermission();
mLog.trace("removePasspointConfiguration uid=%").c(Binder.getCallingUid()).flush();
- if (!mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_wifi_hotspot2_enabled)) {
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_PASSPOINT)) {
throw new UnsupportedOperationException("Passpoint not enabled");
}
return mWifiStateMachine.syncRemovePasspointConfig(mWifiStateMachineChannel, fqdn);
@@ -1753,8 +1817,8 @@
public List<PasspointConfiguration> getPasspointConfigurations() {
enforceAccessPermission();
mLog.trace("getPasspointConfigurations uid=%").c(Binder.getCallingUid()).flush();
- if (!mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_wifi_hotspot2_enabled)) {
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_PASSPOINT)) {
throw new UnsupportedOperationException("Passpoint not enabled");
}
return mWifiStateMachine.syncGetPasspointConfigs(mWifiStateMachineChannel);
@@ -1769,8 +1833,8 @@
public void queryPasspointIcon(long bssid, String fileName) {
enforceAccessPermission();
mLog.trace("queryPasspointIcon uid=%").c(Binder.getCallingUid()).flush();
- if (!mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_wifi_hotspot2_enabled)) {
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_PASSPOINT)) {
throw new UnsupportedOperationException("Passpoint not enabled");
}
mWifiStateMachine.syncQueryPasspointIcon(mWifiStateMachineChannel, bssid, fileName);
@@ -2196,6 +2260,13 @@
}
@Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+ (new WifiShellCommand(mWifiStateMachine)).exec(this, in, out, err, args, callback,
+ resultReceiver);
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -2213,18 +2284,9 @@
String[] ipManagerArgs = new String[args.length - 1];
System.arraycopy(args, 1, ipManagerArgs, 0, ipManagerArgs.length);
mWifiStateMachine.dumpIpManager(fd, pw, ipManagerArgs);
- } else if (args != null && args.length > 0
- && DUMP_ARG_SET_IPREACH_DISCONNECT.equals(args[0])) {
- if (args.length > 1) {
- if (DUMP_ARG_SET_IPREACH_DISCONNECT_ENABLED.equals(args[1])) {
- mWifiStateMachine.setIpReachabilityDisconnectEnabled(true);
- } else if (DUMP_ARG_SET_IPREACH_DISCONNECT_DISABLED.equals(args[1])) {
- mWifiStateMachine.setIpReachabilityDisconnectEnabled(false);
- }
- }
- pw.println("IPREACH_DISCONNECT state is "
- + mWifiStateMachine.getIpReachabilityDisconnectEnabled());
- return;
+ } else if (args != null && args.length > 0 && WifiScoreReport.DUMP_ARG.equals(args[0])) {
+ WifiScoreReport wifiScoreReport = mWifiStateMachine.getWifiScoreReport();
+ if (wifiScoreReport != null) wifiScoreReport.dump(fd, pw, args);
} else {
pw.println("Wi-Fi is " + mWifiStateMachine.syncGetWifiStateByName());
pw.println("Stay-awake conditions: " +
@@ -2234,7 +2296,6 @@
pw.println("mScanPending " + mScanPending);
mWifiController.dump(fd, pw, args);
mSettingsStore.dump(fd, pw, args);
- mNotificationController.dump(fd, pw, args);
mTrafficPoller.dump(fd, pw, args);
pw.println();
pw.println("Locks held:");
@@ -2249,6 +2310,12 @@
pw.println();
mWifiBackupRestore.dump(fd, pw, args);
pw.println();
+ WifiScoreReport wifiScoreReport = mWifiStateMachine.getWifiScoreReport();
+ if (wifiScoreReport != null) {
+ pw.println("WifiScoreReport:");
+ wifiScoreReport.dump(fd, pw, args);
+ }
+ pw.println();
}
}
diff --git a/service/java/com/android/server/wifi/WifiShellCommand.java b/service/java/com/android/server/wifi/WifiShellCommand.java
new file mode 100644
index 0000000..f4db178
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiShellCommand.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.app.AppGlobals;
+import android.content.pm.IPackageManager;
+import android.os.Binder;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+/**
+ * Interprets and executes 'adb shell cmd wifi [args]'.
+ *
+ * To add new commands:
+ * - onCommand: Add a case "<command>" execute. Return a 0
+ * if command executed successfully.
+ * - onHelp: add a description string.
+ *
+ * If additional state objects are necessary add them to the
+ * constructor.
+ *
+ * Permissions: currently root permission is required for all
+ * commands. If the requirement needs to be relaxed then modify
+ * the onCommand method to check for specific permissions on
+ * individual commands.
+ */
+public class WifiShellCommand extends ShellCommand {
+ private final WifiStateMachine mStateMachine;
+ private final IPackageManager mPM;
+
+ WifiShellCommand(WifiStateMachine stateMachine) {
+ mStateMachine = stateMachine;
+ mPM = AppGlobals.getPackageManager();
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ checkRootPermission();
+
+ final PrintWriter pw = getOutPrintWriter();
+ try {
+ switch (cmd != null ? cmd : "") {
+ case "set-ipreach-disconnect": {
+ boolean enabled;
+ String nextArg = getNextArgRequired();
+ if ("enabled".equals(nextArg)) {
+ enabled = true;
+ } else if ("disabled".equals(nextArg)) {
+ enabled = false;
+ } else {
+ pw.println(
+ "Invalid argument to 'set-ipreach-disconnect' - must be 'enabled'"
+ + " or 'disabled'");
+ return -1;
+ }
+ mStateMachine.setIpReachabilityDisconnectEnabled(enabled);
+ return 0;
+ }
+ case "get-ipreach-disconnect":
+ pw.println("IPREACH_DISCONNECT state is "
+ + mStateMachine.getIpReachabilityDisconnectEnabled());
+ return 0;
+ case "set-poll-rssi-interval-msecs":
+ int newPollIntervalMsecs;
+ try {
+ newPollIntervalMsecs = Integer.parseInt(getNextArgRequired());
+ } catch (NumberFormatException e) {
+ pw.println(
+ "Invalid argument to 'set-poll-rssi-interval-msecs' "
+ + "- must be a positive integer");
+ return -1;
+ }
+
+ if (newPollIntervalMsecs < 1) {
+ pw.println(
+ "Invalid argument to 'set-poll-rssi-interval-msecs' "
+ + "- must be a positive integer");
+ return -1;
+ }
+
+ mStateMachine.setPollRssiIntervalMsecs(newPollIntervalMsecs);
+ return 0;
+ case "get-poll-rssi-interval-msecs":
+ pw.println("WifiStateMachine.mPollRssiIntervalMsecs = "
+ + mStateMachine.getPollRssiIntervalMsecs());
+ return 0;
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ } catch (Exception e) {
+ pw.println("Exception: " + e);
+ }
+ return -1;
+ }
+
+ private void checkRootPermission() {
+ final int uid = Binder.getCallingUid();
+ if (uid == 0) {
+ // Root can do anything.
+ return;
+ }
+ throw new SecurityException("Uid " + uid + " does not have access to wifi commands");
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+
+ pw.println("Wi-Fi (wifi) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" set-ipreach-disconnect enabled|disabled");
+ pw.println(" Sets whether CMD_IP_REACHABILITY_LOST events should trigger disconnects.");
+ pw.println(" get-ipreach-disconnect");
+ pw.println(" Gets setting of CMD_IP_REACHABILITY_LOST events triggering disconnects.");
+ pw.println(" set-poll-rssi-interval-msecs <int>");
+ pw.println(" Sets the interval between RSSI polls to <int> milliseconds.");
+ pw.println(" get-poll-rssi-interval-msecs");
+ pw.println(" Gets current interval between RSSI polls, in milliseconds.");
+ pw.println();
+ }
+}
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index 19c4812..3649f8f 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -26,6 +26,8 @@
import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
+import static android.telephony.TelephonyManager.CALL_STATE_IDLE;
+import static android.telephony.TelephonyManager.CALL_STATE_OFFHOOK;
import android.Manifest;
import android.app.ActivityManager;
@@ -75,6 +77,7 @@
import android.net.wifi.WpsInfo;
import android.net.wifi.WpsResult;
import android.net.wifi.WpsResult.Status;
+import android.net.wifi.hotspot2.OsuProvider;
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.net.wifi.p2p.IWifiP2pManager;
import android.os.BatteryStats;
@@ -92,9 +95,11 @@
import android.os.UserManager;
import android.os.WorkSource;
import android.provider.Settings;
+import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import com.android.internal.R;
@@ -221,6 +226,9 @@
private final WifiCountryCode mCountryCode;
// Object holding most recent wifi score report and bad Linkspeed count
private final WifiScoreReport mWifiScoreReport;
+ public WifiScoreReport getWifiScoreReport() {
+ return mWifiScoreReport;
+ }
private final PasspointManager mPasspointManager;
/* Scan results handling */
@@ -284,6 +292,8 @@
private boolean testNetworkDisconnect = false;
private boolean mEnableRssiPolling = false;
+ // Accessed via Binder thread ({get,set}PollRssiIntervalMsecs), and WifiStateMachine thread.
+ private volatile int mPollRssiIntervalMsecs = DEFAULT_POLL_RSSI_INTERVAL_MSECS;
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
@@ -311,7 +321,7 @@
* Interval in milliseconds between polling for RSSI
* and linkspeed information
*/
- private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
+ private static final int DEFAULT_POLL_RSSI_INTERVAL_MSECS = 3000;
/**
* Interval in milliseconds between receiving a disconnect event
@@ -379,6 +389,14 @@
private long mLastDriverRoamAttempt = 0;
private WifiConfiguration targetWificonfiguration = null;
+ int getPollRssiIntervalMsecs() {
+ return mPollRssiIntervalMsecs;
+ }
+
+ void setPollRssiIntervalMsecs(int newPollIntervalMsecs) {
+ mPollRssiIntervalMsecs = newPollIntervalMsecs;
+ }
+
/**
* Method to clear {@link #mTargetRoamBSSID} and reset the the current connected network's
* bssid in wpa_supplicant after a roam/connect attempt.
@@ -590,6 +608,9 @@
// Get the list of installed Passpoint configurations.
static final int CMD_GET_PASSPOINT_CONFIGS = BASE + 108;
+ // Get the list of OSU providers associated with a Passpoint network.
+ static final int CMD_GET_MATCHING_OSU_PROVIDERS = BASE + 109;
+
/* Commands from/to the SupplicantStateTracker */
/* Reset the supplicant state tracker */
static final int CMD_RESET_SUPPLICANT_STATE = BASE + 111;
@@ -711,6 +732,9 @@
/* Indicates that diagnostics should time out a connection start event. */
private static final int CMD_DIAGS_CONNECT_TIMEOUT = BASE + 252;
+ /* Used to set the tx power limit for SAR during the start of a phone call. */
+ private static final int CMD_SELECT_TX_POWER_SCENARIO = BASE + 253;
+
// For message logging.
private static final Class[] sMessageClasses = {
AsyncChannel.class, WifiStateMachine.class, DhcpClient.class };
@@ -742,6 +766,14 @@
private static final int SUSPEND_DUE_TO_HIGH_PERF = 1 << 1;
private static final int SUSPEND_DUE_TO_SCREEN = 1 << 2;
+ /**
+ * Time window in milliseconds for which we send
+ * {@link NetworkAgent#explicitlySelected(boolean)}
+ * after connecting to the network which the user last selected.
+ */
+ @VisibleForTesting
+ public static final int LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS = 30 * 1000;
+
/* Tracks if user has enabled suspend optimizations through settings */
private AtomicBoolean mUserWantsSuspendOpt = new AtomicBoolean(true);
@@ -768,6 +800,7 @@
private final boolean mEnableLinkDebouncing;
private final boolean mEnableChipWakeUpWhenAssociated;
private final boolean mEnableRssiPollWhenAssociated;
+ private final boolean mEnableVoiceCallSarTxPowerLimit;
int mRunningBeaconCount = 0;
@@ -857,6 +890,7 @@
}
return mTelephonyManager;
}
+ private final WifiPhoneStateListener mPhoneStateListener;
private final IBatteryStats mBatteryStats;
@@ -868,11 +902,13 @@
private FrameworkFacade mFacade;
private WifiStateTracker mWifiStateTracker;
private final BackupManagerProxy mBackupManagerProxy;
+ private final WrongPasswordNotifier mWrongPasswordNotifier;
public WifiStateMachine(Context context, FrameworkFacade facade, Looper looper,
UserManager userManager, WifiInjector wifiInjector,
BackupManagerProxy backupManagerProxy, WifiCountryCode countryCode,
- WifiNative wifiNative) {
+ WifiNative wifiNative,
+ WrongPasswordNotifier wrongPasswordNotifier) {
super("WifiStateMachine", looper);
mWifiInjector = wifiInjector;
mWifiMetrics = mWifiInjector.getWifiMetrics();
@@ -883,6 +919,7 @@
mFacade = facade;
mWifiNative = wifiNative;
mBackupManagerProxy = backupManagerProxy;
+ mWrongPasswordNotifier = wrongPasswordNotifier;
// TODO refactor WifiNative use of context out into it's own class
mInterfaceName = mWifiNative.getInterfaceName();
@@ -910,6 +947,7 @@
mFacade.makeSupplicantStateTracker(context, mWifiConfigManager, getHandler());
mLinkProperties = new LinkProperties();
+ mPhoneStateListener = new WifiPhoneStateListener(looper);
mNetworkInfo.setIsAvailable(false);
mLastBssid = null;
@@ -931,7 +969,7 @@
mCountryCode = countryCode;
- mWifiScoreReport = new WifiScoreReport(mContext, mWifiConfigManager);
+ mWifiScoreReport = new WifiScoreReport(mContext, mWifiConfigManager, mClock);
mUserWantsSuspendOpt.set(mFacade.getIntegerSetting(mContext,
Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
@@ -1007,6 +1045,8 @@
R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz);
mEnableLinkDebouncing = mContext.getResources().getBoolean(
R.bool.config_wifi_enable_disconnection_debounce);
+ mEnableVoiceCallSarTxPowerLimit = mContext.getResources().getBoolean(
+ R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit);
mEnableChipWakeUpWhenAssociated = true;
mEnableRssiPollWhenAssociated = true;
@@ -1864,6 +1904,22 @@
}
/**
+ * Retrieve a list of {@link OsuProvider} associated with the given AP synchronously.
+ *
+ * @param scanResult The scan result of the AP
+ * @param channel Channel for communicating with the state machine
+ * @return List of {@link OsuProvider}
+ */
+ public List<OsuProvider> syncGetMatchingOsuProviders(ScanResult scanResult,
+ AsyncChannel channel) {
+ Message resultMsg =
+ channel.sendMessageSynchronously(CMD_GET_MATCHING_OSU_PROVIDERS, scanResult);
+ List<OsuProvider> providers = (List<OsuProvider>) resultMsg.obj;
+ resultMsg.recycle();
+ return providers;
+ }
+
+ /**
* Add or update a Passpoint configuration synchronously.
*
* @param channel Channel for communicating with the state machine
@@ -3083,11 +3139,11 @@
}
private WifiInfo getWiFiInfoForUid(int uid) {
+ WifiInfo result = new WifiInfo(mWifiInfo);
if (Binder.getCallingUid() == Process.myUid()) {
- return mWifiInfo;
+ return result;
}
- WifiInfo result = new WifiInfo(mWifiInfo);
result.setMacAddress(WifiInfo.DEFAULT_MAC_ADDRESS);
IBinder binder = mFacade.getService("package");
@@ -3211,6 +3267,7 @@
if (scanDetailCache != null) {
ScanDetail scanDetail = scanDetailCache.getScanDetail(stateChangeResult.BSSID);
if (scanDetail != null) {
+ mWifiInfo.setFrequency(scanDetail.getScanResult().frequency);
NetworkDetail networkDetail = scanDetail.getNetworkDetail();
if (networkDetail != null
&& networkDetail.getAnt() == NetworkDetail.Ant.ChargeablePublic) {
@@ -3358,6 +3415,7 @@
mDiagsConnectionStartMillis = mClock.getElapsedSinceBootMillis();
mWifiDiagnostics.reportConnectionEvent(
mDiagsConnectionStartMillis, WifiDiagnostics.CONNECTION_EVENT_STARTED);
+ mWrongPasswordNotifier.onNewConnectionAttempt();
// TODO(b/35329124): Remove CMD_DIAGS_CONNECT_TIMEOUT, once WifiStateMachine
// grows a proper CONNECTING state.
sendMessageDelayed(CMD_DIAGS_CONNECT_TIMEOUT,
@@ -3548,6 +3606,27 @@
}
+ /**
+ * Determine if the specified auth failure is considered to be a permanent wrong password
+ * failure. The criteria for such failure is when wrong password error is detected
+ * and the network had never been connected before.
+ *
+ * For networks that have previously connected successfully, we consider wrong password
+ * failures to be temporary, to be on the conservative side. Since this might be the
+ * case where we are trying to connect to a wrong network (e.g. A network with same SSID
+ * but different password).
+ */
+ private boolean isPermanentWrongPasswordFailure(int networkId, int reasonCode) {
+ if (reasonCode != WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD) {
+ return false;
+ }
+ WifiConfiguration network = mWifiConfigManager.getConfiguredNetwork(networkId);
+ if (network != null && network.getNetworkSelectionStatus().getHasEverConnected()) {
+ return false;
+ }
+ return true;
+ }
+
private class WifiNetworkFactory extends NetworkFactory {
public WifiNetworkFactory(Looper l, Context c, String TAG, NetworkCapabilities f) {
super(l, c, TAG, f);
@@ -3667,6 +3746,51 @@
}
}
+ /**
+ * Helper function to increment the appropriate setup failure metrics.
+ */
+ private void incrementMetricsForSetupFailure(int failureReason) {
+ if (failureReason == WifiNative.SETUP_FAILURE_HAL) {
+ mWifiMetrics.incrementNumWifiOnFailureDueToHal();
+ } else if (failureReason == WifiNative.SETUP_FAILURE_WIFICOND) {
+ mWifiMetrics.incrementNumWifiOnFailureDueToWificond();
+ }
+ }
+
+ /**
+ * Register the phone listener if we need to set/reset the power limits during voice call for
+ * this device.
+ */
+ private void maybeRegisterPhoneListener() {
+ if (mEnableVoiceCallSarTxPowerLimit) {
+ logd("Registering for telephony call state changes");
+ getTelephonyManager().listen(
+ mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+ }
+ }
+
+ /**
+ * Listen for phone call state events to set/reset TX power limits for SAR requirements.
+ */
+ private class WifiPhoneStateListener extends PhoneStateListener {
+ WifiPhoneStateListener(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ if (mEnableVoiceCallSarTxPowerLimit) {
+ if (state == CALL_STATE_OFFHOOK) {
+ sendMessage(CMD_SELECT_TX_POWER_SCENARIO,
+ WifiNative.TX_POWER_SCENARIO_VOICE_CALL);
+ } else if (state == CALL_STATE_IDLE) {
+ sendMessage(CMD_SELECT_TX_POWER_SCENARIO,
+ WifiNative.TX_POWER_SCENARIO_NORMAL);
+ }
+ }
+ }
+ }
+
/********************************************************
* HSM states
*******************************************************/
@@ -3769,6 +3893,7 @@
Log.e(TAG, "Failed to load from config store");
}
maybeRegisterNetworkFactory();
+ maybeRegisterPhoneListener();
break;
case CMD_SCREEN_STATE_CHANGED:
handleScreenStateChanged(message.arg1 != 0);
@@ -3818,6 +3943,7 @@
case CMD_ROAM_WATCHDOG_TIMER:
case CMD_DISABLE_P2P_WATCHDOG_TIMER:
case CMD_DISABLE_EPHEMERAL_NETWORK:
+ case CMD_SELECT_TX_POWER_SCENARIO:
messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
break;
case CMD_SET_SUSPEND_OPT_ENABLED:
@@ -3889,6 +4015,9 @@
case CMD_GET_MATCHING_CONFIG:
replyToMessage(message, message.what);
break;
+ case CMD_GET_MATCHING_OSU_PROVIDERS:
+ replyToMessage(message, message.what, new ArrayList<OsuProvider>());
+ break;
case CMD_IP_CONFIGURATION_SUCCESSFUL:
case CMD_IP_CONFIGURATION_LOST:
case CMD_IP_REACHABILITY_LOST:
@@ -3984,6 +4113,11 @@
mWifiDiagnostics.reportConnectionEvent(
(Long) message.obj, BaseWifiDiagnostics.CONNECTION_EVENT_FAILED);
break;
+ case 0:
+ // We want to notice any empty messages (with what == 0) that might crop up.
+ // For example, we may have recycled a message sent to multiple handlers.
+ Log.wtf(TAG, "Error! empty message encountered");
+ break;
default:
loge("Error! unhandled message" + message);
break;
@@ -4013,7 +4147,13 @@
logStateAndMessage(message, this);
switch (message.what) {
case CMD_START_SUPPLICANT:
- mClientInterface = mWifiNative.setupForClientMode();
+ Pair<Integer, IClientInterface> statusAndInterface =
+ mWifiNative.setupForClientMode();
+ if (statusAndInterface.first == WifiNative.SETUP_SUCCESS) {
+ mClientInterface = statusAndInterface.second;
+ } else {
+ incrementMetricsForSetupFailure(statusAndInterface.first);
+ }
if (mClientInterface == null
|| !mDeathRecipient.linkToDeath(mClientInterface.asBinder())) {
setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
@@ -4050,6 +4190,7 @@
}
if (mVerboseLoggingEnabled) log("Supplicant start successful");
mWifiMonitor.startMonitoring(mInterfaceName, true);
+ mWifiInjector.getWifiLastResortWatchdog().clearAllFailureCounts();
setSupplicantLogLevel();
transitionTo(mSupplicantStartingState);
break;
@@ -4191,6 +4332,18 @@
* driver are changed to reduce interference with bluetooth
*/
mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
+ // Check if there is a voice call on-going and set/reset the tx power limit
+ // appropriately.
+ if (mEnableVoiceCallSarTxPowerLimit) {
+ if (getTelephonyManager().isOffhook()) {
+ sendMessage(CMD_SELECT_TX_POWER_SCENARIO,
+ WifiNative.TX_POWER_SCENARIO_VOICE_CALL);
+ } else {
+ sendMessage(CMD_SELECT_TX_POWER_SCENARIO,
+ WifiNative.TX_POWER_SCENARIO_NORMAL);
+ }
+ }
+
// initialize network state
setNetworkDetailedState(DetailedState.DISCONNECTED);
@@ -4304,7 +4457,7 @@
break;
case CMD_RESET_SIM_NETWORKS:
log("resetting EAP-SIM/AKA/AKA' networks since SIM was changed");
- mWifiConfigManager.resetSimNetworks();
+ mWifiConfigManager.resetSimNetworks(message.arg1 == 1);
break;
case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
mBluetoothConnectionActive = (message.arg1 !=
@@ -4372,6 +4525,13 @@
mWifiConnectivityManager.forceConnectivityScan();
}
break;
+ case CMD_SELECT_TX_POWER_SCENARIO:
+ int txPowerScenario = message.arg1;
+ logd("Setting Tx power scenario to " + txPowerScenario);
+ if (!mWifiNative.selectTxPowerScenario(txPowerScenario)) {
+ loge("Failed to set TX power scenario");
+ }
+ break;
default:
return NOT_HANDLED;
}
@@ -4646,22 +4806,20 @@
mWifiConfigManager.updateNetworkAfterConnect(mLastNetworkId);
// On connect, reset wifiScoreReport
mWifiScoreReport.reset();
+
+ // Notify PasspointManager of Passpoint network connected event.
+ WifiConfiguration currentNetwork = getCurrentWifiConfiguration();
+ if (currentNetwork.isPasspoint()) {
+ mPasspointManager.onPasspointNetworkConnected(currentNetwork.FQDN);
+ }
}
}
void registerDisconnected() {
if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
mWifiConfigManager.updateNetworkAfterDisconnect(mLastNetworkId);
- // We are switching away from this configuration,
- // hence record the time we were connected last
- WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(mLastNetworkId);
- if (config != null) {
- // Remove WifiConfiguration for ephemeral or Passpoint networks, since they're
- // temporary networks.
- if (config.ephemeral || config.isPasspoint()) {
- mWifiConfigManager.removeNetwork(mLastNetworkId, Process.WIFI_UID);
- }
- }
+ // Let's remove any ephemeral or passpoint networks on every disconnect.
+ mWifiConfigManager.removeAllEphemeralOrPasspointConfiguredNetworks();
}
}
@@ -4796,9 +4954,21 @@
mWifiDiagnostics.captureBugReportData(
WifiDiagnostics.REPORT_REASON_AUTH_FAILURE);
mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
- mWifiConfigManager.updateNetworkSelectionStatus(mTargetNetworkId,
- WifiConfiguration.NetworkSelectionStatus
- .DISABLED_AUTHENTICATION_FAILURE);
+ int disableReason = WifiConfiguration.NetworkSelectionStatus
+ .DISABLED_AUTHENTICATION_FAILURE;
+ // Check if this is a permanent wrong password failure.
+ if (isPermanentWrongPasswordFailure(mTargetNetworkId, message.arg2)) {
+ disableReason = WifiConfiguration.NetworkSelectionStatus
+ .DISABLED_BY_WRONG_PASSWORD;
+ WifiConfiguration targetedNetwork =
+ mWifiConfigManager.getConfiguredNetwork(mTargetNetworkId);
+ if (targetedNetwork != null) {
+ mWrongPasswordNotifier.onWrongPasswordError(
+ targetedNetwork.SSID);
+ }
+ }
+ mWifiConfigManager.updateNetworkSelectionStatus(
+ mTargetNetworkId, disableReason);
//If failure occurred while Metrics is tracking a ConnnectionEvent, end it.
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,
@@ -4967,6 +5137,10 @@
replyToMessage(message, message.what,
mPasspointManager.getMatchingWifiConfig((ScanResult) message.obj));
break;
+ case CMD_GET_MATCHING_OSU_PROVIDERS:
+ replyToMessage(message, message.what,
+ mPasspointManager.getMatchingOsuProviders((ScanResult) message.obj));
+ break;
case CMD_RECONNECT:
mWifiConnectivityManager.forceConnectivityScan();
break;
@@ -5226,6 +5400,15 @@
if (config != null) {
mWifiInfo.setBSSID(mLastBssid);
mWifiInfo.setNetworkId(mLastNetworkId);
+
+ ScanDetailCache scanDetailCache =
+ mWifiConfigManager.getScanDetailCacheForNetwork(config.networkId);
+ if (scanDetailCache != null && mLastBssid != null) {
+ ScanResult scanResult = scanDetailCache.get(mLastBssid);
+ if (scanResult != null) {
+ mWifiInfo.setFrequency(scanResult.frequency);
+ }
+ }
mWifiConnectivityManager.trackBssid(mLastBssid, true, reasonCode);
// We need to get the updated pseudonym from supplicant for EAP-SIM/AKA/AKA'
if (config.enterpriseConfig != null
@@ -5704,8 +5887,8 @@
mWifiInfo, mNetworkAgent, mAggressiveHandover,
mWifiMetrics);
}
- sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
- mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
+ sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
+ mPollRssiIntervalMsecs);
if (mVerboseLoggingEnabled) sendRssiChangeBroadcast(mWifiInfo.getRssi());
} else {
// Polling has completed
@@ -5722,8 +5905,8 @@
if (mEnableRssiPolling) {
// First poll
fetchRssiLinkSpeedAndFrequencyNative();
- sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
- mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
+ sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
+ mPollRssiIntervalMsecs);
}
break;
case WifiManager.RSSI_PKTCNT_FETCH:
@@ -5768,7 +5951,18 @@
mLastBssid = (String) message.obj;
if (mLastBssid != null && (mWifiInfo.getBSSID() == null
|| !mLastBssid.equals(mWifiInfo.getBSSID()))) {
- mWifiInfo.setBSSID((String) message.obj);
+ mWifiInfo.setBSSID(mLastBssid);
+ WifiConfiguration config = getCurrentWifiConfiguration();
+ if (config != null) {
+ ScanDetailCache scanDetailCache = mWifiConfigManager
+ .getScanDetailCacheForNetwork(config.networkId);
+ if (scanDetailCache != null) {
+ ScanResult scanResult = scanDetailCache.get(mLastBssid);
+ if (scanResult != null) {
+ mWifiInfo.setFrequency(scanResult.frequency);
+ }
+ }
+ }
sendNetworkStateChangeBroadcast(mLastBssid);
}
break;
@@ -5899,13 +6093,29 @@
}
}
+ /**
+ * Helper function to check if we need to invoke
+ * {@link NetworkAgent#explicitlySelected(boolean)} to indicate that we connected to a network
+ * which the user just chose
+ * (i.e less than {@link #LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS) before).
+ */
+ @VisibleForTesting
+ public boolean shouldEvaluateWhetherToSendExplicitlySelected(WifiConfiguration currentConfig) {
+ if (currentConfig == null) {
+ Log.wtf(TAG, "Current WifiConfiguration is null, but IP provisioning just succeeded");
+ return false;
+ }
+ long currentTimeMillis = mClock.getElapsedSinceBootMillis();
+ return (mWifiConfigManager.getLastSelectedNetwork() == currentConfig.networkId
+ && currentTimeMillis - mWifiConfigManager.getLastSelectedTimeStamp()
+ < LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS);
+ }
+
private void sendConnectedState() {
// 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 (config == null) {
- Log.wtf(TAG, "Current WifiConfiguration is null, but IP provisioning just succeeded");
- } else if (mWifiConfigManager.getLastSelectedNetwork() == config.networkId) {
+ if (shouldEvaluateWhetherToSendExplicitlySelected(config)) {
boolean prompt =
mWifiPermissionsUtil.checkConfigOverridePermission(config.lastConnectUid);
if (mVerboseLoggingEnabled) {
@@ -6081,7 +6291,9 @@
class ConnectedState extends State {
@Override
public void enter() {
- updateDefaultRouteMacAddress(1000);
+ // TODO: b/64349637 Investigate getting default router IP/MAC address info from
+ // IpManager
+ //updateDefaultRouteMacAddress(1000);
if (mVerboseLoggingEnabled) {
log("Enter ConnectedState "
+ " mScreenOn=" + mScreenOn);
@@ -6699,7 +6911,13 @@
SoftApModeConfiguration config = (SoftApModeConfiguration) message.obj;
mMode = config.getTargetMode();
- IApInterface apInterface = mWifiNative.setupForSoftApMode();
+ IApInterface apInterface = null;
+ Pair<Integer, IApInterface> statusAndInterface = mWifiNative.setupForSoftApMode();
+ if (statusAndInterface.first == WifiNative.SETUP_SUCCESS) {
+ apInterface = statusAndInterface.second;
+ } else {
+ incrementMetricsForSetupFailure(statusAndInterface.first);
+ }
if (apInterface == null) {
setWifiApState(WIFI_AP_STATE_FAILED,
WifiManager.SAP_START_FAILURE_GENERAL, null, mMode);
@@ -6912,6 +7130,7 @@
*/
public void updateWifiMetrics() {
mWifiMetrics.updateSavedNetworks(mWifiConfigManager.getSavedNetworks());
+ mPasspointManager.updateMetrics();
}
/**
diff --git a/service/java/com/android/server/wifi/WifiVendorHal.java b/service/java/com/android/server/wifi/WifiVendorHal.java
index 88f1898..12674aa 100644
--- a/service/java/com/android/server/wifi/WifiVendorHal.java
+++ b/service/java/com/android/server/wifi/WifiVendorHal.java
@@ -778,9 +778,41 @@
}
/**
+ * Translation table used by getSupportedFeatureSet for translating IWifiChip caps
+ */
+ private static final int[][] sChipFeatureCapabilityTranslation = {
+ {WifiManager.WIFI_FEATURE_TX_POWER_LIMIT,
+ android.hardware.wifi.V1_1.IWifiChip.ChipCapabilityMask.SET_TX_POWER_LIMIT
+ },
+ {WifiManager.WIFI_FEATURE_D2D_RTT,
+ android.hardware.wifi.V1_1.IWifiChip.ChipCapabilityMask.D2D_RTT
+ },
+ {WifiManager.WIFI_FEATURE_D2AP_RTT,
+ android.hardware.wifi.V1_1.IWifiChip.ChipCapabilityMask.D2AP_RTT
+ }
+ };
+
+ /**
+ * Feature bit mask translation for Chip
+ *
+ * @param capabilities bitmask defined IWifiChip.ChipCapabilityMask
+ * @return bitmask defined by WifiManager.WIFI_FEATURE_*
+ */
+ @VisibleForTesting
+ int wifiFeatureMaskFromChipCapabilities(int capabilities) {
+ int features = 0;
+ for (int i = 0; i < sChipFeatureCapabilityTranslation.length; i++) {
+ if ((capabilities & sChipFeatureCapabilityTranslation[i][1]) != 0) {
+ features |= sChipFeatureCapabilityTranslation[i][0];
+ }
+ }
+ return features;
+ }
+
+ /**
* Translation table used by getSupportedFeatureSet for translating IWifiStaIface caps
*/
- private static final int[][] sFeatureCapabilityTranslation = {
+ private static final int[][] sStaFeatureCapabilityTranslation = {
{WifiManager.WIFI_FEATURE_INFRA_5G,
IWifiStaIface.StaIfaceCapabilityMask.STA_5G
},
@@ -831,9 +863,9 @@
@VisibleForTesting
int wifiFeatureMaskFromStaCapabilities(int capabilities) {
int features = 0;
- for (int i = 0; i < sFeatureCapabilityTranslation.length; i++) {
- if ((capabilities & sFeatureCapabilityTranslation[i][1]) != 0) {
- features |= sFeatureCapabilityTranslation[i][0];
+ for (int i = 0; i < sStaFeatureCapabilityTranslation.length; i++) {
+ if ((capabilities & sStaFeatureCapabilityTranslation[i][1]) != 0) {
+ features |= sStaFeatureCapabilityTranslation[i][0];
}
}
return features;
@@ -848,13 +880,22 @@
*/
public int getSupportedFeatureSet() {
int featureSet = 0;
+ if (!mHalDeviceManager.isStarted()) {
+ return featureSet; // TODO: can't get capabilities with Wi-Fi down
+ }
try {
final MutableInt feat = new MutableInt(0);
synchronized (sLock) {
+ if (mIWifiChip != null) {
+ mIWifiChip.getCapabilities((status, capabilities) -> {
+ if (!ok(status)) return;
+ feat.value = wifiFeatureMaskFromChipCapabilities(capabilities);
+ });
+ }
if (mIWifiStaIface != null) {
mIWifiStaIface.getCapabilities((status, capabilities) -> {
if (!ok(status)) return;
- feat.value = wifiFeatureMaskFromStaCapabilities(capabilities);
+ feat.value |= wifiFeatureMaskFromStaCapabilities(capabilities);
});
}
}
@@ -2318,6 +2359,62 @@
}
}
+ /**
+ * Method to mock out the V1_1 IWifiChip retrieval in unit tests.
+ *
+ * @return 1.1 IWifiChip object if the device is running the 1.1 wifi hal service, null
+ * otherwise.
+ */
+ protected android.hardware.wifi.V1_1.IWifiChip getWifiChipForV1_1Mockable() {
+ if (mIWifiChip == null) return null;
+ return android.hardware.wifi.V1_1.IWifiChip.castFrom(mIWifiChip);
+ }
+
+ private int frameworkToHalTxPowerScenario(int scenario) {
+ switch (scenario) {
+ case WifiNative.TX_POWER_SCENARIO_VOICE_CALL:
+ return android.hardware.wifi.V1_1.IWifiChip.TxPowerScenario.VOICE_CALL;
+ default:
+ throw new IllegalArgumentException("bad scenario: " + scenario);
+ }
+ }
+
+ /**
+ * Select one of the pre-configured TX power level scenarios or reset it back to normal.
+ * Primarily used for meeting SAR requirements during voice calls.
+ *
+ * @param scenario Should be one {@link WifiNative#TX_POWER_SCENARIO_NORMAL} or
+ * {@link WifiNative#TX_POWER_SCENARIO_VOICE_CALL}.
+ * @return true for success; false for failure or if the HAL version does not support this API.
+ */
+ public boolean selectTxPowerScenario(int scenario) {
+ synchronized (sLock) {
+ try {
+ android.hardware.wifi.V1_1.IWifiChip iWifiChipV11 = getWifiChipForV1_1Mockable();
+ if (iWifiChipV11 == null) return boolResult(false);
+ WifiStatus status;
+ if (scenario != WifiNative.TX_POWER_SCENARIO_NORMAL) {
+ int halScenario;
+ try {
+ halScenario = frameworkToHalTxPowerScenario(scenario);
+ } catch (IllegalArgumentException e) {
+ mLog.err("Illegal argument for select tx power scenario")
+ .c(e.toString()).flush();
+ return false;
+ }
+ status = iWifiChipV11.selectTxPowerScenario(halScenario);
+ } else {
+ status = iWifiChipV11.resetTxPowerScenario();
+ }
+ if (!ok(status)) return false;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ return true;
+ }
+ }
+
// This creates a blob of IE elements from the array received.
// TODO: This ugly conversion can be removed if we put IE elements in ScanResult.
private static byte[] hidlIeArrayToFrameworkIeBlob(ArrayList<WifiInformationElement> ies) {
diff --git a/service/java/com/android/server/wifi/WrongPasswordNotifier.java b/service/java/com/android/server/wifi/WrongPasswordNotifier.java
new file mode 100644
index 0000000..37e23da
--- /dev/null
+++ b/service/java/com/android/server/wifi/WrongPasswordNotifier.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Settings;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.notification.SystemNotificationChannels;
+import com.android.server.wifi.util.NativeUtil;
+
+/**
+ * Responsible for notifying user for wrong password errors.
+ */
+public class WrongPasswordNotifier {
+ // Number of milliseconds to wait before automatically dismiss the notification.
+ private static final long CANCEL_TIMEOUT_MILLISECONDS = 5 * 60 * 1000;
+
+ // Unique ID associated with the notification.
+ @VisibleForTesting
+ public static final int NOTIFICATION_ID = SystemMessage.NOTE_WIFI_WRONG_PASSWORD;
+
+ // Flag indicating if a wrong password error is detected for the current connection.
+ private boolean mWrongPasswordDetected;
+
+ private final Context mContext;
+ private final NotificationManager mNotificationManager;
+ private final FrameworkFacade mFrameworkFacade;
+
+ public WrongPasswordNotifier(Context context, FrameworkFacade frameworkFacade) {
+ mContext = context;
+ mFrameworkFacade = frameworkFacade;
+ mNotificationManager =
+ (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ /**
+ * Invoked when a wrong password error for a Wi-Fi network is detected.
+ *
+ * @param ssid The SSID of the Wi-Fi network
+ */
+ public void onWrongPasswordError(String ssid) {
+ showNotification(ssid);
+ mWrongPasswordDetected = true;
+ }
+
+ /**
+ * Invoked when attempting a new Wi-Fi network connection.
+ */
+ public void onNewConnectionAttempt() {
+ if (mWrongPasswordDetected) {
+ dismissNotification();
+ mWrongPasswordDetected = false;
+ }
+ }
+
+ /**
+ * Display wrong password notification for a given Wi-Fi network (specified by its SSID).
+ *
+ * @param ssid SSID of the Wi-FI network
+ */
+ private void showNotification(String ssid) {
+ Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS);
+ intent.putExtra("wifi_start_connect_ssid", NativeUtil.removeEnclosingQuotes(ssid));
+ Notification.Builder builder = mFrameworkFacade.makeNotificationBuilder(mContext,
+ SystemNotificationChannels.NETWORK_ALERTS)
+ .setAutoCancel(true)
+ .setTimeoutAfter(CANCEL_TIMEOUT_MILLISECONDS)
+ // TODO(zqiu): consider creating a new icon.
+ .setSmallIcon(com.android.internal.R.drawable.stat_notify_wifi_in_range)
+ .setContentTitle(mContext.getString(
+ com.android.internal.R.string.wifi_available_title_failed_to_connect))
+ .setContentText(ssid)
+ .setContentIntent(mFrameworkFacade.getActivity(
+ mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT))
+ .setColor(mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color));
+ mNotificationManager.notify(NOTIFICATION_ID, builder.build());
+ }
+
+ /**
+ * Dismiss the notification that was generated by {@link #showNotification}. The notification
+ * might have already been dismissed, either by user or timeout. We'll attempt to dismiss it
+ * regardless if it is been dismissed or not, to reduce code complexity.
+ */
+ private void dismissNotification() {
+ // Notification might have already been dismissed, either by user or timeout. It is
+ // still okay to cancel it if already dismissed.
+ mNotificationManager.cancel(null, NOTIFICATION_ID);
+ }
+}
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareClientState.java b/service/java/com/android/server/wifi/aware/WifiAwareClientState.java
index 7123d01..3570f1d 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareClientState.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareClientState.java
@@ -61,14 +61,15 @@
private final String mCallingPackage;
private final boolean mNotifyIdentityChange;
- private AppOpsManager mAppOps;
+ private final AppOpsManager mAppOps;
+ private final long mCreationTime;
private static final byte[] ALL_ZERO_MAC = new byte[] {0, 0, 0, 0, 0, 0};
private byte[] mLastDiscoveryInterfaceMac = ALL_ZERO_MAC;
public WifiAwareClientState(Context context, int clientId, int uid, int pid,
String callingPackage, IWifiAwareEventCallback callback, ConfigRequest configRequest,
- boolean notifyIdentityChange) {
+ boolean notifyIdentityChange, long creationTime) {
mContext = context;
mClientId = clientId;
mUid = uid;
@@ -79,6 +80,7 @@
mNotifyIdentityChange = notifyIdentityChange;
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ mCreationTime = creationTime;
}
/**
@@ -109,6 +111,14 @@
return mNotifyIdentityChange;
}
+ public long getCreationTime() {
+ return mCreationTime;
+ }
+
+ public SparseArray<WifiAwareDiscoverySessionState> getSessions() {
+ return mSessions;
+ }
+
/**
* Searches the discovery sessions of this client and returns the one
* corresponding to the publish/subscribe ID. Used on callbacks from HAL to
@@ -164,15 +174,17 @@
*
* @param sessionId The session ID of the session to be destroyed.
*/
- public void terminateSession(int sessionId) {
+ public WifiAwareDiscoverySessionState terminateSession(int sessionId) {
WifiAwareDiscoverySessionState session = mSessions.get(sessionId);
if (session == null) {
Log.e(TAG, "terminateSession: sessionId doesn't exist - " + sessionId);
- return;
+ return null;
}
session.terminate();
mSessions.delete(sessionId);
+
+ return session;
}
/**
@@ -240,7 +252,8 @@
try {
boolean hasPermission = hasLocationingPermission();
if (VDBG) Log.v(TAG, "hasPermission=" + hasPermission);
- mCallback.onIdentityChanged(hasPermission ? mac : ALL_ZERO_MAC);
+ mCallback.onIdentityChanged(
+ hasPermission ? currentDiscoveryInterfaceMac : ALL_ZERO_MAC);
} catch (RemoteException e) {
Log.w(TAG, "onIdentityChanged: RemoteException - ignored: " + e);
}
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
index 723828d..04bf2e0 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
@@ -16,8 +16,11 @@
package com.android.server.wifi.aware;
+import android.Manifest;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.wifi.V1_0.NanDataPathChannelCfg;
+import android.hardware.wifi.V1_0.NanStatusType;
import android.net.ConnectivityManager;
import android.net.IpPrefix;
import android.net.LinkAddress;
@@ -32,14 +35,18 @@
import android.net.RouteInfo;
import android.net.wifi.aware.WifiAwareManager;
import android.net.wifi.aware.WifiAwareNetworkSpecifier;
+import android.net.wifi.aware.WifiAwareUtils;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.util.WifiPermissionsWrapper;
import libcore.util.HexEncoding;
@@ -84,6 +91,8 @@
private final Map<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation>
mNetworkRequestsCache = new ArrayMap<>();
private Context mContext;
+ private WifiAwareMetrics mAwareMetrics;
+ private WifiPermissionsWrapper mPermissionsWrapper;
private Looper mLooper;
private WifiAwareNetworkFactory mNetworkFactory;
private INetworkManagementService mNwService;
@@ -96,10 +105,13 @@
* Initialize the Aware data-path state manager. Specifically register the network factory with
* connectivity service.
*/
- public void start(Context context, Looper looper) {
+ public void start(Context context, Looper looper, WifiAwareMetrics awareMetrics,
+ WifiPermissionsWrapper permissionsWrapper) {
if (VDBG) Log.v(TAG, "start");
mContext = context;
+ mAwareMetrics = awareMetrics;
+ mPermissionsWrapper = permissionsWrapper;
mLooper = looper;
mNetworkCapabilitiesFilter.clearAll();
@@ -167,7 +179,13 @@
public void deleteAllInterfaces() {
if (VDBG) Log.v(TAG, "deleteAllInterfaces");
- for (String name : mInterfaces) {
+ if (mMgr.getCapabilities() == null) {
+ Log.e(TAG, "deleteAllInterfaces: capabilities aren't initialized yet!");
+ return;
+ }
+
+ for (int i = 0; i < mMgr.getCapabilities().maxNdiInterfaces; ++i) {
+ String name = AWARE_INTERFACE_PREFIX + i;
mMgr.deleteDataPathInterface(name);
}
}
@@ -260,6 +278,7 @@
}
mNetworkRequestsCache.remove(networkSpecifier);
+ mAwareMetrics.recordNdpStatus(reason, networkSpecifier.isOutOfBand(), nnri.startTimestamp);
}
@@ -311,23 +330,32 @@
if (DBG) {
Log.d(TAG, "onDataPathRequest: network request cache = " + mNetworkRequestsCache);
}
- mMgr.respondToDataPathRequest(false, ndpId, "", null, null);
+ mMgr.respondToDataPathRequest(false, ndpId, "", null, null, false);
return null;
}
if (nnri.state != AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_REQUEST) {
Log.w(TAG, "onDataPathRequest: request " + networkSpecifier + " is incorrect state="
+ nnri.state);
- mMgr.respondToDataPathRequest(false, ndpId, "", null, null);
+ mMgr.respondToDataPathRequest(false, ndpId, "", null, null, false);
+ mNetworkRequestsCache.remove(networkSpecifier);
+ return null;
+ }
+
+ nnri.interfaceName = selectInterfaceForRequest(nnri);
+ if (nnri.interfaceName == null) {
+ Log.w(TAG,
+ "onDataPathRequest: request " + networkSpecifier + " no interface available");
+ mMgr.respondToDataPathRequest(false, ndpId, "", null, null, false);
mNetworkRequestsCache.remove(networkSpecifier);
return null;
}
nnri.state = AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_RESPOND_RESPONSE;
nnri.ndpId = ndpId;
- nnri.interfaceName = selectInterfaceForRequest(nnri);
+ nnri.startTimestamp = SystemClock.elapsedRealtime();
mMgr.respondToDataPathRequest(true, ndpId, nnri.interfaceName, nnri.networkSpecifier.pmk,
- nnri.networkSpecifier.passphrase);
+ nnri.networkSpecifier.passphrase, nnri.networkSpecifier.isOutOfBand());
return networkSpecifier;
}
@@ -338,7 +366,7 @@
* @param ndpId The ID of the data-path (NDP)
* @param success Whether or not the 'RespondToDataPathRequest' operation was a success.
*/
- public void onRespondToDataPathRequest(int ndpId, boolean success) {
+ public void onRespondToDataPathRequest(int ndpId, boolean success, int reasonOnFailure) {
if (VDBG) {
Log.v(TAG, "onRespondToDataPathRequest: ndpId=" + ndpId + ", success=" + success);
}
@@ -369,6 +397,8 @@
+ " failed responding");
mMgr.endDataPath(ndpId);
mNetworkRequestsCache.remove(networkSpecifier);
+ mAwareMetrics.recordNdpStatus(reasonOnFailure, networkSpecifier.isOutOfBand(),
+ nnri.startTimestamp);
return;
}
@@ -475,12 +505,19 @@
networkCapabilities, linkProperties, NETWORK_FACTORY_SCORE_AVAIL,
networkSpecifier, ndpId);
nnri.networkAgent.sendNetworkInfo(networkInfo);
+
+ mAwareMetrics.recordNdpStatus(NanStatusType.SUCCESS, networkSpecifier.isOutOfBand(),
+ nnri.startTimestamp);
+ nnri.startTimestamp = SystemClock.elapsedRealtime(); // update time-stamp for duration
+ mAwareMetrics.recordNdpCreation(nnri.uid, mNetworkRequestsCache);
} else {
if (DBG) {
Log.d(TAG, "onDataPathConfirm: data-path for networkSpecifier=" + networkSpecifier
+ " rejected - reason=" + reason);
}
mNetworkRequestsCache.remove(networkSpecifier);
+ mAwareMetrics.recordNdpStatus(reason, networkSpecifier.isOutOfBand(),
+ nnri.startTimestamp);
}
return networkSpecifier;
@@ -505,6 +542,11 @@
}
tearDownInterface(nnriE.getValue());
+ if (nnriE.getValue().state == AwareNetworkRequestInformation.STATE_RESPONDER_CONFIRMED
+ || nnriE.getValue().state
+ == AwareNetworkRequestInformation.STATE_INITIATOR_CONFIRMED) {
+ mAwareMetrics.recordNdpSessionDuration(nnriE.getValue().startTimestamp);
+ }
mNetworkRequestsCache.remove(nnriE.getKey());
}
@@ -538,6 +580,8 @@
}
return;
}
+ mAwareMetrics.recordNdpStatus(NanStatusType.INTERNAL_FAILURE,
+ nnri.networkSpecifier.isOutOfBand(), nnri.startTimestamp);
mMgr.endDataPath(nnri.ndpId);
}
@@ -592,12 +636,21 @@
return true;
}
- nnri = AwareNetworkRequestInformation.processNetworkSpecifier(networkSpecifier, mMgr);
+ nnri = AwareNetworkRequestInformation.processNetworkSpecifier(networkSpecifier, mMgr,
+ mPermissionsWrapper);
if (nnri == null) {
Log.e(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request
+ " - can't parse network specifier");
return false;
}
+
+ // TODO (b/63635780) support more then a single concurrent NDP
+ if (mNetworkRequestsCache.size() > 0) {
+ Log.e(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request
+ + " - >1 concurrent NDPs aren't supported (yet).");
+ return false;
+ }
+
mNetworkRequestsCache.put(networkSpecifier, nnri);
return true;
@@ -636,12 +689,20 @@
}
nnri.interfaceName = selectInterfaceForRequest(nnri);
- mMgr.initiateDataPathSetup(networkSpecifier, nnri.networkSpecifier.peerId,
- NanDataPathChannelCfg.REQUEST_CHANNEL_SETUP, selectChannelForRequest(nnri),
+ if (nnri.interfaceName == null) {
+ Log.w(TAG, "needNetworkFor: request " + networkSpecifier
+ + " no interface available");
+ mNetworkRequestsCache.remove(networkSpecifier);
+ return;
+ }
+
+ mMgr.initiateDataPathSetup(networkSpecifier, nnri.peerInstanceId,
+ NanDataPathChannelCfg.CHANNEL_NOT_REQUESTED, selectChannelForRequest(nnri),
nnri.peerDiscoveryMac, nnri.interfaceName, nnri.networkSpecifier.pmk,
- nnri.networkSpecifier.passphrase);
+ nnri.networkSpecifier.passphrase, nnri.networkSpecifier.isOutOfBand());
nnri.state =
AwareNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_REQUEST_RESPONSE;
+ nnri.startTimestamp = SystemClock.elapsedRealtime();
} else {
if (nnri.state != AwareNetworkRequestInformation.STATE_RESPONDER_IDLE) {
if (DBG) {
@@ -773,13 +834,14 @@
Log.e(TAG, "selectInterfaceForRequest: req=" + req + " - but no interfaces available!");
- return "";
+ return null;
}
/**
* Select a channel for the network request.
*
- * TODO: for now simply select channel 6
+ * TODO (b/38209409): The value from this function isn't currently used - the channel selection
+ * is delegated to the HAL.
*/
private int selectChannelForRequest(AwareNetworkRequestInformation req) {
return 2437;
@@ -807,16 +869,19 @@
public int uid;
public String interfaceName;
public int pubSubId = 0;
+ public int peerInstanceId = 0;
public byte[] peerDiscoveryMac = null;
public int ndpId;
public byte[] peerDataMac;
public WifiAwareNetworkSpecifier networkSpecifier;
+ public long startTimestamp = 0; // request is made (initiator) / get request (responder)
public WifiAwareNetworkAgent networkAgent;
static AwareNetworkRequestInformation processNetworkSpecifier(WifiAwareNetworkSpecifier ns,
- WifiAwareStateManager mgr) {
+ WifiAwareStateManager mgr, WifiPermissionsWrapper permissionWrapper) {
int uid, pubSubId = 0;
+ int peerInstanceId = 0;
byte[] peerMac = ns.peerMac;
if (VDBG) {
@@ -879,15 +944,17 @@
if (ns.type == WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB) {
pubSubId = session.getPubSubId();
- String peerMacStr = session.getMac(ns.peerId, null);
- if (peerMacStr == null) {
+ WifiAwareDiscoverySessionState.PeerInfo peerInfo = session.getPeerInfo(
+ ns.peerId);
+ if (peerInfo == null) {
Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns
- + " -- no MAC address associated with this peer id -- peerId="
+ + " -- no peer info associated with this peer id -- peerId="
+ ns.peerId);
return null;
}
+ peerInstanceId = peerInfo.mInstanceId;
try {
- peerMac = HexEncoding.decode(peerMacStr.toCharArray(), false);
+ peerMac = peerInfo.mMac;
if (peerMac == null || peerMac.length != 6) {
Log.e(TAG, "processNetworkSpecifier: networkSpecifier="
+ ns + " -- invalid peer MAC address");
@@ -908,6 +975,30 @@
return null;
}
+ // validate permission if PMK is used (SystemApi)
+ if (ns.pmk != null && ns.pmk.length != 0) {
+ if (permissionWrapper.getUidPermission(Manifest.permission.CONNECTIVITY_INTERNAL,
+ ns.requestorUid) != PackageManager.PERMISSION_GRANTED) {
+ Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns.toString()
+ + " -- UID doesn't have permission to use PMK API");
+ return null;
+ }
+ }
+
+ // validate passphrase & PMK (if provided)
+ if (!TextUtils.isEmpty(ns.passphrase)) { // non-null indicates usage
+ if (!WifiAwareUtils.validatePassphrase(ns.passphrase)) {
+ Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns.toString()
+ + " -- invalid passphrase length: " + ns.passphrase.length());
+ return null;
+ }
+ }
+ if (ns.pmk != null && !WifiAwareUtils.validatePmk(ns.pmk)) { // non-null indicates usage
+ Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns.toString()
+ + " -- invalid pmk length: " + ns.pmk.length);
+ return null;
+ }
+
// create container and populate
AwareNetworkRequestInformation nnri = new AwareNetworkRequestInformation();
nnri.state = (ns.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR)
@@ -915,6 +1006,7 @@
: AwareNetworkRequestInformation.STATE_RESPONDER_IDLE;
nnri.uid = uid;
nnri.pubSubId = pubSubId;
+ nnri.peerInstanceId = peerInstanceId;
nnri.peerDiscoveryMac = peerMac;
nnri.networkSpecifier = ns;
@@ -926,11 +1018,14 @@
StringBuilder sb = new StringBuilder("AwareNetworkRequestInformation: ");
sb.append("state=").append(state).append(", ns=").append(networkSpecifier).append(
", uid=").append(uid).append(", interfaceName=").append(interfaceName).append(
- ", pubSubId=").append(pubSubId).append(", peerDiscoveryMac=").append(
+ ", pubSubId=").append(pubSubId).append(", peerInstanceId=").append(
+ peerInstanceId).append(", peerDiscoveryMac=").append(
peerDiscoveryMac == null ? ""
: String.valueOf(HexEncoding.encode(peerDiscoveryMac))).append(
", ndpId=").append(ndpId).append(", peerDataMac=").append(
- peerDataMac == null ? "" : String.valueOf(HexEncoding.encode(peerDataMac)));
+ peerDataMac == null ? ""
+ : String.valueOf(HexEncoding.encode(peerDataMac))).append(
+ ", startTimestamp=").append(startTimestamp);
return sb.toString();
}
}
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareDiscoverySessionState.java b/service/java/com/android/server/wifi/aware/WifiAwareDiscoverySessionState.java
index d006aa8..86f4e37 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareDiscoverySessionState.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareDiscoverySessionState.java
@@ -28,6 +28,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Arrays;
/**
* Manages the state of a single Aware discovery session (publish or subscribe).
@@ -40,21 +41,43 @@
private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
+ private int mNextPeerIdToBeAllocated = 100; // used to create a unique peer ID
+
private final WifiAwareNativeApi mWifiAwareNativeApi;
private int mSessionId;
- private int mPubSubId;
+ private byte mPubSubId;
private IWifiAwareDiscoverySessionCallback mCallback;
private boolean mIsPublishSession;
+ private final long mCreationTime;
- private final SparseArray<String> mMacByRequestorInstanceId = new SparseArray<>();
+ static class PeerInfo {
+ PeerInfo(int instanceId, byte[] mac) {
+ mInstanceId = instanceId;
+ mMac = mac;
+ }
+
+ int mInstanceId;
+ byte[] mMac;
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("instanceId [");
+ sb.append(mInstanceId).append(", mac=").append(HexEncoding.encode(mMac)).append("]");
+ return sb.toString();
+ }
+ }
+
+ private final SparseArray<PeerInfo> mPeerInfoByRequestorInstanceId = new SparseArray<>();
public WifiAwareDiscoverySessionState(WifiAwareNativeApi wifiAwareNativeApi, int sessionId,
- int pubSubId, IWifiAwareDiscoverySessionCallback callback, boolean isPublishSession) {
+ byte pubSubId, IWifiAwareDiscoverySessionCallback callback, boolean isPublishSession,
+ long creationTime) {
mWifiAwareNativeApi = wifiAwareNativeApi;
mSessionId = sessionId;
mPubSubId = pubSubId;
mCallback = callback;
mIsPublishSession = isPublishSession;
+ mCreationTime = creationTime;
}
public int getSessionId() {
@@ -69,21 +92,20 @@
return mIsPublishSession;
}
+ public long getCreationTime() {
+ return mCreationTime;
+ }
+
public IWifiAwareDiscoverySessionCallback getCallback() {
return mCallback;
}
/**
- * Return the MAC address (String) of the specified peer ID - or a null if no such address is
+ * Return the peer information of the specified peer ID - or a null if no such peer ID is
* registered.
*/
- public String getMac(int peerId, String sep) {
- String mac = mMacByRequestorInstanceId.get(peerId);
- if (mac != null && sep != null && !sep.isEmpty()) {
- mac = new StringBuilder(mac).insert(10, sep).insert(8, sep).insert(6, sep)
- .insert(4, sep).insert(2, sep).toString();
- }
- return mac;
+ public PeerInfo getPeerInfo(int peerId) {
+ return mPeerInfoByRequestorInstanceId.get(peerId);
}
/**
@@ -183,8 +205,8 @@
* callbacks related to the message (success/failure).
*/
public boolean sendMessage(short transactionId, int peerId, byte[] message, int messageId) {
- String peerMacStr = mMacByRequestorInstanceId.get(peerId);
- if (peerMacStr == null) {
+ PeerInfo peerInfo = mPeerInfoByRequestorInstanceId.get(peerId);
+ if (peerInfo == null) {
Log.e(TAG, "sendMessage: attempting to send a message to an address which didn't "
+ "match/contact us");
try {
@@ -194,10 +216,9 @@
}
return false;
}
- byte[] peerMac = HexEncoding.decode(peerMacStr.toCharArray(), false);
- boolean success = mWifiAwareNativeApi.sendMessage(transactionId, mPubSubId, peerId, peerMac,
- message, messageId);
+ boolean success = mWifiAwareNativeApi.sendMessage(transactionId, mPubSubId,
+ peerInfo.mInstanceId, peerInfo.mMac, message, messageId);
if (!success) {
try {
mCallback.onMessageSendFail(messageId, NanStatusType.INTERNAL_FAILURE);
@@ -226,13 +247,10 @@
*/
public void onMatch(int requestorInstanceId, byte[] peerMac, byte[] serviceSpecificInfo,
byte[] matchFilter) {
- String prevMac = mMacByRequestorInstanceId.get(requestorInstanceId);
- mMacByRequestorInstanceId.put(requestorInstanceId, new String(HexEncoding.encode(peerMac)));
-
- if (DBG) Log.d(TAG, "onMatch: previous peer MAC replaced - " + prevMac);
+ int peerId = getPeerIdOrAddIfNew(requestorInstanceId, peerMac);
try {
- mCallback.onMatch(requestorInstanceId, serviceSpecificInfo, matchFilter);
+ mCallback.onMatch(peerId, serviceSpecificInfo, matchFilter);
} catch (RemoteException e) {
Log.w(TAG, "onMatch: RemoteException (FYI): " + e);
}
@@ -249,20 +267,35 @@
* @param message The received message.
*/
public void onMessageReceived(int requestorInstanceId, byte[] peerMac, byte[] message) {
- String prevMac = mMacByRequestorInstanceId.get(requestorInstanceId);
- mMacByRequestorInstanceId.put(requestorInstanceId, new String(HexEncoding.encode(peerMac)));
-
- if (DBG) {
- Log.d(TAG, "onMessageReceived: previous peer MAC replaced - " + prevMac);
- }
+ int peerId = getPeerIdOrAddIfNew(requestorInstanceId, peerMac);
try {
- mCallback.onMessageReceived(requestorInstanceId, message);
+ mCallback.onMessageReceived(peerId, message);
} catch (RemoteException e) {
Log.w(TAG, "onMessageReceived: RemoteException (FYI): " + e);
}
}
+ private int getPeerIdOrAddIfNew(int requestorInstanceId, byte[] peerMac) {
+ for (int i = 0; i < mPeerInfoByRequestorInstanceId.size(); ++i) {
+ PeerInfo peerInfo = mPeerInfoByRequestorInstanceId.valueAt(i);
+ if (peerInfo.mInstanceId == requestorInstanceId && Arrays.equals(peerMac,
+ peerInfo.mMac)) {
+ return mPeerInfoByRequestorInstanceId.keyAt(i);
+ }
+ }
+
+ int newPeerId = mNextPeerIdToBeAllocated++;
+ PeerInfo newPeerInfo = new PeerInfo(requestorInstanceId, peerMac);
+ mPeerInfoByRequestorInstanceId.put(newPeerId, newPeerInfo);
+
+ if (DBG) {
+ Log.d(TAG, "New peer info: peerId=" + newPeerId + ", peerInfo=" + newPeerInfo);
+ }
+
+ return newPeerId;
+ }
+
/**
* Dump the internal state of the class.
*/
@@ -271,6 +304,6 @@
pw.println(" mSessionId: " + mSessionId);
pw.println(" mIsPublishSession: " + mIsPublishSession);
pw.println(" mPubSubId: " + mPubSubId);
- pw.println(" mMacByRequestorInstanceId: [" + mMacByRequestorInstanceId + "]");
+ pw.println(" mPeerInfoByRequestorInstanceId: [" + mPeerInfoByRequestorInstanceId + "]");
}
}
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareMetrics.java b/service/java/com/android/server/wifi/aware/WifiAwareMetrics.java
new file mode 100644
index 0000000..02eaf5d
--- /dev/null
+++ b/service/java/com/android/server/wifi/aware/WifiAwareMetrics.java
@@ -0,0 +1,829 @@
+/*
+ * Copyright (C) 2017 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.aware;
+
+import android.hardware.wifi.V1_0.NanStatusType;
+import android.net.wifi.aware.WifiAwareNetworkSpecifier;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.Clock;
+import com.android.server.wifi.nano.WifiMetricsProto;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Wi-Fi Aware metric container/processor.
+ */
+public class WifiAwareMetrics {
+ private static final String TAG = "WifiAwareMetrics";
+ private static final boolean DBG = false;
+
+ // Histogram: 8 buckets (i=0, ..., 7) of 9 slots in range 10^i -> 10^(i+1)
+ // Buckets:
+ // 1 -> 10: 9 @ 1
+ // 10 -> 100: 9 @ 10
+ // 100 -> 1000: 9 @ 10^2
+ // 10^3 -> 10^4: 9 @ 10^3
+ // 10^4 -> 10^5: 9 @ 10^4
+ // 10^5 -> 10^6: 9 @ 10^5
+ // 10^6 -> 10^7: 9 @ 10^6
+ // 10^7 -> 10^8: 9 @ 10^7 --> 10^8 ms -> 10^5s -> 28 hours
+ private static final HistParms DURATION_LOG_HISTOGRAM = new HistParms(0, 1, 10, 9, 8);
+
+ private final Object mLock = new Object();
+ private final Clock mClock;
+
+ // enableUsage/disableUsage data
+ private long mLastEnableUsageMs = 0;
+ private long mLastEnableUsageInThisSampleWindowMs = 0;
+ private long mAvailableTimeMs = 0;
+ private SparseIntArray mHistogramAwareAvailableDurationMs = new SparseIntArray();
+
+ // enabled data
+ private long mLastEnableAwareMs = 0;
+ private long mLastEnableAwareInThisSampleWindowMs = 0;
+ private long mEnabledTimeMs = 0;
+ private SparseIntArray mHistogramAwareEnabledDurationMs = new SparseIntArray();
+
+ // attach data
+ private static class AttachData {
+ boolean mUsesIdentityCallback; // do any attach sessions of the UID use identity callback
+ int mMaxConcurrentAttaches;
+ }
+ private Map<Integer, AttachData> mAttachDataByUid = new HashMap<>();
+ private SparseIntArray mAttachStatusData = new SparseIntArray();
+ private SparseIntArray mHistogramAttachDuration = new SparseIntArray();
+
+ // discovery data
+ private int mMaxPublishInApp = 0;
+ private int mMaxSubscribeInApp = 0;
+ private int mMaxDiscoveryInApp = 0;
+ private int mMaxPublishInSystem = 0;
+ private int mMaxSubscribeInSystem = 0;
+ private int mMaxDiscoveryInSystem = 0;
+ private SparseIntArray mPublishStatusData = new SparseIntArray();
+ private SparseIntArray mSubscribeStatusData = new SparseIntArray();
+ private SparseIntArray mHistogramPublishDuration = new SparseIntArray();
+ private SparseIntArray mHistogramSubscribeDuration = new SparseIntArray();
+ private Set<Integer> mAppsWithDiscoverySessionResourceFailure = new HashSet<>();
+
+ // data-path (NDI/NDP) data
+ private int mMaxNdiInApp = 0;
+ private int mMaxNdpInApp = 0;
+ private int mMaxSecureNdpInApp = 0;
+ private int mMaxNdiInSystem = 0;
+ private int mMaxNdpInSystem = 0;
+ private int mMaxSecureNdpInSystem = 0;
+ private int mMaxNdpPerNdi = 0;
+ private SparseIntArray mInBandNdpStatusData = new SparseIntArray();
+ private SparseIntArray mOutOfBandNdpStatusData = new SparseIntArray();
+
+ private SparseIntArray mNdpCreationTimeDuration = new SparseIntArray();
+ private long mNdpCreationTimeMin = -1;
+ private long mNdpCreationTimeMax = 0;
+ private long mNdpCreationTimeSum = 0;
+ private long mNdpCreationTimeSumSq = 0;
+ private long mNdpCreationTimeNumSamples = 0;
+
+ private SparseIntArray mHistogramNdpDuration = new SparseIntArray();
+
+ public WifiAwareMetrics(Clock clock) {
+ mClock = clock;
+ }
+
+ /**
+ * Push usage stats for WifiAwareStateMachine.enableUsage() to
+ * histogram_aware_available_duration_ms.
+ */
+ public void recordEnableUsage() {
+ synchronized (mLock) {
+ if (mLastEnableUsageMs != 0) {
+ Log.w(TAG, "enableUsage: mLastEnableUsage*Ms initialized!?");
+ }
+ mLastEnableUsageMs = mClock.getElapsedSinceBootMillis();
+ mLastEnableUsageInThisSampleWindowMs = mLastEnableUsageMs;
+ }
+ }
+
+ /**
+ * Push usage stats for WifiAwareStateMachine.disableUsage() to
+ * histogram_aware_available_duration_ms.
+ */
+
+ public void recordDisableUsage() {
+ synchronized (mLock) {
+ if (mLastEnableUsageMs == 0) {
+ Log.e(TAG, "disableUsage: mLastEnableUsage not initialized!?");
+ return;
+ }
+
+ long now = mClock.getElapsedSinceBootMillis();
+ addLogValueToHistogram(now - mLastEnableUsageMs, mHistogramAwareAvailableDurationMs,
+ DURATION_LOG_HISTOGRAM);
+ mAvailableTimeMs += now - mLastEnableUsageInThisSampleWindowMs;
+ mLastEnableUsageMs = 0;
+ mLastEnableUsageInThisSampleWindowMs = 0;
+ }
+ }
+
+ /**
+ * Push usage stats of Aware actually being enabled on-the-air: start
+ */
+ public void recordEnableAware() {
+ synchronized (mLock) {
+ if (mLastEnableAwareMs != 0) {
+ return; // already enabled
+ }
+ mLastEnableAwareMs = mClock.getElapsedSinceBootMillis();
+ mLastEnableAwareInThisSampleWindowMs = mLastEnableAwareMs;
+ }
+ }
+
+ /**
+ * Push usage stats of Aware actually being enabled on-the-air: stop (disable)
+ */
+ public void recordDisableAware() {
+ synchronized (mLock) {
+ if (mLastEnableAwareMs == 0) {
+ return; // already disabled
+ }
+
+ long now = mClock.getElapsedSinceBootMillis();
+ addLogValueToHistogram(now - mLastEnableAwareMs, mHistogramAwareEnabledDurationMs,
+ DURATION_LOG_HISTOGRAM);
+ mEnabledTimeMs += now - mLastEnableAwareInThisSampleWindowMs;
+ mLastEnableAwareMs = 0;
+ mLastEnableAwareInThisSampleWindowMs = 0;
+ }
+ }
+
+ /**
+ * Push information about a new attach session.
+ */
+ public void recordAttachSession(int uid, boolean usesIdentityCallback,
+ SparseArray<WifiAwareClientState> clients) {
+ // count the number of clients with the specific uid
+ int currentConcurrentCount = 0;
+ for (int i = 0; i < clients.size(); ++i) {
+ if (clients.valueAt(i).getUid() == uid) {
+ ++currentConcurrentCount;
+ }
+ }
+
+ synchronized (mLock) {
+ AttachData data = mAttachDataByUid.get(uid);
+ if (data == null) {
+ data = new AttachData();
+ mAttachDataByUid.put(uid, data);
+ }
+ data.mUsesIdentityCallback |= usesIdentityCallback;
+ data.mMaxConcurrentAttaches = Math.max(data.mMaxConcurrentAttaches,
+ currentConcurrentCount);
+ recordAttachStatus(NanStatusType.SUCCESS);
+ }
+ }
+
+ /**
+ * Push information about a new attach session status (recorded when attach session is created).
+ */
+ public void recordAttachStatus(int status) {
+ synchronized (mLock) {
+ mAttachStatusData.put(status, mAttachStatusData.get(status) + 1);
+ }
+ }
+
+ /**
+ * Push duration information of an attach session.
+ */
+ public void recordAttachSessionDuration(long creationTime) {
+ synchronized (mLock) {
+ addLogValueToHistogram(mClock.getElapsedSinceBootMillis() - creationTime,
+ mHistogramAttachDuration,
+ DURATION_LOG_HISTOGRAM);
+ }
+ }
+
+ /**
+ * Push information about the new discovery session.
+ */
+ public void recordDiscoverySession(int uid, boolean isPublish,
+ SparseArray<WifiAwareClientState> clients) {
+ // count the number of sessions per uid and overall
+ int numPublishesInSystem = 0;
+ int numSubscribesInSystem = 0;
+ int numPublishesOnUid = 0;
+ int numSubscribesOnUid = 0;
+
+ for (int i = 0; i < clients.size(); ++i) {
+ WifiAwareClientState client = clients.valueAt(i);
+ boolean sameUid = client.getUid() == uid;
+
+ SparseArray<WifiAwareDiscoverySessionState> sessions = client.getSessions();
+ for (int j = 0; j < sessions.size(); ++j) {
+ WifiAwareDiscoverySessionState session = sessions.valueAt(j);
+
+ if (session.isPublishSession()) {
+ numPublishesInSystem += 1;
+ if (sameUid) {
+ numPublishesOnUid += 1;
+ }
+ } else {
+ numSubscribesInSystem += 1;
+ if (sameUid) {
+ numSubscribesOnUid += 1;
+ }
+ }
+ }
+ }
+
+ synchronized (mLock) {
+ mMaxPublishInApp = Math.max(mMaxPublishInApp, numPublishesOnUid);
+ mMaxSubscribeInApp = Math.max(mMaxSubscribeInApp, numSubscribesOnUid);
+ mMaxDiscoveryInApp = Math.max(mMaxDiscoveryInApp,
+ numPublishesOnUid + numSubscribesOnUid);
+ mMaxPublishInSystem = Math.max(mMaxPublishInSystem, numPublishesInSystem);
+ mMaxSubscribeInSystem = Math.max(mMaxSubscribeInSystem, numSubscribesInSystem);
+ mMaxDiscoveryInSystem = Math.max(mMaxDiscoveryInSystem,
+ numPublishesInSystem + numSubscribesInSystem);
+ }
+ }
+
+ /**
+ * Push information about a new discovery session status (recorded when the discovery session is
+ * created).
+ */
+ public void recordDiscoveryStatus(int uid, int status, boolean isPublish) {
+ synchronized (mLock) {
+ if (isPublish) {
+ mPublishStatusData.put(status, mPublishStatusData.get(status) + 1);
+ } else {
+ mSubscribeStatusData.put(status, mSubscribeStatusData.get(status) + 1);
+ }
+
+ if (status == NanStatusType.NO_RESOURCES_AVAILABLE) {
+ mAppsWithDiscoverySessionResourceFailure.add(uid);
+ }
+ }
+ }
+
+ /**
+ * Push duration information of a discovery session.
+ */
+ public void recordDiscoverySessionDuration(long creationTime, boolean isPublish) {
+ synchronized (mLock) {
+ addLogValueToHistogram(mClock.getElapsedSinceBootMillis() - creationTime,
+ isPublish ? mHistogramPublishDuration : mHistogramSubscribeDuration,
+ DURATION_LOG_HISTOGRAM);
+ }
+ }
+
+ /**
+ * Record NDP (and by extension NDI) usage - on successful creation of an NDP.
+ */
+ public void recordNdpCreation(int uid,
+ Map<WifiAwareNetworkSpecifier, WifiAwareDataPathStateManager
+ .AwareNetworkRequestInformation> networkRequestCache) {
+ int numNdpInApp = 0;
+ int numSecureNdpInApp = 0;
+ int numNdpInSystem = 0;
+ int numSecureNdpInSystem = 0;
+
+ Map<String, Integer> ndpPerNdiMap = new HashMap<>();
+ Set<String> ndiInApp = new HashSet<>();
+ Set<String> ndiInSystem = new HashSet<>();
+
+ for (WifiAwareDataPathStateManager.AwareNetworkRequestInformation anri :
+ networkRequestCache.values()) {
+ if (anri.state
+ != WifiAwareDataPathStateManager.AwareNetworkRequestInformation
+ .STATE_INITIATOR_CONFIRMED
+ && anri.state
+ != WifiAwareDataPathStateManager.AwareNetworkRequestInformation
+ .STATE_RESPONDER_CONFIRMED) {
+ continue; // only count completed (up-and-running) NDPs
+ }
+
+ boolean sameUid = anri.uid == uid;
+ boolean isSecure = !TextUtils.isEmpty(anri.networkSpecifier.passphrase) || (
+ anri.networkSpecifier.pmk != null && anri.networkSpecifier.pmk.length != 0);
+
+ // in-app stats
+ if (sameUid) {
+ numNdpInApp += 1;
+ if (isSecure) {
+ numSecureNdpInApp += 1;
+ }
+
+ ndiInApp.add(anri.interfaceName);
+ }
+
+ // system stats
+ numNdpInSystem += 1;
+ if (isSecure) {
+ numSecureNdpInSystem += 1;
+ }
+
+ // ndp/ndi stats
+ Integer ndpCount = ndpPerNdiMap.get(anri.interfaceName);
+ if (ndpCount == null) {
+ ndpPerNdiMap.put(anri.interfaceName, 1);
+ } else {
+ ndpPerNdiMap.put(anri.interfaceName, ndpCount + 1);
+ }
+
+ // ndi stats
+ ndiInSystem.add(anri.interfaceName);
+ }
+
+ synchronized (mLock) {
+ mMaxNdiInApp = Math.max(mMaxNdiInApp, ndiInApp.size());
+ mMaxNdpInApp = Math.max(mMaxNdpInApp, numNdpInApp);
+ mMaxSecureNdpInApp = Math.max(mMaxSecureNdpInApp, numSecureNdpInApp);
+ mMaxNdiInSystem = Math.max(mMaxNdiInSystem, ndiInSystem.size());
+ mMaxNdpInSystem = Math.max(mMaxNdpInSystem, numNdpInSystem);
+ mMaxSecureNdpInSystem = Math.max(mMaxSecureNdpInSystem, numSecureNdpInSystem);
+ mMaxNdpPerNdi = Math.max(mMaxNdpPerNdi, Collections.max(ndpPerNdiMap.values()));
+ }
+ }
+
+ /**
+ * Record the completion status of NDP negotiation. There are multiple steps in NDP negotiation
+ * a failure on any aborts the process and is recorded. A success on intermediate stages is
+ * not recorded - only the final success.
+ */
+ public void recordNdpStatus(int status, boolean isOutOfBand, long startTimestamp) {
+ synchronized (mLock) {
+ if (isOutOfBand) {
+ mOutOfBandNdpStatusData.put(status, mOutOfBandNdpStatusData.get(status) + 1);
+ } else {
+ mInBandNdpStatusData.put(status, mOutOfBandNdpStatusData.get(status) + 1);
+ }
+
+ if (status == NanStatusType.SUCCESS) {
+ long creationTime = mClock.getElapsedSinceBootMillis() - startTimestamp;
+ addLogValueToHistogram(creationTime, mNdpCreationTimeDuration,
+ DURATION_LOG_HISTOGRAM);
+ mNdpCreationTimeMin = (mNdpCreationTimeMin == -1) ? creationTime : Math.min(
+ mNdpCreationTimeMin, creationTime);
+ mNdpCreationTimeMax = Math.max(mNdpCreationTimeMax, creationTime);
+ mNdpCreationTimeSum += creationTime;
+ mNdpCreationTimeSumSq += creationTime * creationTime;
+ mNdpCreationTimeNumSamples += 1;
+ }
+ }
+ }
+
+ /**
+ * Record the duration of the NDP session. The creation time is assumed to be the time at
+ * which a confirm message was received (i.e. the end of the setup negotiation).
+ */
+ public void recordNdpSessionDuration(long creationTime) {
+ synchronized (mLock) {
+ addLogValueToHistogram(mClock.getElapsedSinceBootMillis() - creationTime,
+ mHistogramNdpDuration, DURATION_LOG_HISTOGRAM);
+ }
+ }
+
+ /**
+ * Consolidate all metrics into the proto.
+ */
+ public WifiMetricsProto.WifiAwareLog consolidateProto() {
+ WifiMetricsProto.WifiAwareLog log = new WifiMetricsProto.WifiAwareLog();
+ long now = mClock.getElapsedSinceBootMillis();
+ synchronized (mLock) {
+ log.histogramAwareAvailableDurationMs = histogramToProtoArray(
+ mHistogramAwareAvailableDurationMs, DURATION_LOG_HISTOGRAM);
+ log.availableTimeMs = mAvailableTimeMs;
+ if (mLastEnableUsageInThisSampleWindowMs != 0) {
+ log.availableTimeMs += now - mLastEnableUsageInThisSampleWindowMs;
+ }
+
+ log.histogramAwareEnabledDurationMs = histogramToProtoArray(
+ mHistogramAwareEnabledDurationMs, DURATION_LOG_HISTOGRAM);
+ log.enabledTimeMs = mEnabledTimeMs;
+ if (mLastEnableAwareInThisSampleWindowMs != 0) {
+ log.enabledTimeMs += now - mLastEnableAwareInThisSampleWindowMs;
+ }
+
+ log.numApps = mAttachDataByUid.size();
+ log.numAppsUsingIdentityCallback = 0;
+ log.maxConcurrentAttachSessionsInApp = 0;
+ for (AttachData ad: mAttachDataByUid.values()) {
+ if (ad.mUsesIdentityCallback) {
+ ++log.numAppsUsingIdentityCallback;
+ }
+ log.maxConcurrentAttachSessionsInApp = Math.max(
+ log.maxConcurrentAttachSessionsInApp, ad.mMaxConcurrentAttaches);
+ }
+ log.histogramAttachSessionStatus = histogramToProtoArray(mAttachStatusData);
+ log.histogramAttachDurationMs = histogramToProtoArray(mHistogramAttachDuration,
+ DURATION_LOG_HISTOGRAM);
+
+ log.maxConcurrentPublishInApp = mMaxPublishInApp;
+ log.maxConcurrentSubscribeInApp = mMaxSubscribeInApp;
+ log.maxConcurrentDiscoverySessionsInApp = mMaxDiscoveryInApp;
+ log.maxConcurrentPublishInSystem = mMaxPublishInSystem;
+ log.maxConcurrentSubscribeInSystem = mMaxSubscribeInSystem;
+ log.maxConcurrentDiscoverySessionsInSystem = mMaxDiscoveryInSystem;
+ log.histogramPublishStatus = histogramToProtoArray(mPublishStatusData);
+ log.histogramSubscribeStatus = histogramToProtoArray(mSubscribeStatusData);
+ log.numAppsWithDiscoverySessionFailureOutOfResources =
+ mAppsWithDiscoverySessionResourceFailure.size();
+ log.histogramPublishSessionDurationMs = histogramToProtoArray(mHistogramPublishDuration,
+ DURATION_LOG_HISTOGRAM);
+ log.histogramSubscribeSessionDurationMs = histogramToProtoArray(
+ mHistogramSubscribeDuration, DURATION_LOG_HISTOGRAM);
+
+ log.maxConcurrentNdiInApp = mMaxNdiInApp;
+ log.maxConcurrentNdiInSystem = mMaxNdiInSystem;
+ log.maxConcurrentNdpInApp = mMaxNdpInApp;
+ log.maxConcurrentNdpInSystem = mMaxNdpInSystem;
+ log.maxConcurrentSecureNdpInApp = mMaxSecureNdpInApp;
+ log.maxConcurrentSecureNdpInSystem = mMaxSecureNdpInSystem;
+ log.maxConcurrentNdpPerNdi = mMaxNdpPerNdi;
+ log.histogramRequestNdpStatus = histogramToProtoArray(mInBandNdpStatusData);
+ log.histogramRequestNdpOobStatus = histogramToProtoArray(mOutOfBandNdpStatusData);
+
+ log.histogramNdpCreationTimeMs = histogramToProtoArray(mNdpCreationTimeDuration,
+ DURATION_LOG_HISTOGRAM);
+ log.ndpCreationTimeMsMin = mNdpCreationTimeMin;
+ log.ndpCreationTimeMsMax = mNdpCreationTimeMax;
+ log.ndpCreationTimeMsSum = mNdpCreationTimeSum;
+ log.ndpCreationTimeMsSumOfSq = mNdpCreationTimeSumSq;
+ log.ndpCreationTimeMsNumSamples = mNdpCreationTimeNumSamples;
+
+ log.histogramNdpSessionDurationMs = histogramToProtoArray(mHistogramNdpDuration,
+ DURATION_LOG_HISTOGRAM);
+ }
+ return log;
+ }
+
+ /**
+ * clear Wi-Fi Aware metrics
+ */
+ public void clear() {
+ long now = mClock.getElapsedSinceBootMillis();
+ synchronized (mLock) {
+ // don't clear mLastEnableUsage since could be valid for next measurement period
+ mHistogramAwareAvailableDurationMs.clear();
+ mAvailableTimeMs = 0;
+ if (mLastEnableUsageInThisSampleWindowMs != 0) {
+ mLastEnableUsageInThisSampleWindowMs = now;
+ }
+
+ // don't clear mLastEnableAware since could be valid for next measurement period
+ mHistogramAwareEnabledDurationMs.clear();
+ mEnabledTimeMs = 0;
+ if (mLastEnableAwareInThisSampleWindowMs != 0) {
+ mLastEnableAwareInThisSampleWindowMs = now;
+ }
+
+ mAttachDataByUid.clear();
+ mAttachStatusData.clear();
+ mHistogramAttachDuration.clear();
+
+ mMaxPublishInApp = 0;
+ mMaxSubscribeInApp = 0;
+ mMaxDiscoveryInApp = 0;
+ mMaxPublishInSystem = 0;
+ mMaxSubscribeInSystem = 0;
+ mMaxDiscoveryInSystem = 0;
+ mPublishStatusData.clear();
+ mSubscribeStatusData.clear();
+ mHistogramPublishDuration.clear();
+ mHistogramSubscribeDuration.clear();
+ mAppsWithDiscoverySessionResourceFailure.clear();
+
+ mMaxNdiInApp = 0;
+ mMaxNdpInApp = 0;
+ mMaxSecureNdpInApp = 0;
+ mMaxNdiInSystem = 0;
+ mMaxNdpInSystem = 0;
+ mMaxSecureNdpInSystem = 0;
+ mMaxNdpPerNdi = 0;
+ mInBandNdpStatusData.clear();
+ mOutOfBandNdpStatusData.clear();
+
+ mNdpCreationTimeDuration.clear();
+ mNdpCreationTimeMin = -1;
+ mNdpCreationTimeMax = 0;
+ mNdpCreationTimeSum = 0;
+ mNdpCreationTimeSumSq = 0;
+ mNdpCreationTimeNumSamples = 0;
+
+ mHistogramNdpDuration.clear();
+ }
+ }
+
+ /**
+ * Dump all WifiAwareMetrics to console (pw) - this method is never called to dump the
+ * serialized metrics (handled by parent WifiMetrics).
+ *
+ * @param fd unused
+ * @param pw PrintWriter for writing dump to
+ * @param args unused
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ synchronized (mLock) {
+ pw.println("mLastEnableUsageMs:" + mLastEnableUsageMs);
+ pw.println(
+ "mLastEnableUsageInThisSampleWindowMs:" + mLastEnableUsageInThisSampleWindowMs);
+ pw.println("mAvailableTimeMs:" + mAvailableTimeMs);
+ pw.println("mHistogramAwareAvailableDurationMs:");
+ for (int i = 0; i < mHistogramAwareAvailableDurationMs.size(); ++i) {
+ pw.println(" " + mHistogramAwareAvailableDurationMs.keyAt(i) + ": "
+ + mHistogramAwareAvailableDurationMs.valueAt(i));
+ }
+
+ pw.println("mLastEnableAwareMs:" + mLastEnableAwareMs);
+ pw.println(
+ "mLastEnableAwareInThisSampleWindowMs:" + mLastEnableAwareInThisSampleWindowMs);
+ pw.println("mEnabledTimeMs:" + mEnabledTimeMs);
+ pw.println("mHistogramAwareEnabledDurationMs:");
+ for (int i = 0; i < mHistogramAwareEnabledDurationMs.size(); ++i) {
+ pw.println(" " + mHistogramAwareEnabledDurationMs.keyAt(i) + ": "
+ + mHistogramAwareEnabledDurationMs.valueAt(i));
+ }
+
+ pw.println("mAttachDataByUid:");
+ for (Map.Entry<Integer, AttachData> ade: mAttachDataByUid.entrySet()) {
+ pw.println(" " + "uid=" + ade.getKey() + ": identity="
+ + ade.getValue().mUsesIdentityCallback + ", maxConcurrent="
+ + ade.getValue().mMaxConcurrentAttaches);
+ }
+ pw.println("mAttachStatusData:");
+ for (int i = 0; i < mAttachStatusData.size(); ++i) {
+ pw.println(" " + mAttachStatusData.keyAt(i) + ": "
+ + mAttachStatusData.valueAt(i));
+ }
+ pw.println("mHistogramAttachDuration:");
+ for (int i = 0; i < mHistogramAttachDuration.size(); ++i) {
+ pw.println(" " + mHistogramAttachDuration.keyAt(i) + ": "
+ + mHistogramAttachDuration.valueAt(i));
+ }
+
+ pw.println("mMaxPublishInApp:" + mMaxPublishInApp);
+ pw.println("mMaxSubscribeInApp:" + mMaxSubscribeInApp);
+ pw.println("mMaxDiscoveryInApp:" + mMaxDiscoveryInApp);
+ pw.println("mMaxPublishInSystem:" + mMaxPublishInSystem);
+ pw.println("mMaxSubscribeInSystem:" + mMaxSubscribeInSystem);
+ pw.println("mMaxDiscoveryInSystem:" + mMaxDiscoveryInSystem);
+ pw.println("mPublishStatusData:");
+ for (int i = 0; i < mPublishStatusData.size(); ++i) {
+ pw.println(" " + mPublishStatusData.keyAt(i) + ": "
+ + mPublishStatusData.valueAt(i));
+ }
+ pw.println("mSubscribeStatusData:");
+ for (int i = 0; i < mSubscribeStatusData.size(); ++i) {
+ pw.println(" " + mSubscribeStatusData.keyAt(i) + ": "
+ + mSubscribeStatusData.valueAt(i));
+ }
+ pw.println("mHistogramPublishDuration:");
+ for (int i = 0; i < mHistogramPublishDuration.size(); ++i) {
+ pw.println(" " + mHistogramPublishDuration.keyAt(i) + ": "
+ + mHistogramPublishDuration.valueAt(i));
+ }
+ pw.println("mHistogramSubscribeDuration:");
+ for (int i = 0; i < mHistogramSubscribeDuration.size(); ++i) {
+ pw.println(" " + mHistogramSubscribeDuration.keyAt(i) + ": "
+ + mHistogramSubscribeDuration.valueAt(i));
+ }
+ pw.println("mAppsWithDiscoverySessionResourceFailure:");
+ for (Integer uid: mAppsWithDiscoverySessionResourceFailure) {
+ pw.println(" " + uid);
+ }
+
+ pw.println("mMaxNdiInApp:" + mMaxNdiInApp);
+ pw.println("mMaxNdpInApp:" + mMaxNdpInApp);
+ pw.println("mMaxSecureNdpInApp:" + mMaxSecureNdpInApp);
+ pw.println("mMaxNdiInSystem:" + mMaxNdiInSystem);
+ pw.println("mMaxNdpInSystem:" + mMaxNdpInSystem);
+ pw.println("mMaxSecureNdpInSystem:" + mMaxSecureNdpInSystem);
+ pw.println("mMaxNdpPerNdi:" + mMaxNdpPerNdi);
+ pw.println("mInBandNdpStatusData:");
+ for (int i = 0; i < mInBandNdpStatusData.size(); ++i) {
+ pw.println(" " + mInBandNdpStatusData.keyAt(i) + ": "
+ + mInBandNdpStatusData.valueAt(i));
+ }
+ pw.println("mOutOfBandNdpStatusData:");
+ for (int i = 0; i < mOutOfBandNdpStatusData.size(); ++i) {
+ pw.println(" " + mOutOfBandNdpStatusData.keyAt(i) + ": "
+ + mOutOfBandNdpStatusData.valueAt(i));
+ }
+
+ pw.println("mNdpCreationTimeDuration:");
+ for (int i = 0; i < mNdpCreationTimeDuration.size(); ++i) {
+ pw.println(" " + mNdpCreationTimeDuration.keyAt(i) + ": "
+ + mNdpCreationTimeDuration.valueAt(i));
+ }
+ pw.println("mNdpCreationTimeMin:" + mNdpCreationTimeMin);
+ pw.println("mNdpCreationTimeMax:" + mNdpCreationTimeMax);
+ pw.println("mNdpCreationTimeSum:" + mNdpCreationTimeSum);
+ pw.println("mNdpCreationTimeSumSq:" + mNdpCreationTimeSumSq);
+ pw.println("mNdpCreationTimeNumSamples:" + mNdpCreationTimeNumSamples);
+
+ pw.println("mHistogramNdpDuration:");
+ for (int i = 0; i < mHistogramNdpDuration.size(); ++i) {
+ pw.println(" " + mHistogramNdpDuration.keyAt(i) + ": "
+ + mHistogramNdpDuration.valueAt(i));
+ }
+ }
+ }
+
+ // histogram utilities
+
+ /**
+ * Specifies a ~log histogram consisting of two levels of buckets - a set of N big buckets:
+ *
+ * Buckets starts at: B + P * M^i, where i=0, ... , N-1 (N big buckets)
+ * Each big bucket is divided into S sub-buckets
+ *
+ * Each (big) bucket is M times bigger than the previous one.
+ *
+ * The buckets are then:
+ * #0: B + P * M^0 with S buckets each of width (P*M^1-P*M^0)/S
+ * #1: B + P * M^1 with S buckets each of width (P*M^2-P*M^1)/S
+ * ...
+ * #N-1: B + P * M^(N-1) with S buckets each of width (P*M^N-P*M^(N-1))/S
+ */
+ @VisibleForTesting
+ public static class HistParms {
+ public HistParms(int b, int p, int m, int s, int n) {
+ this.b = b;
+ this.p = p;
+ this.m = m;
+ this.s = s;
+ this.n = n;
+
+ // derived values
+ mLog = Math.log(m);
+ bb = new double[n];
+ sbw = new double[n];
+ bb[0] = b + p;
+ sbw[0] = p * (m - 1.0) / (double) s;
+ for (int i = 1; i < n; ++i) {
+ bb[i] = m * (bb[i - 1] - b) + b;
+ sbw[i] = m * sbw[i - 1];
+ }
+ }
+
+ // spec
+ public int b;
+ public int p;
+ public int m;
+ public int s;
+ public int n;
+
+ // derived
+ public double mLog;
+ public double[] bb; // bucket base
+ public double[] sbw; // sub-bucket width
+ }
+
+ /**
+ * Adds the input value to the histogram based on the histogram parameters.
+ */
+ @VisibleForTesting
+ public static int addLogValueToHistogram(long x, SparseIntArray histogram, HistParms hp) {
+ double logArg = (double) (x - hp.b) / (double) hp.p;
+ int bigBucketIndex = -1;
+ if (logArg > 0) {
+ bigBucketIndex = (int) (Math.log(logArg) / hp.mLog);
+ }
+ int subBucketIndex;
+ if (bigBucketIndex < 0) {
+ bigBucketIndex = 0;
+ subBucketIndex = 0;
+ } else if (bigBucketIndex >= hp.n) {
+ bigBucketIndex = hp.n - 1;
+ subBucketIndex = hp.s - 1;
+ } else {
+ subBucketIndex = (int) ((x - hp.bb[bigBucketIndex]) / hp.sbw[bigBucketIndex]);
+ if (subBucketIndex >= hp.s) { // probably a rounding error so move to next big bucket
+ bigBucketIndex++;
+ if (bigBucketIndex >= hp.n) {
+ bigBucketIndex = hp.n - 1;
+ subBucketIndex = hp.s - 1;
+ } else {
+ subBucketIndex = (int) ((x - hp.bb[bigBucketIndex]) / hp.sbw[bigBucketIndex]);
+ }
+ }
+ }
+ int key = bigBucketIndex * hp.s + subBucketIndex;
+
+ // note that get() returns 0 if index not there already
+ int newValue = histogram.get(key) + 1;
+ histogram.put(key, newValue);
+
+ return newValue;
+ }
+
+ /**
+ * Converts the histogram (with the specified histogram parameters) to an array of proto
+ * histogram buckets.
+ */
+ @VisibleForTesting
+ public static WifiMetricsProto.WifiAwareLog.HistogramBucket[] histogramToProtoArray(
+ SparseIntArray histogram, HistParms hp) {
+ WifiMetricsProto.WifiAwareLog.HistogramBucket[] protoArray =
+ new WifiMetricsProto.WifiAwareLog.HistogramBucket[histogram.size()];
+ for (int i = 0; i < histogram.size(); ++i) {
+ int key = histogram.keyAt(i);
+
+ protoArray[i] = new WifiMetricsProto.WifiAwareLog.HistogramBucket();
+ protoArray[i].start = (long) (hp.bb[key / hp.s] + hp.sbw[key / hp.s] * (key % hp.s));
+ protoArray[i].end = (long) (protoArray[i].start + hp.sbw[key / hp.s]);
+ protoArray[i].count = histogram.valueAt(i);
+ }
+
+ return protoArray;
+ }
+
+ /**
+ * Adds the NanStatusType to the histogram (translating to the proto enumeration of the status).
+ */
+ public static void addNanHalStatusToHistogram(int halStatus, SparseIntArray histogram) {
+ int protoStatus = convertNanStatusTypeToProtoEnum(halStatus);
+ int newValue = histogram.get(protoStatus) + 1;
+ histogram.put(protoStatus, newValue);
+ }
+
+ /**
+ * Converts a histogram of proto NanStatusTypeEnum to a raw proto histogram.
+ */
+ @VisibleForTesting
+ public static WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket[] histogramToProtoArray(
+ SparseIntArray histogram) {
+ WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket[] protoArray =
+ new WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket[histogram.size()];
+
+ for (int i = 0; i < histogram.size(); ++i) {
+ protoArray[i] = new WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket();
+ protoArray[i].nanStatusType = histogram.keyAt(i);
+ protoArray[i].count = histogram.valueAt(i);
+ }
+
+ return protoArray;
+ }
+
+ /**
+ * Convert a HAL NanStatusType enum to a Metrics proto enum NanStatusTypeEnum.
+ */
+ public static int convertNanStatusTypeToProtoEnum(int nanStatusType) {
+ switch (nanStatusType) {
+ case NanStatusType.SUCCESS:
+ return WifiMetricsProto.WifiAwareLog.SUCCESS;
+ case NanStatusType.INTERNAL_FAILURE:
+ return WifiMetricsProto.WifiAwareLog.INTERNAL_FAILURE;
+ case NanStatusType.PROTOCOL_FAILURE:
+ return WifiMetricsProto.WifiAwareLog.PROTOCOL_FAILURE;
+ case NanStatusType.INVALID_SESSION_ID:
+ return WifiMetricsProto.WifiAwareLog.INVALID_SESSION_ID;
+ case NanStatusType.NO_RESOURCES_AVAILABLE:
+ return WifiMetricsProto.WifiAwareLog.NO_RESOURCES_AVAILABLE;
+ case NanStatusType.INVALID_ARGS:
+ return WifiMetricsProto.WifiAwareLog.INVALID_ARGS;
+ case NanStatusType.INVALID_PEER_ID:
+ return WifiMetricsProto.WifiAwareLog.INVALID_PEER_ID;
+ case NanStatusType.INVALID_NDP_ID:
+ return WifiMetricsProto.WifiAwareLog.INVALID_NDP_ID;
+ case NanStatusType.NAN_NOT_ALLOWED:
+ return WifiMetricsProto.WifiAwareLog.NAN_NOT_ALLOWED;
+ case NanStatusType.NO_OTA_ACK:
+ return WifiMetricsProto.WifiAwareLog.NO_OTA_ACK;
+ case NanStatusType.ALREADY_ENABLED:
+ return WifiMetricsProto.WifiAwareLog.ALREADY_ENABLED;
+ case NanStatusType.FOLLOWUP_TX_QUEUE_FULL:
+ return WifiMetricsProto.WifiAwareLog.FOLLOWUP_TX_QUEUE_FULL;
+ case NanStatusType.UNSUPPORTED_CONCURRENCY_NAN_DISABLED:
+ return WifiMetricsProto.WifiAwareLog.UNSUPPORTED_CONCURRENCY_NAN_DISABLED;
+ default:
+ Log.e(TAG, "Unrecognized NanStatusType: " + nanStatusType);
+ return WifiMetricsProto.WifiAwareLog.UNKNOWN_HAL_STATUS;
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareNativeApi.java b/service/java/com/android/server/wifi/aware/WifiAwareNativeApi.java
index 2efa2ae..a6e724f 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareNativeApi.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareNativeApi.java
@@ -36,28 +36,131 @@
import android.net.wifi.aware.PublishConfig;
import android.net.wifi.aware.SubscribeConfig;
import android.os.RemoteException;
+import android.os.ShellCommand;
import android.util.Log;
import libcore.util.HexEncoding;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
/**
* Translates Wi-Fi Aware requests from the framework to the HAL (HIDL).
*
* Delegates the management of the NAN interface to WifiAwareNativeManager.
*/
-public class WifiAwareNativeApi {
+public class WifiAwareNativeApi implements WifiAwareShellCommand.DelegatedShellCommand {
private static final String TAG = "WifiAwareNativeApi";
private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
+ private static final String SERVICE_NAME_FOR_OOB_DATA_PATH = "Wi-Fi Aware Data Path";
+
private final WifiAwareNativeManager mHal;
public WifiAwareNativeApi(WifiAwareNativeManager wifiAwareNativeManager) {
mHal = wifiAwareNativeManager;
+ onReset();
+ }
+
+ /*
+ * Parameters settable through the shell command.
+ * see wifi/1.0/types.hal NanBandSpecificConfig.discoveryWindowIntervalVal for description
+ */
+ public static final String PARAM_DW_DEFAULT_24GHZ = "dw_default_24ghz";
+ public static final int PARAM_DW_DEFAULT_24GHZ_DEFAULT = -1; // Firmware default
+ public static final String PARAM_DW_DEFAULT_5GHZ = "dw_default_5ghz";
+ public static final int PARAM_DW_DEFAULT_5GHZ_DEFAULT = -1; // Firmware default
+ public static final String PARAM_DW_ON_INACTIVE_24GHZ = "dw_on_inactive_24ghz";
+ public static final int PARAM_DW_ON_INACTIVE_24GHZ_DEFAULT = 4; // 4 -> DW=8, latency=4s
+ public static final String PARAM_DW_ON_INACTIVE_5GHZ = "dw_on_inactive_5ghz";
+ public static final int PARAM_DW_ON_INACTIVE_5GHZ_DEFAULT = 0; // 0 = disabled
+ public static final String PARAM_DW_ON_IDLE_24GHZ = "dw_on_idle_24ghz";
+ public static final int PARAM_DW_ON_IDLE_24GHZ_DEFAULT = -1; // NOP (but disabling on IDLE)
+ public static final String PARAM_DW_ON_IDLE_5GHZ = "dw_on_idle_5ghz";
+ public static final int PARAM_DW_ON_IDLE_5GHZ_DEFAULT = -1; // NOP (but disabling on IDLE)
+
+ public static final String PARAM_MAC_RANDOM_INTERVAL_SEC = "mac_random_interval_sec";
+ public static final int PARAM_MAC_RANDOM_INTERVAL_SEC_DEFAULT = 1800; // 30 minutes
+
+ private Map<String, Integer> mSettableParameters = new HashMap<>();
+
+ /**
+ * Interpreter of adb shell command 'adb shell wifiaware native_api ...'.
+ *
+ * @return -1 if parameter not recognized or invalid value, 0 otherwise.
+ */
+ @Override
+ public int onCommand(ShellCommand parentShell) {
+ final PrintWriter pw = parentShell.getErrPrintWriter();
+
+ String subCmd = parentShell.getNextArgRequired();
+ if (VDBG) Log.v(TAG, "onCommand: subCmd='" + subCmd + "'");
+ switch (subCmd) {
+ case "set": {
+ String name = parentShell.getNextArgRequired();
+ if (VDBG) Log.v(TAG, "onCommand: name='" + name + "'");
+ if (!mSettableParameters.containsKey(name)) {
+ pw.println("Unknown parameter name -- '" + name + "'");
+ return -1;
+ }
+
+ String valueStr = parentShell.getNextArgRequired();
+ if (VDBG) Log.v(TAG, "onCommand: valueStr='" + valueStr + "'");
+ int value;
+ try {
+ value = Integer.valueOf(valueStr);
+ } catch (NumberFormatException e) {
+ pw.println("Can't convert value to integer -- '" + valueStr + "'");
+ return -1;
+ }
+ mSettableParameters.put(name, value);
+ return 0;
+ }
+ case "get": {
+ String name = parentShell.getNextArgRequired();
+ if (VDBG) Log.v(TAG, "onCommand: name='" + name + "'");
+ if (!mSettableParameters.containsKey(name)) {
+ pw.println("Unknown parameter name -- '" + name + "'");
+ return -1;
+ }
+
+ parentShell.getOutPrintWriter().println((int) mSettableParameters.get(name));
+ return 0;
+ }
+ default:
+ pw.println("Unknown 'wifiaware native_api <cmd>'");
+ }
+
+ return -1;
+ }
+
+ @Override
+ public void onReset() {
+ mSettableParameters.put(PARAM_DW_DEFAULT_24GHZ, PARAM_DW_DEFAULT_24GHZ_DEFAULT);
+ mSettableParameters.put(PARAM_DW_DEFAULT_5GHZ, PARAM_DW_DEFAULT_5GHZ_DEFAULT);
+ mSettableParameters.put(PARAM_DW_ON_INACTIVE_24GHZ, PARAM_DW_ON_INACTIVE_24GHZ_DEFAULT);
+ mSettableParameters.put(PARAM_DW_ON_INACTIVE_5GHZ, PARAM_DW_ON_INACTIVE_5GHZ_DEFAULT);
+ mSettableParameters.put(PARAM_DW_ON_IDLE_24GHZ, PARAM_DW_ON_IDLE_24GHZ_DEFAULT);
+ mSettableParameters.put(PARAM_DW_ON_IDLE_5GHZ, PARAM_DW_ON_IDLE_5GHZ_DEFAULT);
+
+ mSettableParameters.put(PARAM_MAC_RANDOM_INTERVAL_SEC,
+ PARAM_MAC_RANDOM_INTERVAL_SEC_DEFAULT);
+ }
+
+ @Override
+ public void onHelp(String command, ShellCommand parentShell) {
+ final PrintWriter pw = parentShell.getOutPrintWriter();
+
+ pw.println(" " + command);
+ pw.println(" set <name> <value>: sets named parameter to value. Names: "
+ + mSettableParameters.keySet());
+ pw.println(" get <name>: gets named parameter value. Names: "
+ + mSettableParameters.keySet());
}
/**
@@ -98,13 +201,17 @@
* @param notifyIdentityChange Indicates whether or not to get address change callbacks.
* @param initialConfiguration Specifies whether initial configuration
* (true) or an update (false) to the configuration.
+ * @param isInteractive PowerManager.isInteractive
+ * @param isIdle PowerManager.isIdle
*/
public boolean enableAndConfigure(short transactionId, ConfigRequest configRequest,
- boolean notifyIdentityChange, boolean initialConfiguration) {
+ boolean notifyIdentityChange, boolean initialConfiguration, boolean isInteractive,
+ boolean isIdle) {
if (VDBG) {
Log.v(TAG, "enableAndConfigure: transactionId=" + transactionId + ", configRequest="
+ configRequest + ", notifyIdentityChange=" + notifyIdentityChange
- + ", initialConfiguration=" + initialConfiguration);
+ + ", initialConfiguration=" + initialConfiguration
+ + ", isInteractive=" + isInteractive + ", isIdle=" + isIdle);
}
IWifiNanIface iface = mHal.getWifiNanIface();
@@ -131,7 +238,8 @@
req.configParams.includeSubscribeServiceIdsInBeacon = true;
req.configParams.numberOfSubscribeServiceIdsInBeacon = 0;
req.configParams.rssiWindowSize = 8;
- req.configParams.macAddressRandomizationIntervalSec = 1800;
+ req.configParams.macAddressRandomizationIntervalSec = mSettableParameters.get(
+ PARAM_MAC_RANDOM_INTERVAL_SEC);
NanBandSpecificConfig config24 = new NanBandSpecificConfig();
config24.rssiClose = 60;
@@ -187,6 +295,8 @@
req.debugConfigs.useSdfInBandVal[NanBandIndex.NAN_BAND_24GHZ] = true;
req.debugConfigs.useSdfInBandVal[NanBandIndex.NAN_BAND_5GHZ] = true;
+ updateConfigForPowerSettings(req.configParams, isInteractive, isIdle);
+
status = iface.enableRequest(transactionId, req);
} else {
NanConfigRequest req = new NanConfigRequest();
@@ -199,7 +309,8 @@
req.includeSubscribeServiceIdsInBeacon = true;
req.numberOfSubscribeServiceIdsInBeacon = 0;
req.rssiWindowSize = 8;
- req.macAddressRandomizationIntervalSec = 1800;
+ req.macAddressRandomizationIntervalSec = mSettableParameters.get(
+ PARAM_MAC_RANDOM_INTERVAL_SEC);
NanBandSpecificConfig config24 = new NanBandSpecificConfig();
config24.rssiClose = 60;
@@ -235,6 +346,8 @@
}
req.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ] = config5;
+ updateConfigForPowerSettings(req, isInteractive, isIdle);
+
status = iface.configRequest(transactionId, req);
}
if (status.code == WifiStatusCode.SUCCESS) {
@@ -287,9 +400,10 @@
* session.
* @param publishConfig Configuration of the discovery session.
*/
- public boolean publish(short transactionId, int publishId, PublishConfig publishConfig) {
+ public boolean publish(short transactionId, byte publishId, PublishConfig publishConfig) {
if (VDBG) {
- Log.d(TAG, "publish: transactionId=" + transactionId + ", config=" + publishConfig);
+ Log.d(TAG, "publish: transactionId=" + transactionId + ", publishId=" + publishId
+ + ", config=" + publishConfig);
}
IWifiNanIface iface = mHal.getWifiNanIface();
@@ -299,13 +413,12 @@
}
NanPublishRequest req = new NanPublishRequest();
- req.baseConfigs.sessionId = 0;
+ req.baseConfigs.sessionId = publishId;
req.baseConfigs.ttlSec = (short) publishConfig.mTtlSec;
req.baseConfigs.discoveryWindowPeriod = 1;
req.baseConfigs.discoveryCount = 0;
convertNativeByteArrayToArrayList(publishConfig.mServiceName, req.baseConfigs.serviceName);
- // TODO: what's the right value on publish?
- req.baseConfigs.discoveryMatchIndicator = NanMatchAlg.MATCH_ONCE;
+ req.baseConfigs.discoveryMatchIndicator = NanMatchAlg.MATCH_NEVER;
convertNativeByteArrayToArrayList(publishConfig.mServiceSpecificInfo,
req.baseConfigs.serviceSpecificInfo);
convertNativeByteArrayToArrayList(publishConfig.mMatchFilter,
@@ -348,10 +461,11 @@
* subscribe session.
* @param subscribeConfig Configuration of the discovery session.
*/
- public boolean subscribe(short transactionId, int subscribeId,
+ public boolean subscribe(short transactionId, byte subscribeId,
SubscribeConfig subscribeConfig) {
if (VDBG) {
- Log.d(TAG, "subscribe: transactionId=" + transactionId + ", config=" + subscribeConfig);
+ Log.d(TAG, "subscribe: transactionId=" + transactionId + ", subscribeId=" + subscribeId
+ + ", config=" + subscribeConfig);
}
IWifiNanIface iface = mHal.getWifiNanIface();
@@ -361,7 +475,7 @@
}
NanSubscribeRequest req = new NanSubscribeRequest();
- req.baseConfigs.sessionId = 0;
+ req.baseConfigs.sessionId = subscribeId;
req.baseConfigs.ttlSec = (short) subscribeConfig.mTtlSec;
req.baseConfigs.discoveryWindowPeriod = 1;
req.baseConfigs.discoveryCount = 0;
@@ -413,14 +527,16 @@
* @param messageId Arbitary integer from host (not sent to HAL - useful for
* testing/debugging at this level)
*/
- public boolean sendMessage(short transactionId, int pubSubId, int requestorInstanceId,
+ public boolean sendMessage(short transactionId, byte pubSubId, int requestorInstanceId,
byte[] dest, byte[] message, int messageId) {
if (VDBG) {
Log.d(TAG,
"sendMessage: transactionId=" + transactionId + ", pubSubId=" + pubSubId
+ ", requestorInstanceId=" + requestorInstanceId + ", dest="
- + String.valueOf(HexEncoding.encode(dest)) + ", messageId="
- + messageId);
+ + String.valueOf(HexEncoding.encode(dest)) + ", messageId=" + messageId
+ + ", message=" + (message == null ? "<null>"
+ : HexEncoding.encode(message)) + ", message.length=" + (message == null
+ ? 0 : message.length));
}
IWifiNanIface iface = mHal.getWifiNanIface();
@@ -430,7 +546,7 @@
}
NanTransmitFollowupRequest req = new NanTransmitFollowupRequest();
- req.discoverySessionId = (byte) pubSubId;
+ req.discoverySessionId = pubSubId;
req.peerId = requestorInstanceId;
copyArray(dest, req.addr);
req.isHighPriority = false;
@@ -460,7 +576,7 @@
* @param pubSubId ID of the publish/subscribe session - obtained when
* creating a session.
*/
- public boolean stopPublish(short transactionId, int pubSubId) {
+ public boolean stopPublish(short transactionId, byte pubSubId) {
if (VDBG) {
Log.d(TAG, "stopPublish: transactionId=" + transactionId + ", pubSubId=" + pubSubId);
}
@@ -472,7 +588,7 @@
}
try {
- WifiStatus status = iface.stopPublishRequest(transactionId, (byte) pubSubId);
+ WifiStatus status = iface.stopPublishRequest(transactionId, pubSubId);
if (status.code == WifiStatusCode.SUCCESS) {
return true;
} else {
@@ -493,7 +609,7 @@
* @param pubSubId ID of the publish/subscribe session - obtained when
* creating a session.
*/
- public boolean stopSubscribe(short transactionId, int pubSubId) {
+ public boolean stopSubscribe(short transactionId, byte pubSubId) {
if (VDBG) {
Log.d(TAG, "stopSubscribe: transactionId=" + transactionId + ", pubSubId=" + pubSubId);
}
@@ -505,7 +621,7 @@
}
try {
- WifiStatus status = iface.stopSubscribeRequest(transactionId, (byte) pubSubId);
+ WifiStatus status = iface.stopSubscribeRequest(transactionId, pubSubId);
if (status.code == WifiStatusCode.SUCCESS) {
return true;
} else {
@@ -605,7 +721,7 @@
*/
public boolean initiateDataPath(short transactionId, int peerId, int channelRequestType,
int channel, byte[] peer, String interfaceName, byte[] pmk, String passphrase,
- Capabilities capabilities) {
+ boolean isOutOfBand, Capabilities capabilities) {
if (VDBG) {
Log.v(TAG, "initiateDataPath: transactionId=" + transactionId + ", peerId=" + peerId
+ ", channelRequestType=" + channelRequestType + ", channel=" + channel
@@ -644,6 +760,12 @@
convertNativeByteArrayToArrayList(passphrase.getBytes(), req.securityConfig.passphrase);
}
+ if (req.securityConfig.securityType != NanDataPathSecurityType.OPEN && isOutOfBand) {
+ convertNativeByteArrayToArrayList(
+ SERVICE_NAME_FOR_OOB_DATA_PATH.getBytes(StandardCharsets.UTF_8),
+ req.serviceNameOutOfBand);
+ }
+
try {
WifiStatus status = iface.initiateDataPathRequest(transactionId, req);
if (status.code == WifiStatusCode.SUCCESS) {
@@ -670,10 +792,13 @@
* request callback.
* @param pmk Pairwise master key (PMK - see IEEE 802.11i) for the data-path.
* @param passphrase Passphrase for the data-path.
+ * @param isOutOfBand Is the data-path out-of-band (i.e. without a corresponding Aware discovery
+ * session).
* @param capabilities The capabilities of the firmware.
*/
public boolean respondToDataPathRequest(short transactionId, boolean accept, int ndpId,
- String interfaceName, byte[] pmk, String passphrase, Capabilities capabilities) {
+ String interfaceName, byte[] pmk, String passphrase, boolean isOutOfBand,
+ Capabilities capabilities) {
if (VDBG) {
Log.v(TAG, "respondToDataPathRequest: transactionId=" + transactionId + ", accept="
+ accept + ", int ndpId=" + ndpId + ", interfaceName=" + interfaceName);
@@ -708,6 +833,12 @@
convertNativeByteArrayToArrayList(passphrase.getBytes(), req.securityConfig.passphrase);
}
+ if (req.securityConfig.securityType != NanDataPathSecurityType.OPEN && isOutOfBand) {
+ convertNativeByteArrayToArrayList(
+ SERVICE_NAME_FOR_OOB_DATA_PATH.getBytes(StandardCharsets.UTF_8),
+ req.serviceNameOutOfBand);
+ }
+
try {
WifiStatus status = iface.respondToDataPathIndicationRequest(transactionId, req);
if (status.code == WifiStatusCode.SUCCESS) {
@@ -758,6 +889,38 @@
// utilities
/**
+ * Update the NAN configuration to reflect the current power settings.
+ */
+ private void updateConfigForPowerSettings(NanConfigRequest req, boolean isInteractive,
+ boolean isIdle) {
+ if (isIdle) { // lowest power state: doze
+ updateSingleConfigForPowerSettings(req.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ],
+ mSettableParameters.get(PARAM_DW_ON_IDLE_5GHZ));
+ updateSingleConfigForPowerSettings(req.bandSpecificConfig[NanBandIndex.NAN_BAND_24GHZ],
+ mSettableParameters.get(PARAM_DW_ON_IDLE_24GHZ));
+ } else if (!isInteractive) { // intermediate power state: inactive
+ updateSingleConfigForPowerSettings(req.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ],
+ mSettableParameters.get(PARAM_DW_ON_INACTIVE_5GHZ));
+ updateSingleConfigForPowerSettings(req.bandSpecificConfig[NanBandIndex.NAN_BAND_24GHZ],
+ mSettableParameters.get(PARAM_DW_ON_INACTIVE_24GHZ));
+ } else { // the default state
+ updateSingleConfigForPowerSettings(req.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ],
+ mSettableParameters.get(PARAM_DW_DEFAULT_5GHZ));
+ updateSingleConfigForPowerSettings(req.bandSpecificConfig[NanBandIndex.NAN_BAND_24GHZ],
+ mSettableParameters.get(PARAM_DW_DEFAULT_24GHZ));
+ }
+
+ // else do nothing - normal power state
+ }
+
+ private void updateSingleConfigForPowerSettings(NanBandSpecificConfig cfg, int override) {
+ if (override != -1) {
+ cfg.validDiscoveryWindowIntervalVal = true;
+ cfg.discoveryWindowIntervalVal = (byte) override;
+ }
+ }
+
+ /**
* Returns the strongest supported cipher suite.
*
* Baseline is very simple: 256 > 128 > 0.
@@ -820,6 +983,8 @@
* Dump the internal state of the class.
*/
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("WifiAwareNativeApi:");
+ pw.println(" mSettableParameters: " + mSettableParameters);
mHal.dump(fd, pw, args);
}
}
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java b/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java
index 6f1925f..05721af 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java
@@ -26,17 +26,25 @@
import android.hardware.wifi.V1_0.NanMatchInd;
import android.hardware.wifi.V1_0.NanStatusType;
import android.hardware.wifi.V1_0.WifiNanStatus;
+import android.os.ShellCommand;
import android.util.Log;
+import android.util.SparseIntArray;
import libcore.util.HexEncoding;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Manages the callbacks from Wi-Fi Aware HIDL (HAL).
*/
-public class WifiAwareNativeCallback extends IWifiNanIfaceEventCallback.Stub {
+public class WifiAwareNativeCallback extends IWifiNanIfaceEventCallback.Stub implements
+ WifiAwareShellCommand.DelegatedShellCommand {
private static final String TAG = "WifiAwareNativeCallback";
private static final boolean DBG = false;
private static final boolean VDBG = false;
@@ -47,6 +55,89 @@
mWifiAwareStateManager = wifiAwareStateManager;
}
+ /*
+ * Counts of callbacks from HAL. Retrievable through shell command.
+ */
+ private static final int CB_EV_CLUSTER = 0;
+ private static final int CB_EV_DISABLED = 1;
+ private static final int CB_EV_PUBLISH_TERMINATED = 2;
+ private static final int CB_EV_SUBSCRIBE_TERMINATED = 3;
+ private static final int CB_EV_MATCH = 4;
+ private static final int CB_EV_MATCH_EXPIRED = 5;
+ private static final int CB_EV_FOLLOWUP_RECEIVED = 6;
+ private static final int CB_EV_TRANSMIT_FOLLOWUP = 7;
+ private static final int CB_EV_DATA_PATH_REQUEST = 8;
+ private static final int CB_EV_DATA_PATH_CONFIRM = 9;
+ private static final int CB_EV_DATA_PATH_TERMINATED = 10;
+
+ private SparseIntArray mCallbackCounter = new SparseIntArray();
+
+ private void incrementCbCount(int callbackId) {
+ mCallbackCounter.put(callbackId, mCallbackCounter.get(callbackId) + 1);
+ }
+
+ /**
+ * Interpreter of adb shell command 'adb shell wifiaware native_cb ...'.
+ *
+ * @return -1 if parameter not recognized or invalid value, 0 otherwise.
+ */
+ @Override
+ public int onCommand(ShellCommand parentShell) {
+ final PrintWriter pwe = parentShell.getErrPrintWriter();
+ final PrintWriter pwo = parentShell.getOutPrintWriter();
+
+ String subCmd = parentShell.getNextArgRequired();
+ if (VDBG) Log.v(TAG, "onCommand: subCmd='" + subCmd + "'");
+ switch (subCmd) {
+ case "get_cb_count": {
+ String option = parentShell.getNextOption();
+ Log.v(TAG, "option='" + option + "'");
+ boolean reset = false;
+ if (option != null) {
+ if ("--reset".equals(option)) {
+ reset = true;
+ } else {
+ pwe.println("Unknown option to 'get_cb_count'");
+ return -1;
+ }
+ }
+
+ JSONObject j = new JSONObject();
+ try {
+ for (int i = 0; i < mCallbackCounter.size(); ++i) {
+ j.put(Integer.toString(mCallbackCounter.keyAt(i)),
+ mCallbackCounter.valueAt(i));
+ }
+ } catch (JSONException e) {
+ Log.e(TAG, "onCommand: get_cb_count e=" + e);
+ }
+ pwo.println(j.toString());
+ if (reset) {
+ mCallbackCounter.clear();
+ }
+ return 0;
+ }
+ default:
+ pwe.println("Unknown 'wifiaware native_cb <cmd>'");
+ }
+
+ return -1;
+ }
+
+ @Override
+ public void onReset() {
+ // NOP (onReset is intended for configuration reset - not data reset)
+ }
+
+ @Override
+ public void onHelp(String command, ShellCommand parentShell) {
+ final PrintWriter pw = parentShell.getOutPrintWriter();
+
+ pw.println(" " + command);
+ pw.println(" get_cb_count [--reset]: gets the number of callbacks (and optionally reset "
+ + "count)");
+ }
+
@Override
public void notifyCapabilitiesResponse(short id, WifiNanStatus status,
NanCapabilities capabilities) {
@@ -76,6 +167,11 @@
capabilities.maxSubscribeInterfaceAddresses;
frameworkCapabilities.supportedCipherSuites = capabilities.supportedCipherSuites;
+ // TODO (b/63635780, b/63635857): enable framework support of >1 NDI and >1 NDP per NDI
+ // Until then: force corresponding capabilities to 1.
+ frameworkCapabilities.maxNdiInterfaces = 1;
+ frameworkCapabilities.maxNdpSessions = 1;
+
mWifiAwareStateManager.onCapabilitiesUpdateResponse(id, frameworkCapabilities);
} else {
Log.e(TAG, "notifyCapabilitiesResponse: error code=" + status.status + " ("
@@ -87,7 +183,12 @@
public void notifyEnableResponse(short id, WifiNanStatus status) {
if (VDBG) Log.v(TAG, "notifyEnableResponse: id=" + id + ", status=" + statusString(status));
- if (status.status == NanStatusType.SUCCESS) {
+ if (status.status == NanStatusType.ALREADY_ENABLED) {
+ Log.wtf(TAG, "notifyEnableResponse: id=" + id + ", already enabled!?");
+ }
+
+ if (status.status == NanStatusType.SUCCESS
+ || status.status == NanStatusType.ALREADY_ENABLED) {
mWifiAwareStateManager.onConfigSuccessResponse(id);
} else {
mWifiAwareStateManager.onConfigFailedResponse(id, status.status);
@@ -111,12 +212,11 @@
Log.v(TAG, "notifyDisableResponse: id=" + id + ", status=" + statusString(status));
}
- if (status.status == NanStatusType.SUCCESS) {
- // NOP
- } else {
+ if (status.status != NanStatusType.SUCCESS) {
Log.e(TAG, "notifyDisableResponse: failure - code=" + status.status + " ("
+ status.description + ")");
}
+ mWifiAwareStateManager.onDisableResponse(id, status.status);
}
@Override
@@ -255,6 +355,7 @@
Log.v(TAG, "eventClusterEvent: eventType=" + event.eventType + ", addr="
+ String.valueOf(HexEncoding.encode(event.addr)));
}
+ incrementCbCount(CB_EV_CLUSTER);
if (event.eventType == NanClusterEventType.DISCOVERY_MAC_ADDRESS_CHANGED) {
mWifiAwareStateManager.onInterfaceAddressChangeNotification(event.addr);
@@ -272,6 +373,7 @@
@Override
public void eventDisabled(WifiNanStatus status) {
if (VDBG) Log.v(TAG, "eventDisabled: status=" + statusString(status));
+ incrementCbCount(CB_EV_DISABLED);
mWifiAwareStateManager.onAwareDownNotification(status.status);
}
@@ -282,6 +384,7 @@
Log.v(TAG, "eventPublishTerminated: sessionId=" + sessionId + ", status="
+ statusString(status));
}
+ incrementCbCount(CB_EV_PUBLISH_TERMINATED);
mWifiAwareStateManager.onSessionTerminatedNotification(sessionId, status.status, true);
}
@@ -292,6 +395,7 @@
Log.v(TAG, "eventSubscribeTerminated: sessionId=" + sessionId + ", status="
+ statusString(status));
}
+ incrementCbCount(CB_EV_SUBSCRIBE_TERMINATED);
mWifiAwareStateManager.onSessionTerminatedNotification(sessionId, status.status, false);
}
@@ -302,9 +406,13 @@
Log.v(TAG, "eventMatch: discoverySessionId=" + event.discoverySessionId + ", peerId="
+ event.peerId + ", addr=" + String.valueOf(HexEncoding.encode(event.addr))
+ ", serviceSpecificInfo=" + Arrays.toString(
- convertArrayListToNativeByteArray(event.serviceSpecificInfo)) + ", matchFilter="
- + Arrays.toString(convertArrayListToNativeByteArray(event.matchFilter)));
+ convertArrayListToNativeByteArray(event.serviceSpecificInfo)) + ", ssi.size()="
+ + (event.serviceSpecificInfo == null ? 0 : event.serviceSpecificInfo.size())
+ + ", matchFilter=" + Arrays.toString(
+ convertArrayListToNativeByteArray(event.matchFilter)) + ", mf.size()=" + (
+ event.matchFilter == null ? 0 : event.matchFilter.size()));
}
+ incrementCbCount(CB_EV_MATCH);
mWifiAwareStateManager.onMatchNotification(event.discoverySessionId, event.peerId,
event.addr, convertArrayListToNativeByteArray(event.serviceSpecificInfo),
@@ -317,6 +425,7 @@
Log.v(TAG, "eventMatchExpired: discoverySessionId=" + discoverySessionId
+ ", peerId=" + peerId);
}
+ incrementCbCount(CB_EV_MATCH_EXPIRED);
// NOP
}
@@ -326,8 +435,11 @@
if (VDBG) {
Log.v(TAG, "eventFollowupReceived: discoverySessionId=" + event.discoverySessionId
+ ", peerId=" + event.peerId + ", addr=" + String.valueOf(
- HexEncoding.encode(event.addr)));
+ HexEncoding.encode(event.addr)) + ", serviceSpecificInfo=" + Arrays.toString(
+ convertArrayListToNativeByteArray(event.serviceSpecificInfo)) + ", ssi.size()="
+ + (event.serviceSpecificInfo == null ? 0 : event.serviceSpecificInfo.size()));
}
+ incrementCbCount(CB_EV_FOLLOWUP_RECEIVED);
mWifiAwareStateManager.onMessageReceivedNotification(event.discoverySessionId, event.peerId,
event.addr, convertArrayListToNativeByteArray(event.serviceSpecificInfo));
@@ -338,6 +450,7 @@
if (VDBG) {
Log.v(TAG, "eventTransmitFollowup: id=" + id + ", status=" + statusString(status));
}
+ incrementCbCount(CB_EV_TRANSMIT_FOLLOWUP);
if (status.status == NanStatusType.SUCCESS) {
mWifiAwareStateManager.onMessageSendSuccessNotification(id);
@@ -354,6 +467,7 @@
HexEncoding.encode(event.peerDiscMacAddr)) + ", ndpInstanceId="
+ event.ndpInstanceId);
}
+ incrementCbCount(CB_EV_DATA_PATH_REQUEST);
mWifiAwareStateManager.onDataPathRequestNotification(event.discoverySessionId,
event.peerDiscMacAddr, event.ndpInstanceId);
@@ -367,6 +481,7 @@
+ ", dataPathSetupSuccess=" + event.dataPathSetupSuccess + ", reason="
+ event.status.status);
}
+ incrementCbCount(CB_EV_DATA_PATH_CONFIRM);
mWifiAwareStateManager.onDataPathConfirmNotification(event.ndpInstanceId,
event.peerNdiMacAddr, event.dataPathSetupSuccess, event.status.status,
@@ -376,10 +491,20 @@
@Override
public void eventDataPathTerminated(int ndpInstanceId) {
if (VDBG) Log.v(TAG, "eventDataPathTerminated: ndpInstanceId=" + ndpInstanceId);
+ incrementCbCount(CB_EV_DATA_PATH_TERMINATED);
mWifiAwareStateManager.onDataPathEndNotification(ndpInstanceId);
}
+ /**
+ * Dump the internal state of the class.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("WifiAwareNativeCallback:");
+ pw.println(" mCallbackCounter: " + mCallbackCounter);
+ }
+
+
// utilities
/**
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java b/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java
index 22f1385..e855d81 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java
@@ -23,6 +23,7 @@
import android.os.RemoteException;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wifi.HalDeviceManager;
import java.io.FileDescriptor;
@@ -31,7 +32,7 @@
/**
* Manages the interface to Wi-Fi Aware HIDL (HAL).
*/
-class WifiAwareNativeManager {
+public class WifiAwareNativeManager {
private static final String TAG = "WifiAwareNativeManager";
private static final boolean DBG = false;
@@ -53,6 +54,10 @@
mWifiAwareStateManager = awareStateManager;
mHalDeviceManager = halDeviceManager;
mWifiAwareNativeCallback = wifiAwareNativeCallback;
+ }
+
+ public void start() {
+ mHalDeviceManager.initialize();
mHalDeviceManager.registerStatusListener(
new HalDeviceManager.ManagerStatusListener() {
@Override
@@ -71,16 +76,26 @@
}
}, null);
if (mHalDeviceManager.isStarted()) {
+ mHalDeviceManager.registerInterfaceAvailableForRequestListener(
+ IfaceType.NAN, mInterfaceAvailableForRequestListener, null);
tryToGetAware();
}
}
- /* package */ IWifiNanIface getWifiNanIface() {
+ /**
+ * Returns the native HAL WifiNanIface through which commands to the NAN HAL are dispatched.
+ * Return may be null if not initialized/available.
+ */
+ @VisibleForTesting
+ public IWifiNanIface getWifiNanIface() {
synchronized (mLock) {
return mWifiNanIface;
}
}
+ /**
+ * Attempt to obtain the HAL NAN interface. If available then enables Aware usage.
+ */
private void tryToGetAware() {
synchronized (mLock) {
if (DBG) Log.d(TAG, "tryToGetAware: mWifiNanIface=" + mWifiNanIface);
@@ -157,6 +172,7 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("WifiAwareNativeManager:");
pw.println(" mWifiNanIface: " + mWifiNanIface);
+ mWifiAwareNativeCallback.dump(fd, pw, args);
mHalDeviceManager.dump(fd, pw, args);
}
}
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareService.java b/service/java/com/android/server/wifi/aware/WifiAwareService.java
index 75efa02..e210d3e 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareService.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareService.java
@@ -53,7 +53,6 @@
}
HalDeviceManager halDeviceManager = wifiInjector.getHalDeviceManager();
- halDeviceManager.initialize();
WifiAwareStateManager wifiAwareStateManager = new WifiAwareStateManager();
WifiAwareNativeCallback wifiAwareNativeCallback = new WifiAwareNativeCallback(
@@ -61,10 +60,16 @@
WifiAwareNativeManager wifiAwareNativeManager = new WifiAwareNativeManager(
wifiAwareStateManager, halDeviceManager, wifiAwareNativeCallback);
WifiAwareNativeApi wifiAwareNativeApi = new WifiAwareNativeApi(wifiAwareNativeManager);
- wifiAwareStateManager.setNative(wifiAwareNativeApi);
+ wifiAwareStateManager.setNative(wifiAwareNativeManager, wifiAwareNativeApi);
+ WifiAwareShellCommand wifiAwareShellCommand = new WifiAwareShellCommand();
+ wifiAwareShellCommand.register("native_api", wifiAwareNativeApi);
+ wifiAwareShellCommand.register("native_cb", wifiAwareNativeCallback);
+ wifiAwareShellCommand.register("state_mgr", wifiAwareStateManager);
HandlerThread awareHandlerThread = wifiInjector.getWifiAwareHandlerThread();
- mImpl.start(awareHandlerThread, wifiAwareStateManager);
+ mImpl.start(awareHandlerThread, wifiAwareStateManager, wifiAwareShellCommand,
+ wifiInjector.getWifiMetrics().getWifiAwareMetrics(),
+ wifiInjector.getWifiPermissionsWrapper());
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
mImpl.startLate();
}
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java b/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
index fa695cd..b77ae63 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
@@ -32,10 +32,14 @@
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import com.android.server.wifi.util.WifiPermissionsWrapper;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -53,6 +57,7 @@
private Context mContext;
private WifiAwareStateManager mStateManager;
+ private WifiAwareShellCommand mShellCommand;
private final Object mLock = new Object();
private final SparseArray<IBinder.DeathRecipient> mDeathRecipientsByClientId =
@@ -77,11 +82,14 @@
* Start the service: allocate a new thread (for now), start the handlers of
* the components of the service.
*/
- public void start(HandlerThread handlerThread, WifiAwareStateManager awareStateManager) {
+ public void start(HandlerThread handlerThread, WifiAwareStateManager awareStateManager,
+ WifiAwareShellCommand awareShellCommand, WifiAwareMetrics awareMetrics,
+ WifiPermissionsWrapper permissionsWrapper) {
Log.i(TAG, "Starting Wi-Fi Aware service");
mStateManager = awareStateManager;
- mStateManager.start(mContext, handlerThread.getLooper());
+ mShellCommand = awareShellCommand;
+ mStateManager.start(mContext, handlerThread.getLooper(), awareMetrics, permissionsWrapper);
}
/**
@@ -371,6 +379,12 @@
}
@Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+ mShellCommand.exec(this, in, out, err, args, callback, resultReceiver);
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareShellCommand.java b/service/java/com/android/server/wifi/aware/WifiAwareShellCommand.java
new file mode 100644
index 0000000..41eef69
--- /dev/null
+++ b/service/java/com/android/server/wifi/aware/WifiAwareShellCommand.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2017 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.aware;
+
+import android.os.Binder;
+import android.os.ShellCommand;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Interprets and executes 'adb shell cmd wifiaware [args]'.
+ */
+public class WifiAwareShellCommand extends ShellCommand {
+ private static final String TAG = "WifiAwareShellCommand";
+
+ private Map<String, DelegatedShellCommand> mDelegatedCommands = new HashMap<>();
+
+ /**
+ * Register an delegated command interpreter for the specified 'command'. Each class can
+ * interpret and execute their own commands.
+ */
+ public void register(String command, DelegatedShellCommand shellCommand) {
+ if (mDelegatedCommands.containsKey(command)) {
+ Log.e(TAG, "register: overwriting existing command -- '" + command + "'");
+ }
+
+ mDelegatedCommands.put(command, shellCommand);
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ checkRootPermission();
+
+ final PrintWriter pw = getErrPrintWriter();
+ try {
+ if ("reset".equals(cmd)) {
+ for (DelegatedShellCommand dsc: mDelegatedCommands.values()) {
+ dsc.onReset();
+ }
+ } else {
+ DelegatedShellCommand delegatedCmd = null;
+ if (!TextUtils.isEmpty(cmd)) {
+ delegatedCmd = mDelegatedCommands.get(cmd);
+ }
+
+ if (delegatedCmd != null) {
+ return delegatedCmd.onCommand(this);
+ } else {
+ return handleDefaultCommands(cmd);
+ }
+ }
+ } catch (Exception e) {
+ pw.println("Exception: " + e);
+ }
+ return -1;
+ }
+
+ private void checkRootPermission() {
+ final int uid = Binder.getCallingUid();
+ if (uid == 0) {
+ // Root can do anything.
+ return;
+ }
+ throw new SecurityException("Uid " + uid + " does not have access to wifiaware commands");
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+
+ pw.println("Wi-Fi Aware (wifiaware) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" reset");
+ pw.println(" Reset parameters to default values.");
+ for (Map.Entry<String, DelegatedShellCommand> sce: mDelegatedCommands.entrySet()) {
+ sce.getValue().onHelp(sce.getKey(), this);
+ }
+ pw.println();
+ }
+
+ /**
+ * Interface that delegated command targets must implement. They are passed the parent shell
+ * command (the real command interpreter) from which they can obtain arguments.
+ */
+ public interface DelegatedShellCommand {
+ /**
+ * Execute the specified command. Use the parent shell to obtain arguments. Note that the
+ * first argument (which specified the delegated shell) has already been extracted.
+ */
+ int onCommand(ShellCommand parentShell);
+
+ /**
+ * Reset all parameters to their default values.
+ */
+ void onReset();
+
+ /**
+ * Print out help for the delegated command. The name of the delegated command is passed
+ * as a first argument as an assist (prevents hard-coding of that string in multiple
+ * places).
+ */
+ void onHelp(String command, ShellCommand parentShell);
+
+ }
+}
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java
index a35c786..30aa42a 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java
@@ -16,8 +16,10 @@
package com.android.server.wifi.aware;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.hardware.wifi.V1_0.NanStatusType;
import android.net.wifi.RttManager;
import android.net.wifi.aware.Characteristics;
@@ -31,7 +33,9 @@
import android.os.Bundle;
import android.os.Looper;
import android.os.Message;
+import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.ArrayMap;
@@ -44,12 +48,18 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.internal.util.WakeupMessage;
+import com.android.server.wifi.util.NativeUtil;
+import com.android.server.wifi.util.WifiPermissionsWrapper;
import libcore.util.HexEncoding;
+import org.json.JSONException;
+import org.json.JSONObject;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -57,7 +67,7 @@
/**
* Manages the state of the Wi-Fi Aware system service.
*/
-public class WifiAwareStateManager {
+public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShellCommand {
private static final String TAG = "WifiAwareStateManager";
private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
@@ -108,6 +118,8 @@
private static final int COMMAND_TYPE_RESPOND_TO_DATA_PATH_SETUP_REQUEST = 117;
private static final int COMMAND_TYPE_END_DATA_PATH = 118;
private static final int COMMAND_TYPE_TRANSMIT_NEXT_MESSAGE = 119;
+ private static final int COMMAND_TYPE_RECONFIGURE = 120;
+ private static final int COMMAND_TYPE_DELAYED_INITIALIZATION = 121;
private static final int RESPONSE_TYPE_ON_CONFIG_SUCCESS = 200;
private static final int RESPONSE_TYPE_ON_CONFIG_FAIL = 201;
@@ -122,6 +134,7 @@
private static final int RESPONSE_TYPE_ON_INITIATE_DATA_PATH_FAIL = 210;
private static final int RESPONSE_TYPE_ON_RESPOND_TO_DATA_PATH_SETUP_REQUEST = 211;
private static final int RESPONSE_TYPE_ON_END_DATA_PATH = 212;
+ private static final int RESPONSE_TYPE_ON_DISABLE = 213;
private static final int NOTIFICATION_TYPE_INTERFACE_CHANGE = 301;
private static final int NOTIFICATION_TYPE_CLUSTER_CHANGE = 302;
@@ -171,8 +184,10 @@
private static final String MESSAGE_BUNDLE_KEY_NOTIFY_IDENTITY_CHANGE = "notify_identity_chg";
private static final String MESSAGE_BUNDLE_KEY_PMK = "pmk";
private static final String MESSAGE_BUNDLE_KEY_PASSPHRASE = "passphrase";
+ private static final String MESSAGE_BUNDLE_KEY_OOB = "out_of_band";
private WifiAwareNativeApi mWifiAwareNativeApi;
+ private WifiAwareNativeManager mWifiAwareNativeManager;
/*
* Asynchronous access with no lock
@@ -184,11 +199,13 @@
* handler thread: no need to use a lock.
*/
private Context mContext;
+ private WifiAwareMetrics mAwareMetrics;
private volatile Capabilities mCapabilities;
private volatile Characteristics mCharacteristics = null;
private WifiAwareStateMachine mSm;
private WifiAwareRttStateManager mRtt;
private WifiAwareDataPathStateManager mDataPathMgr;
+ private PowerManager mPowerManager;
private final SparseArray<WifiAwareClientState> mClients = new SparseArray<>();
private ConfigRequest mCurrentAwareConfiguration = null;
@@ -198,37 +215,180 @@
private byte[] mCurrentDiscoveryInterfaceMac = ALL_ZERO_MAC;
public WifiAwareStateManager() {
- // empty
+ onReset();
}
- public void setNative(WifiAwareNativeApi wifiAwareNativeApi) {
+ /**
+ * Inject references to other manager objects. Needed to resolve
+ * circular dependencies and to allow mocking.
+ */
+ public void setNative(WifiAwareNativeManager wifiAwareNativeManager,
+ WifiAwareNativeApi wifiAwareNativeApi) {
+ mWifiAwareNativeManager = wifiAwareNativeManager;
mWifiAwareNativeApi = wifiAwareNativeApi;
}
+ /*
+ * parameters settable through shell command
+ */
+ public static final String PARAM_ON_IDLE_DISABLE_AWARE = "on_idle_disable_aware";
+ public static final int PARAM_ON_IDLE_DISABLE_AWARE_DEFAULT = 1; // 0 = false, 1 = true
+
+ private Map<String, Integer> mSettableParameters = new HashMap<>();
+
+ /**
+ * Interpreter of adb shell command 'adb shell wifiaware native_api ...'.
+ *
+ * @return -1 if parameter not recognized or invalid value, 0 otherwise.
+ */
+ @Override
+ public int onCommand(ShellCommand parentShell) {
+ final PrintWriter pw_err = parentShell.getErrPrintWriter();
+ final PrintWriter pw_out = parentShell.getOutPrintWriter();
+
+ String subCmd = parentShell.getNextArgRequired();
+ if (VDBG) Log.v(TAG, "onCommand: subCmd='" + subCmd + "'");
+ switch (subCmd) {
+ case "set": {
+ String name = parentShell.getNextArgRequired();
+ if (VDBG) Log.v(TAG, "onCommand: name='" + name + "'");
+ if (!mSettableParameters.containsKey(name)) {
+ pw_err.println("Unknown parameter name -- '" + name + "'");
+ return -1;
+ }
+
+ String valueStr = parentShell.getNextArgRequired();
+ if (VDBG) Log.v(TAG, "onCommand: valueStr='" + valueStr + "'");
+ int value;
+ try {
+ value = Integer.valueOf(valueStr);
+ } catch (NumberFormatException e) {
+ pw_err.println("Can't convert value to integer -- '" + valueStr + "'");
+ return -1;
+ }
+ mSettableParameters.put(name, value);
+ return 0;
+ }
+ case "get": {
+ String name = parentShell.getNextArgRequired();
+ if (VDBG) Log.v(TAG, "onCommand: name='" + name + "'");
+ if (!mSettableParameters.containsKey(name)) {
+ pw_err.println("Unknown parameter name -- '" + name + "'");
+ return -1;
+ }
+
+ pw_out.println((int) mSettableParameters.get(name));
+ return 0;
+ }
+ case "get_capabilities": {
+ JSONObject j = new JSONObject();
+ if (mCapabilities != null) {
+ try {
+ j.put("maxConcurrentAwareClusters",
+ mCapabilities.maxConcurrentAwareClusters);
+ j.put("maxPublishes", mCapabilities.maxPublishes);
+ j.put("maxSubscribes", mCapabilities.maxSubscribes);
+ j.put("maxServiceNameLen", mCapabilities.maxServiceNameLen);
+ j.put("maxMatchFilterLen", mCapabilities.maxMatchFilterLen);
+ j.put("maxTotalMatchFilterLen", mCapabilities.maxTotalMatchFilterLen);
+ j.put("maxServiceSpecificInfoLen", mCapabilities.maxServiceSpecificInfoLen);
+ j.put("maxExtendedServiceSpecificInfoLen",
+ mCapabilities.maxExtendedServiceSpecificInfoLen);
+ j.put("maxNdiInterfaces", mCapabilities.maxNdiInterfaces);
+ j.put("maxNdpSessions", mCapabilities.maxNdpSessions);
+ j.put("maxAppInfoLen", mCapabilities.maxAppInfoLen);
+ j.put("maxQueuedTransmitMessages", mCapabilities.maxQueuedTransmitMessages);
+ j.put("maxSubscribeInterfaceAddresses",
+ mCapabilities.maxSubscribeInterfaceAddresses);
+ j.put("supportedCipherSuites", mCapabilities.supportedCipherSuites);
+ } catch (JSONException e) {
+ Log.e(TAG, "onCommand: get_capabilities e=" + e);
+ }
+ }
+ pw_out.println(j.toString());
+ return 0;
+ }
+ default:
+ pw_err.println("Unknown 'wifiaware state_mgr <cmd>'");
+ }
+
+ return -1;
+ }
+
+ @Override
+ public void onReset() {
+ mSettableParameters.put(PARAM_ON_IDLE_DISABLE_AWARE, PARAM_ON_IDLE_DISABLE_AWARE_DEFAULT);
+ }
+
+ @Override
+ public void onHelp(String command, ShellCommand parentShell) {
+ final PrintWriter pw = parentShell.getOutPrintWriter();
+
+ pw.println(" " + command);
+ pw.println(" set <name> <value>: sets named parameter to value. Names: "
+ + mSettableParameters.keySet());
+ pw.println(" get <name>: gets named parameter value. Names: "
+ + mSettableParameters.keySet());
+ pw.println(" get_capabilities: prints out the capabilities as a JSON string");
+ }
+
/**
* Initialize the handler of the state manager with the specified thread
* looper.
*
* @param looper Thread looper on which to run the handler.
*/
- public void start(Context context, Looper looper) {
+ public void start(Context context, Looper looper, WifiAwareMetrics awareMetrics,
+ WifiPermissionsWrapper permissionsWrapper) {
Log.i(TAG, "start()");
mContext = context;
+ mAwareMetrics = awareMetrics;
mSm = new WifiAwareStateMachine(TAG, looper);
mSm.setDbg(DBG);
mSm.start();
mRtt = new WifiAwareRttStateManager();
mDataPathMgr = new WifiAwareDataPathStateManager(this);
- mDataPathMgr.start(mContext, mSm.getHandler().getLooper());
+ mDataPathMgr.start(mContext, mSm.getHandler().getLooper(), awareMetrics,
+ permissionsWrapper);
+
+ mPowerManager = mContext.getSystemService(PowerManager.class);
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_SCREEN_ON);
+ intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (VDBG) Log.v(TAG, "BroadcastReceiver: action=" + action);
+ if (action.equals(Intent.ACTION_SCREEN_ON)
+ || action.equals(Intent.ACTION_SCREEN_OFF)) {
+ reconfigure();
+ }
+
+ if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) {
+ if (mSettableParameters.get(PARAM_ON_IDLE_DISABLE_AWARE) != 0) {
+ if (mPowerManager.isDeviceIdleMode()) {
+ disableUsage();
+ } else {
+ enableUsage();
+ }
+ } else {
+ reconfigure();
+ }
+ }
+ }
+ }, intentFilter);
}
/**
* Initialize the late-initialization sub-services: depend on other services already existing.
*/
public void startLate() {
- mRtt.start(mContext, mSm.getHandler().getLooper());
+ delayedInitialization();
}
/**
@@ -261,6 +421,15 @@
*/
/**
+ * Place a request for delayed start operation on the state machine queue.
+ */
+ public void delayedInitialization() {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_DELAYED_INITIALIZATION;
+ mSm.sendMessage(msg);
+ }
+
+ /**
* Place a request for a new client connection on the state machine queue.
*/
public void connect(int clientId, int uid, int pid, String callingPackage,
@@ -291,6 +460,17 @@
}
/**
+ * Place a request to reconfigure Aware. No additional input - intended to use current
+ * power settings when executed. Thus possibly entering or exiting power saving mode if
+ * needed (or do nothing if Aware is not active).
+ */
+ public void reconfigure() {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_RECONFIGURE;
+ mSm.sendMessage(msg);
+ }
+
+ /**
* Place a request to stop a discovery session on the state machine queue.
*/
public void terminateSession(int clientId, int sessionId) {
@@ -391,6 +571,11 @@
* only happens when a connection is created.
*/
public void enableUsage() {
+ if (mSettableParameters.get(PARAM_ON_IDLE_DISABLE_AWARE) != 0
+ && mPowerManager.isDeviceIdleMode()) {
+ Log.d(TAG, "enableUsage(): while device is in IDLE mode - ignoring");
+ return;
+ }
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_ENABLE_USAGE;
mSm.sendMessage(msg);
@@ -468,7 +653,7 @@
*/
public void initiateDataPathSetup(WifiAwareNetworkSpecifier networkSpecifier, int peerId,
int channelRequestType, int channel, byte[] peer, String interfaceName, byte[] pmk,
- String passphrase) {
+ String passphrase, boolean isOutOfBand) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_INITIATE_DATA_PATH_SETUP;
msg.obj = networkSpecifier;
@@ -479,6 +664,7 @@
msg.getData().putString(MESSAGE_BUNDLE_KEY_INTERFACE_NAME, interfaceName);
msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_PMK, pmk);
msg.getData().putString(MESSAGE_BUNDLE_KEY_PASSPHRASE, passphrase);
+ msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_OOB, isOutOfBand);
mSm.sendMessage(msg);
}
@@ -486,7 +672,7 @@
* Command to respond to the data-path request (executed by the responder).
*/
public void respondToDataPathRequest(boolean accept, int ndpId, String interfaceName,
- byte[] pmk, String passphrase) {
+ byte[] pmk, String passphrase, boolean isOutOfBand) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_RESPOND_TO_DATA_PATH_SETUP_REQUEST;
msg.arg2 = ndpId;
@@ -494,6 +680,7 @@
msg.getData().putString(MESSAGE_BUNDLE_KEY_INTERFACE_NAME, interfaceName);
msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_PMK, pmk);
msg.getData().putString(MESSAGE_BUNDLE_KEY_PASSPHRASE, passphrase);
+ msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_OOB, isOutOfBand);
mSm.sendMessage(msg);
}
@@ -549,11 +736,23 @@
}
/**
+ * Place a callback request on the stage machine queue: disable request finished
+ * (with the provided reason code).
+ */
+ public void onDisableResponse(short transactionId, int reason) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
+ msg.arg1 = RESPONSE_TYPE_ON_DISABLE;
+ msg.arg2 = transactionId;
+ msg.obj = reason;
+ mSm.sendMessage(msg);
+ }
+
+ /**
* Place a callback request on the state machine queue: session
* configuration (new or update) request succeeded.
*/
public void onSessionConfigSuccessResponse(short transactionId, boolean isPublish,
- int pubSubId) {
+ byte pubSubId) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
msg.arg1 = RESPONSE_TYPE_ON_SESSION_CONFIG_SUCCESS;
msg.arg2 = transactionId;
@@ -663,7 +862,7 @@
/**
* Response from firmware to
- * {@link #respondToDataPathRequest(boolean, int, String, byte[], String)}.
+ * {@link #respondToDataPathRequest(boolean, int, String, byte[], String, boolean)}
*/
public void onRespondToDataPathSetupRequestResponse(short transactionId, boolean success,
int reasonOnFailure) {
@@ -856,7 +1055,7 @@
private WakeupMessage mSendMessageTimeoutMessage = new WakeupMessage(mContext, getHandler(),
HAL_SEND_MESSAGE_TIMEOUT_TAG, MESSAGE_TYPE_SEND_MESSAGE_TIMEOUT);
- private static final long AWARE_WAIT_FOR_DP_CONFIRM_TIMEOUT = 5_000;
+ private static final long AWARE_WAIT_FOR_DP_CONFIRM_TIMEOUT = 20_000;
private final Map<WifiAwareNetworkSpecifier, WakeupMessage>
mDataPathConfirmTimeoutMessages = new ArrayMap<>();
@@ -1218,6 +1417,9 @@
waitForResponse = disconnectLocal(mCurrentTransactionId, clientId);
break;
}
+ case COMMAND_TYPE_RECONFIGURE:
+ waitForResponse = reconfigureLocal(mCurrentTransactionId);
+ break;
case COMMAND_TYPE_TERMINATE_SESSION: {
int clientId = msg.arg2;
int sessionId = (Integer) msg.obj;
@@ -1321,8 +1523,7 @@
waitForResponse = false;
break;
case COMMAND_TYPE_DISABLE_USAGE:
- disableUsageLocal();
- waitForResponse = false;
+ waitForResponse = disableUsageLocal(mCurrentTransactionId);
break;
case COMMAND_TYPE_START_RANGING: {
Bundle data = msg.getData();
@@ -1377,10 +1578,11 @@
String interfaceName = data.getString(MESSAGE_BUNDLE_KEY_INTERFACE_NAME);
byte[] pmk = data.getByteArray(MESSAGE_BUNDLE_KEY_PMK);
String passphrase = data.getString(MESSAGE_BUNDLE_KEY_PASSPHRASE);
+ boolean isOutOfBand = data.getBoolean(MESSAGE_BUNDLE_KEY_OOB);
waitForResponse = initiateDataPathSetupLocal(mCurrentTransactionId,
networkSpecifier, peerId, channelRequestType, channel, peer,
- interfaceName, pmk, passphrase);
+ interfaceName, pmk, passphrase, isOutOfBand);
if (waitForResponse) {
WakeupMessage timeout = new WakeupMessage(mContext, getHandler(),
@@ -1400,15 +1602,21 @@
String interfaceName = data.getString(MESSAGE_BUNDLE_KEY_INTERFACE_NAME);
byte[] pmk = data.getByteArray(MESSAGE_BUNDLE_KEY_PMK);
String passphrase = data.getString(MESSAGE_BUNDLE_KEY_PASSPHRASE);
+ boolean isOutOfBand = data.getBoolean(MESSAGE_BUNDLE_KEY_OOB);
waitForResponse = respondToDataPathRequestLocal(mCurrentTransactionId, accept,
- ndpId, interfaceName, pmk, passphrase);
+ ndpId, interfaceName, pmk, passphrase, isOutOfBand);
break;
}
case COMMAND_TYPE_END_DATA_PATH:
waitForResponse = endDataPathLocal(mCurrentTransactionId, msg.arg2);
break;
+ case COMMAND_TYPE_DELAYED_INITIALIZATION:
+ mWifiAwareNativeManager.start();
+ mRtt.start(mContext, mSm.getHandler().getLooper());
+ waitForResponse = false;
+ break;
default:
waitForResponse = false;
Log.wtf(TAG, "processCommand: this isn't a COMMAND -- msg=" + msg);
@@ -1447,7 +1655,7 @@
break;
}
case RESPONSE_TYPE_ON_SESSION_CONFIG_SUCCESS: {
- int pubSubId = (Integer) msg.obj;
+ byte pubSubId = (Byte) msg.obj;
boolean isPublish = msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SESSION_TYPE);
onSessionConfigSuccessLocal(mCurrentCommand, pubSubId, isPublish);
@@ -1535,6 +1743,9 @@
msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG),
msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE));
break;
+ case RESPONSE_TYPE_ON_DISABLE:
+ onDisableResponseLocal(mCurrentCommand, (Integer) msg.obj);
+ break;
default:
Log.wtf(TAG, "processResponse: this isn't a RESPONSE -- msg=" + msg);
mCurrentCommand = null;
@@ -1566,13 +1777,16 @@
break;
}
case COMMAND_TYPE_DISCONNECT: {
- /*
- * Will only get here on DISCONNECT if was downgrading. The
- * callback will do a NOP - but should still call it.
- */
onConfigFailedLocal(mCurrentCommand, NanStatusType.INTERNAL_FAILURE);
break;
}
+ case COMMAND_TYPE_RECONFIGURE:
+ /*
+ * Reconfigure timed-out. There is nothing to do but log the issue - which
+ * will be done in the callback.
+ */
+ onConfigFailedLocal(mCurrentCommand, NanStatusType.INTERNAL_FAILURE);
+ break;
case COMMAND_TYPE_TERMINATE_SESSION: {
Log.wtf(TAG, "processTimeout: TERMINATE_SESSION - shouldn't be waiting!");
break;
@@ -1651,6 +1865,11 @@
// TODO: fix status: timeout
onEndPathEndResponseLocal(mCurrentCommand, false, 0);
break;
+ case COMMAND_TYPE_DELAYED_INITIALIZATION:
+ Log.wtf(TAG,
+ "processTimeout: COMMAND_TYPE_DELAYED_INITIALIZATION - shouldn't be "
+ + "waiting!");
+ break;
default:
Log.wtf(TAG, "processTimeout: this isn't a COMMAND -- msg=" + msg);
/* fall-through */
@@ -1775,6 +1994,12 @@
if (!mUsageEnabled) {
Log.w(TAG, "connect(): called with mUsageEnabled=false");
+ try {
+ callback.onConnectFail(NanStatusType.INTERNAL_FAILURE);
+ mAwareMetrics.recordAttachStatus(NanStatusType.INTERNAL_FAILURE);
+ } catch (RemoteException e) {
+ Log.w(TAG, "connectLocal onConnectFail(): RemoteException (FYI): " + e);
+ }
return false;
}
@@ -1793,6 +2018,7 @@
+ ", incompatible with current configurations");
try {
callback.onConnectFail(NanStatusType.INTERNAL_FAILURE);
+ mAwareMetrics.recordAttachStatus(NanStatusType.INTERNAL_FAILURE);
} catch (RemoteException e) {
Log.w(TAG, "connectLocal onConnectFail(): RemoteException (FYI): " + e);
}
@@ -1802,26 +2028,30 @@
}
if (mCurrentAwareConfiguration != null && mCurrentAwareConfiguration.equals(merged)
- && mCurrentIdentityNotification == notifyIdentityChange) {
+ && (mCurrentIdentityNotification || !notifyIdentityChange)) {
try {
callback.onConnectSuccess(clientId);
} catch (RemoteException e) {
Log.w(TAG, "connectLocal onConnectSuccess(): RemoteException (FYI): " + e);
}
WifiAwareClientState client = new WifiAwareClientState(mContext, clientId, uid, pid,
- callingPackage, callback, configRequest, notifyIdentityChange);
+ callingPackage, callback, configRequest, notifyIdentityChange,
+ SystemClock.elapsedRealtime());
client.onInterfaceAddressChange(mCurrentDiscoveryInterfaceMac);
mClients.append(clientId, client);
+ mAwareMetrics.recordAttachSession(uid, notifyIdentityChange, mClients);
return false;
}
boolean notificationRequired =
doesAnyClientNeedIdentityChangeNotifications() || notifyIdentityChange;
boolean success = mWifiAwareNativeApi.enableAndConfigure(transactionId, merged,
- notificationRequired, mCurrentAwareConfiguration == null);
+ notificationRequired, mCurrentAwareConfiguration == null,
+ mPowerManager.isInteractive(), mPowerManager.isDeviceIdleMode());
if (!success) {
try {
callback.onConnectFail(NanStatusType.INTERNAL_FAILURE);
+ mAwareMetrics.recordAttachStatus(NanStatusType.INTERNAL_FAILURE);
} catch (RemoteException e) {
Log.w(TAG, "connectLocal onConnectFail(): RemoteException (FYI): " + e);
}
@@ -1842,12 +2072,18 @@
return false;
}
mClients.delete(clientId);
+ mAwareMetrics.recordAttachSessionDuration(client.getCreationTime());
+ SparseArray<WifiAwareDiscoverySessionState> sessions = client.getSessions();
+ for (int i = 0; i < sessions.size(); ++i) {
+ mAwareMetrics.recordDiscoverySessionDuration(sessions.valueAt(i).getCreationTime(),
+ sessions.valueAt(i).isPublishSession());
+ }
client.destroy();
if (mClients.size() == 0) {
mCurrentAwareConfiguration = null;
- mWifiAwareNativeApi.disable((short) 0);
- return false;
+ deleteAllDataPathInterfaces();
+ return mWifiAwareNativeApi.disable(transactionId);
}
ConfigRequest merged = mergeConfigRequests(null);
@@ -1862,7 +2098,22 @@
}
return mWifiAwareNativeApi.enableAndConfigure(transactionId, merged, notificationReqs,
- false);
+ false, mPowerManager.isInteractive(), mPowerManager.isDeviceIdleMode());
+ }
+
+ private boolean reconfigureLocal(short transactionId) {
+ if (VDBG) Log.v(TAG, "reconfigureLocal(): transactionId=" + transactionId);
+
+ if (mClients.size() == 0) {
+ // no clients - Aware is not enabled, nothing to reconfigure
+ return false;
+ }
+
+ boolean notificationReqs = doesAnyClientNeedIdentityChangeNotifications();
+
+ return mWifiAwareNativeApi.enableAndConfigure(transactionId, mCurrentAwareConfiguration,
+ notificationReqs, false, mPowerManager.isInteractive(),
+ mPowerManager.isDeviceIdleMode());
}
private void terminateSessionLocal(int clientId, int sessionId) {
@@ -1877,7 +2128,11 @@
return;
}
- client.terminateSession(sessionId);
+ WifiAwareDiscoverySessionState session = client.terminateSession(sessionId);
+ if (session != null) {
+ mAwareMetrics.recordDiscoverySessionDuration(session.getCreationTime(),
+ session.isPublishSession());
+ }
}
private boolean publishLocal(short transactionId, int clientId, PublishConfig publishConfig,
@@ -1893,13 +2148,15 @@
return false;
}
- boolean success = mWifiAwareNativeApi.publish(transactionId, 0, publishConfig);
+ boolean success = mWifiAwareNativeApi.publish(transactionId, (byte) 0, publishConfig);
if (!success) {
try {
callback.onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
} catch (RemoteException e) {
Log.w(TAG, "publishLocal onSessionConfigFail(): RemoteException (FYI): " + e);
}
+ mAwareMetrics.recordDiscoveryStatus(client.getUid(), NanStatusType.INTERNAL_FAILURE,
+ true);
}
return success;
@@ -1925,7 +2182,12 @@
return false;
}
- return session.updatePublish(transactionId, publishConfig);
+ boolean status = session.updatePublish(transactionId, publishConfig);
+ if (!status) {
+ mAwareMetrics.recordDiscoveryStatus(client.getUid(), NanStatusType.INTERNAL_FAILURE,
+ true);
+ }
+ return status;
}
private boolean subscribeLocal(short transactionId, int clientId,
@@ -1941,13 +2203,15 @@
return false;
}
- boolean success = mWifiAwareNativeApi.subscribe(transactionId, 0, subscribeConfig);
+ boolean success = mWifiAwareNativeApi.subscribe(transactionId, (byte) 0, subscribeConfig);
if (!success) {
try {
callback.onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
} catch (RemoteException e) {
Log.w(TAG, "subscribeLocal onSessionConfigFail(): RemoteException (FYI): " + e);
}
+ mAwareMetrics.recordDiscoveryStatus(client.getUid(), NanStatusType.INTERNAL_FAILURE,
+ false);
}
return success;
@@ -1975,7 +2239,12 @@
return false;
}
- return session.updateSubscribe(transactionId, subscribeConfig);
+ boolean status = session.updateSubscribe(transactionId, subscribeConfig);
+ if (!status) {
+ mAwareMetrics.recordDiscoveryStatus(client.getUid(), NanStatusType.INTERNAL_FAILURE,
+ false);
+ }
+ return status;
}
private boolean sendFollowonMessageLocal(short transactionId, int clientId, int sessionId,
@@ -2012,24 +2281,31 @@
mUsageEnabled = true;
queryCapabilities();
- createAllDataPathInterfaces();
sendAwareStateChangedBroadcast(true);
+
+ mAwareMetrics.recordEnableUsage();
}
- private void disableUsageLocal() {
- if (VDBG) Log.v(TAG, "disableUsageLocal: mUsageEnabled=" + mUsageEnabled);
+ private boolean disableUsageLocal(short transactionId) {
+ if (VDBG) {
+ Log.v(TAG, "disableUsageLocal: transactionId=" + transactionId + ", mUsageEnabled="
+ + mUsageEnabled);
+ }
if (!mUsageEnabled) {
- return;
+ return false;
}
onAwareDownLocal();
- deleteAllDataPathInterfaces();
mUsageEnabled = false;
- mWifiAwareNativeApi.disable((short) 0);
+ boolean callDispatched = mWifiAwareNativeApi.disable(transactionId);
sendAwareStateChangedBroadcast(false);
+
+ mAwareMetrics.recordDisableUsage();
+
+ return callDispatched;
}
private void startRangingLocal(int clientId, int sessionId, RttManager.RttParams[] params,
@@ -2057,10 +2333,13 @@
for (RttManager.RttParams param : params) {
String peerIdStr = param.bssid;
try {
- param.bssid = session.getMac(Integer.parseInt(peerIdStr), ":");
- if (param.bssid == null) {
+ WifiAwareDiscoverySessionState.PeerInfo peerInfo = session.getPeerInfo(
+ Integer.parseInt(peerIdStr));
+ if (peerInfo == null || peerInfo.mMac == null) {
Log.d(TAG, "startRangingLocal: no MAC address for peer ID=" + peerIdStr);
param.bssid = "";
+ } else {
+ param.bssid = NativeUtil.macAddressFromByteArray(peerInfo.mMac);
}
} catch (NumberFormatException e) {
Log.e(TAG, "startRangingLocal: invalid peer ID specification (in bssid field): '"
@@ -2074,19 +2353,22 @@
private boolean initiateDataPathSetupLocal(short transactionId,
WifiAwareNetworkSpecifier networkSpecifier, int peerId, int channelRequestType,
- int channel, byte[] peer, String interfaceName, byte[] pmk, String passphrase) {
+ int channel, byte[] peer, String interfaceName, byte[] pmk, String passphrase,
+ boolean isOutOfBand) {
if (VDBG) {
- Log.v(TAG,
- "initiateDataPathSetupLocal(): transactionId=" + transactionId
- + ", networkSpecifier=" + networkSpecifier + ", peerId=" + peerId
- + ", channelRequestType=" + channelRequestType + ", channel=" + channel
- + ", peer=" + String.valueOf(HexEncoding.encode(peer))
- + ", interfaceName=" + interfaceName + ", pmk=" + pmk
- + ", passphrase=" + passphrase);
+ Log.v(TAG, "initiateDataPathSetupLocal(): transactionId=" + transactionId
+ + ", networkSpecifier=" + networkSpecifier + ", peerId=" + peerId
+ + ", channelRequestType=" + channelRequestType + ", channel=" + channel
+ + ", peer="
+ + String.valueOf(HexEncoding.encode(peer)) + ", interfaceName=" + interfaceName
+ + ", pmk=" + ((pmk == null) ? "" : "*") + ", passphrase=" + (
+ (passphrase == null) ? "" : "*") + ", isOutOfBand="
+ + isOutOfBand);
}
boolean success = mWifiAwareNativeApi.initiateDataPath(transactionId, peerId,
- channelRequestType, channel, peer, interfaceName, pmk, passphrase, mCapabilities);
+ channelRequestType, channel, peer, interfaceName, pmk, passphrase, isOutOfBand,
+ mCapabilities);
if (!success) {
mDataPathMgr.onDataPathInitiateFail(networkSpecifier, NanStatusType.INTERNAL_FAILURE);
}
@@ -2095,18 +2377,19 @@
}
private boolean respondToDataPathRequestLocal(short transactionId, boolean accept,
- int ndpId, String interfaceName, byte[] pmk, String passphrase) {
+ int ndpId, String interfaceName, byte[] pmk, String passphrase, boolean isOutOfBand) {
if (VDBG) {
Log.v(TAG,
"respondToDataPathRequestLocal(): transactionId=" + transactionId + ", accept="
+ accept + ", ndpId=" + ndpId + ", interfaceName=" + interfaceName
- + ", pmk=" + pmk + ", passphrase=" + passphrase);
+ + ", pmk=" + ((pmk == null) ? "" : "*") + ", passphrase="
+ + ((passphrase == null) ? "" : "*") + ", isOutOfBand="
+ + isOutOfBand);
}
-
boolean success = mWifiAwareNativeApi.respondToDataPathRequest(transactionId, accept, ndpId,
- interfaceName, pmk, passphrase, mCapabilities);
+ interfaceName, pmk, passphrase, isOutOfBand, mCapabilities);
if (!success) {
- mDataPathMgr.onRespondToDataPathRequest(ndpId, false);
+ mDataPathMgr.onRespondToDataPathRequest(ndpId, false, NanStatusType.INTERNAL_FAILURE);
}
return success;
}
@@ -2143,8 +2426,10 @@
String callingPackage = data.getString(MESSAGE_BUNDLE_KEY_CALLING_PACKAGE);
WifiAwareClientState client = new WifiAwareClientState(mContext, clientId, uid, pid,
- callingPackage, callback, configRequest, notifyIdentityChange);
+ callingPackage, callback, configRequest, notifyIdentityChange,
+ SystemClock.elapsedRealtime());
mClients.put(clientId, client);
+ mAwareMetrics.recordAttachSession(uid, notifyIdentityChange, mClients);
try {
callback.onConnectSuccess(clientId);
} catch (RemoteException e) {
@@ -2156,11 +2441,18 @@
/*
* NOP (i.e. updated configuration after disconnecting a client)
*/
+ } else if (completedCommand.arg1 == COMMAND_TYPE_RECONFIGURE) {
+ /*
+ * NOP (i.e. updated configuration at power saving event)
+ */
} else {
Log.wtf(TAG, "onConfigCompletedLocal: unexpected completedCommand=" + completedCommand);
return;
}
+ if (mCurrentAwareConfiguration == null) { // enabled (as opposed to re-configured)
+ createAllDataPathInterfaces();
+ }
mCurrentAwareConfiguration = mergeConfigRequests(null);
if (mCurrentAwareConfiguration == null) {
Log.wtf(TAG, "onConfigCompletedLocal: got a null merged configuration after config!?");
@@ -2179,6 +2471,7 @@
try {
callback.onConnectFail(reason);
+ mAwareMetrics.recordAttachStatus(reason);
} catch (RemoteException e) {
Log.w(TAG, "onConfigFailedLocal onConnectFail(): RemoteException (FYI): " + e);
}
@@ -2187,15 +2480,39 @@
* NOP (tried updating configuration after disconnecting a client -
* shouldn't fail but there's nothing to do - the old configuration
* is still up-and-running).
+ *
+ * OR: timed-out getting a response to a disable. Either way a NOP.
+ */
+ } else if (failedCommand.arg1 == COMMAND_TYPE_RECONFIGURE) {
+ /*
+ * NOP (configuration change as part of possibly power saving event - should not
+ * fail but there's nothing to do).
*/
} else {
Log.wtf(TAG, "onConfigFailedLocal: unexpected failedCommand=" + failedCommand);
return;
}
-
}
- private void onSessionConfigSuccessLocal(Message completedCommand, int pubSubId,
+ private void onDisableResponseLocal(Message command, int reason) {
+ if (VDBG) {
+ Log.v(TAG, "onDisableResponseLocal: command=" + command + ", reason=" + reason);
+ }
+
+ /*
+ * do nothing:
+ * - success: was waiting so that don't enable while disabling
+ * - fail: shouldn't happen (though can if already disabled for instance)
+ */
+ if (reason != NanStatusType.SUCCESS) {
+ Log.e(TAG, "onDisableResponseLocal: FAILED!? command=" + command + ", reason="
+ + reason);
+ }
+
+ mAwareMetrics.recordDisableAware();
+ }
+
+ private void onSessionConfigSuccessLocal(Message completedCommand, byte pubSubId,
boolean isPublish) {
if (VDBG) {
Log.v(TAG, "onSessionConfigSuccessLocal: completedCommand=" + completedCommand
@@ -2224,8 +2541,15 @@
}
WifiAwareDiscoverySessionState session = new WifiAwareDiscoverySessionState(
- mWifiAwareNativeApi, sessionId, pubSubId, callback, isPublish);
+ mWifiAwareNativeApi, sessionId, pubSubId, callback, isPublish,
+ SystemClock.elapsedRealtime());
client.addSession(session);
+
+ mAwareMetrics.recordDiscoverySession(client.getUid(),
+ completedCommand.arg1 == COMMAND_TYPE_PUBLISH, mClients);
+ mAwareMetrics.recordDiscoveryStatus(client.getUid(), NanStatusType.SUCCESS,
+ completedCommand.arg1 == COMMAND_TYPE_PUBLISH);
+
} else if (completedCommand.arg1 == COMMAND_TYPE_UPDATE_PUBLISH
|| completedCommand.arg1 == COMMAND_TYPE_UPDATE_SUBSCRIBE) {
int clientId = completedCommand.arg2;
@@ -2251,6 +2575,8 @@
Log.e(TAG, "onSessionConfigSuccessLocal: onSessionConfigSuccess() RemoteException="
+ e);
}
+ mAwareMetrics.recordDiscoveryStatus(client.getUid(), NanStatusType.SUCCESS,
+ completedCommand.arg1 == COMMAND_TYPE_UPDATE_PUBLISH);
} else {
Log.wtf(TAG,
"onSessionConfigSuccessLocal: unexpected completedCommand=" + completedCommand);
@@ -2265,14 +2591,24 @@
if (failedCommand.arg1 == COMMAND_TYPE_PUBLISH
|| failedCommand.arg1 == COMMAND_TYPE_SUBSCRIBE) {
+ int clientId = failedCommand.arg2;
IWifiAwareDiscoverySessionCallback callback =
(IWifiAwareDiscoverySessionCallback) failedCommand.obj;
+
+ WifiAwareClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG, "onSessionConfigFailLocal: no client exists for clientId=" + clientId);
+ return;
+ }
+
try {
callback.onSessionConfigFail(reason);
} catch (RemoteException e) {
Log.w(TAG, "onSessionConfigFailLocal onSessionConfigFail(): RemoteException (FYI): "
+ e);
}
+ mAwareMetrics.recordDiscoveryStatus(client.getUid(), reason,
+ failedCommand.arg1 == COMMAND_TYPE_PUBLISH);
} else if (failedCommand.arg1 == COMMAND_TYPE_UPDATE_PUBLISH
|| failedCommand.arg1 == COMMAND_TYPE_UPDATE_SUBSCRIBE) {
int clientId = failedCommand.arg2;
@@ -2296,6 +2632,12 @@
} catch (RemoteException e) {
Log.e(TAG, "onSessionConfigFailLocal: onSessionConfigFail() RemoteException=" + e);
}
+ mAwareMetrics.recordDiscoveryStatus(client.getUid(), reason,
+ failedCommand.arg1 == COMMAND_TYPE_UPDATE_PUBLISH);
+
+ if (reason == NanStatusType.INVALID_SESSION_ID) {
+ client.removeSession(sessionId);
+ }
} else {
Log.wtf(TAG, "onSessionConfigFailLocal: unexpected failedCommand=" + failedCommand);
}
@@ -2435,7 +2777,7 @@
+ ", success=" + success + ", reasonOnFailure=" + reasonOnFailure);
}
- mDataPathMgr.onRespondToDataPathRequest(command.arg2, success);
+ mDataPathMgr.onRespondToDataPathRequest(command.arg2, success, reasonOnFailure);
}
private void onEndPathEndResponseLocal(Message command, boolean success, int reasonOnFailure) {
@@ -2462,6 +2804,8 @@
WifiAwareClientState client = mClients.valueAt(i);
client.onInterfaceAddressChange(mac);
}
+
+ mAwareMetrics.recordEnableAware();
}
private void onClusterChangeLocal(int flag, byte[] clusterId) {
@@ -2474,6 +2818,8 @@
WifiAwareClientState client = mClients.valueAt(i);
client.onClusterChange(flag, clusterId, mCurrentDiscoveryInterfaceMac);
}
+
+ mAwareMetrics.recordEnableAware();
}
private void onMatchLocal(int pubSubId, int requestorInstanceId, byte[] peerMac,
@@ -2516,6 +2862,8 @@
"onSessionTerminatedLocal onSessionTerminated(): RemoteException (FYI): " + e);
}
data.first.removeSession(data.second.getSessionId());
+ mAwareMetrics.recordDiscoverySessionDuration(data.second.getCreationTime(),
+ data.second.isPublishSession());
}
private void onMessageReceivedLocal(int pubSubId, int requestorInstanceId, byte[] peerMac,
@@ -2542,11 +2890,23 @@
Log.v(TAG, "onAwareDown");
}
+ for (int i = 0; i < mClients.size(); ++i) {
+ mAwareMetrics.recordAttachSessionDuration(mClients.valueAt(i).getCreationTime());
+ SparseArray<WifiAwareDiscoverySessionState> sessions = mClients.valueAt(
+ i).getSessions();
+ for (int j = 0; j < sessions.size(); ++j) {
+ mAwareMetrics.recordDiscoverySessionDuration(sessions.valueAt(i).getCreationTime(),
+ sessions.valueAt(i).isPublishSession());
+ }
+ }
+ mAwareMetrics.recordDisableAware();
+
mClients.clear();
mCurrentAwareConfiguration = null;
mSm.onAwareDownCleanupSendQueueState();
mDataPathMgr.onAwareDownCleanupDataPaths();
mCurrentDiscoveryInterfaceMac = ALL_ZERO_MAC;
+ deleteAllDataPathInterfaces();
}
/*
@@ -2699,6 +3059,7 @@
for (int i = 0; i < mClients.size(); ++i) {
mClients.valueAt(i).dump(fd, pw, args);
}
+ pw.println(" mSettableParameters: " + mSettableParameters);
mSm.dump(fd, pw, args);
mRtt.dump(fd, pw, args);
mDataPathMgr.dump(fd, pw, args);
diff --git a/service/java/com/android/server/wifi/hotspot2/ANQPNetworkKey.java b/service/java/com/android/server/wifi/hotspot2/ANQPNetworkKey.java
index aaaedb3..19590b3 100644
--- a/service/java/com/android/server/wifi/hotspot2/ANQPNetworkKey.java
+++ b/service/java/com/android/server/wifi/hotspot2/ANQPNetworkKey.java
@@ -96,10 +96,9 @@
if (mHESSID != 0L) {
return Utils.macToString(mHESSID) + ":" + mAnqpDomainID;
} else if (mBSSID != 0L) {
- return Utils.macToString(mBSSID)
- + ":<" + Utils.toUnicodeEscapedString(mSSID) + ">";
+ return Utils.macToString(mBSSID) + ":<" + mSSID + ">";
} else {
- return "<" + Utils.toUnicodeEscapedString(mSSID) + ">:" + mAnqpDomainID;
+ return "<" + mSSID + ">:" + mAnqpDomainID;
}
}
}
diff --git a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
index 19af85f..0c77162 100644
--- a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
+++ b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
@@ -293,7 +293,6 @@
} else {
mWifiMode = 0;
mMaxRate = 0;
- Log.w("WifiMode", mSSID + ", Invalid SupportedRates!!!");
}
if (DBG) {
Log.d(TAG, mSSID + "ChannelWidth is: " + mChannelWidth + " PrimaryFreq: " + mPrimaryFreq
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointConfigStoreData.java b/service/java/com/android/server/wifi/hotspot2/PasspointConfigStoreData.java
index 74a4760..38401d2 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointConfigStoreData.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointConfigStoreData.java
@@ -66,6 +66,7 @@
private static final String XML_TAG_CLIENT_PRIVATE_KEY_ALIAS = "ClientPrivateKeyAlias";
private static final String XML_TAG_PROVIDER_INDEX = "ProviderIndex";
+ private static final String XML_TAG_HAS_EVER_CONNECTED = "HasEverConnected";
private final WifiKeyStore mKeyStore;
private final SIMAccessor mSimAccessor;
@@ -211,6 +212,7 @@
provider.getClientCertificateAlias());
XmlUtil.writeNextValue(out, XML_TAG_CLIENT_PRIVATE_KEY_ALIAS,
provider.getClientPrivateKeyAlias());
+ XmlUtil.writeNextValue(out, XML_TAG_HAS_EVER_CONNECTED, provider.getHasEverConnected());
if (provider.getConfig() != null) {
XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION);
PasspointXmlUtils.serializePasspointConfiguration(out, provider.getConfig());
@@ -304,6 +306,7 @@
String caCertificateAlias = null;
String clientCertificateAlias = null;
String clientPrivateKeyAlias = null;
+ boolean hasEverConnected = false;
PasspointConfiguration config = null;
while (XmlUtils.nextElementWithin(in, outerTagDepth)) {
if (in.getAttributeValue(null, "name") != null) {
@@ -326,6 +329,9 @@
case XML_TAG_CLIENT_PRIVATE_KEY_ALIAS:
clientPrivateKeyAlias = (String) value;
break;
+ case XML_TAG_HAS_EVER_CONNECTED:
+ hasEverConnected = (boolean) value;
+ break;
}
} else {
if (!TextUtils.equals(in.getName(),
@@ -344,7 +350,8 @@
throw new XmlPullParserException("Missing Passpoint configuration");
}
return new PasspointProvider(config, mKeyStore, mSimAccessor, providerId, creatorUid,
- caCertificateAlias, clientCertificateAlias, clientPrivateKeyAlias);
+ caCertificateAlias, clientCertificateAlias, clientPrivateKeyAlias,
+ hasEverConnected);
}
/**
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
index 9000d43..5d79ba4 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
@@ -18,9 +18,7 @@
import static android.net.wifi.WifiManager.ACTION_PASSPOINT_DEAUTH_IMMINENT;
import static android.net.wifi.WifiManager.ACTION_PASSPOINT_ICON;
-import static android.net.wifi.WifiManager.ACTION_PASSPOINT_OSU_PROVIDERS_LIST;
import static android.net.wifi.WifiManager.ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION;
-import static android.net.wifi.WifiManager.EXTRA_ANQP_ELEMENT_DATA;
import static android.net.wifi.WifiManager.EXTRA_BSSID_LONG;
import static android.net.wifi.WifiManager.EXTRA_DELAY;
import static android.net.wifi.WifiManager.EXTRA_ESS;
@@ -35,6 +33,7 @@
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.hotspot2.OsuProvider;
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -46,10 +45,12 @@
import com.android.server.wifi.WifiConfigManager;
import com.android.server.wifi.WifiConfigStore;
import com.android.server.wifi.WifiKeyStore;
+import com.android.server.wifi.WifiMetrics;
import com.android.server.wifi.WifiNative;
import com.android.server.wifi.hotspot2.anqp.ANQPElement;
import com.android.server.wifi.hotspot2.anqp.Constants;
-import com.android.server.wifi.hotspot2.anqp.RawByteElement;
+import com.android.server.wifi.hotspot2.anqp.HSOsuProvidersElement;
+import com.android.server.wifi.hotspot2.anqp.OsuProviderInfo;
import com.android.server.wifi.util.InformationElementUtil;
import com.android.server.wifi.util.ScanResultUtil;
@@ -97,6 +98,7 @@
private final ANQPRequestManager mAnqpRequestManager;
private final WifiConfigManager mWifiConfigManager;
private final CertificateVerifier mCertVerifier;
+ private final WifiMetrics mWifiMetrics;
// Counter used for assigning unique identifier to each provider.
private long mProviderIndex;
@@ -121,18 +123,6 @@
// Add new entry to the cache.
mAnqpCache.addEntry(anqpKey, anqpElements);
-
- // Broadcast OSU providers info.
- if (anqpElements.containsKey(Constants.ANQPElementType.HSOSUProviders)) {
- RawByteElement osuProviders = (RawByteElement) anqpElements.get(
- Constants.ANQPElementType.HSOSUProviders);
- Intent intent = new Intent(ACTION_PASSPOINT_OSU_PROVIDERS_LIST);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(EXTRA_BSSID_LONG, bssid);
- intent.putExtra(EXTRA_ANQP_ELEMENT_DATA, osuProviders.getPayload());
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
- android.Manifest.permission.ACCESS_WIFI_STATE);
- }
}
@Override
@@ -206,7 +196,8 @@
public PasspointManager(Context context, WifiNative wifiNative, WifiKeyStore keyStore,
Clock clock, SIMAccessor simAccessor, PasspointObjectFactory objectFactory,
- WifiConfigManager wifiConfigManager, WifiConfigStore wifiConfigStore) {
+ WifiConfigManager wifiConfigManager, WifiConfigStore wifiConfigStore,
+ WifiMetrics wifiMetrics) {
mHandler = objectFactory.makePasspointEventHandler(wifiNative,
new CallbackHandler(context));
mKeyStore = keyStore;
@@ -217,6 +208,7 @@
mAnqpRequestManager = objectFactory.makeANQPRequestManager(mHandler, clock);
mCertVerifier = objectFactory.makeCertificateVerifier();
mWifiConfigManager = wifiConfigManager;
+ mWifiMetrics = wifiMetrics;
mProviderIndex = 0;
wifiConfigStore.registerStoreData(objectFactory.makePasspointConfigStoreData(
mKeyStore, mSimAccessor, new DataSourceHandler()));
@@ -234,6 +226,7 @@
* @return true if provider is added, false otherwise
*/
public boolean addOrUpdateProvider(PasspointConfiguration config, int uid) {
+ mWifiMetrics.incrementNumPasspointProviderInstallation();
if (config == null) {
Log.e(TAG, "Configuration not provided");
return false;
@@ -278,6 +271,7 @@
mWifiConfigManager.saveToStore(true /* forceWrite */);
Log.d(TAG, "Added/updated Passpoint configuration: " + config.getHomeSp().getFqdn()
+ " by " + uid);
+ mWifiMetrics.incrementNumPasspointProviderInstallSuccess();
return true;
}
@@ -288,6 +282,7 @@
* @return true if a provider is removed, false otherwise
*/
public boolean removeProvider(String fqdn) {
+ mWifiMetrics.incrementNumPasspointProviderUninstallation();
if (!mProviders.containsKey(fqdn)) {
Log.e(TAG, "Config doesn't exist");
return false;
@@ -297,6 +292,7 @@
mProviders.remove(fqdn);
mWifiConfigManager.saveToStore(true /* forceWrite */);
Log.d(TAG, "Removed Passpoint configuration: " + fqdn);
+ mWifiMetrics.incrementNumPasspointProviderUninstallSuccess();
return true;
}
@@ -337,7 +333,13 @@
scanResult.informationElements);
// Lookup ANQP data in the cache.
- long bssid = Utils.parseMac(scanResult.BSSID);
+ long bssid;
+ try {
+ bssid = Utils.parseMac(scanResult.BSSID);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Invalid BSSID provided in the scan result: " + scanResult.BSSID);
+ return null;
+ }
ANQPNetworkKey anqpKey = ANQPNetworkKey.buildKey(scanResult.SSID, bssid, scanResult.hessid,
vsa.anqpDomainID);
ANQPData anqpEntry = mAnqpCache.getEntry(anqpKey);
@@ -448,7 +450,13 @@
InformationElementUtil.getHS2VendorSpecificIE(scanResult.informationElements);
// Lookup ANQP data in the cache.
- long bssid = Utils.parseMac(scanResult.BSSID);
+ long bssid;
+ try {
+ bssid = Utils.parseMac(scanResult.BSSID);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Invalid BSSID provided in the scan result: " + scanResult.BSSID);
+ return new HashMap<Constants.ANQPElementType, ANQPElement>();
+ }
ANQPData anqpEntry = mAnqpCache.getEntry(ANQPNetworkKey.buildKey(
scanResult.SSID, bssid, scanResult.hessid, vsa.anqpDomainID));
if (anqpEntry != null) {
@@ -472,6 +480,10 @@
Log.e(TAG, "Attempt to get matching config for a null ScanResult");
return null;
}
+ if (!scanResult.isPasspointNetwork()) {
+ Log.e(TAG, "Attempt to get matching config for a non-Passpoint AP");
+ return null;
+ }
Pair<PasspointProvider, PasspointMatch> matchedProvider = matchProvider(scanResult);
if (matchedProvider == null) {
return null;
@@ -485,6 +497,80 @@
}
/**
+ * Return the list of Hosspot 2.0 OSU (Online Sign-Up) providers associated with the given
+ * AP.
+ *
+ * An empty list will be returned when an invalid scan result is provided or no match is found.
+ *
+ * @param scanResult The scan result of the AP
+ * @return List of {@link OsuProvider}
+ */
+ public List<OsuProvider> getMatchingOsuProviders(ScanResult scanResult) {
+ if (scanResult == null) {
+ Log.e(TAG, "Attempt to retrieve OSU providers for a null ScanResult");
+ return new ArrayList<OsuProvider>();
+ }
+ if (!scanResult.isPasspointNetwork()) {
+ Log.e(TAG, "Attempt to retrieve OSU providers for a non-Passpoint AP");
+ return new ArrayList<OsuProvider>();
+ }
+
+ // Lookup OSU Providers ANQP element.
+ Map<Constants.ANQPElementType, ANQPElement> anqpElements = getANQPElements(scanResult);
+ if (!anqpElements.containsKey(Constants.ANQPElementType.HSOSUProviders)) {
+ return new ArrayList<OsuProvider>();
+ }
+
+ HSOsuProvidersElement element =
+ (HSOsuProvidersElement) anqpElements.get(Constants.ANQPElementType.HSOSUProviders);
+ List<OsuProvider> providers = new ArrayList<>();
+ for (OsuProviderInfo info : element.getProviders()) {
+ // TODO(b/62256482): include icon data once the icon file retrieval and management
+ // support is added.
+ OsuProvider provider = new OsuProvider(element.getOsuSsid(), info.getFriendlyName(),
+ info.getServiceDescription(), info.getServerUri(),
+ info.getNetworkAccessIdentifier(), info.getMethodList(), null);
+ providers.add(provider);
+ }
+ return providers;
+ }
+
+ /**
+ * Invoked when a Passpoint network was successfully connected based on the credentials
+ * provided by the given Passpoint provider (specified by its FQDN).
+ *
+ * @param fqdn The FQDN of the Passpoint provider
+ */
+ public void onPasspointNetworkConnected(String fqdn) {
+ PasspointProvider provider = mProviders.get(fqdn);
+ if (provider == null) {
+ Log.e(TAG, "Passpoint network connected without provider: " + fqdn);
+ return;
+ }
+
+ if (!provider.getHasEverConnected()) {
+ // First successful connection using this provider.
+ provider.setHasEverConnected(true);
+ }
+ }
+
+ /**
+ * Update metrics related to installed Passpoint providers, this includes the number of
+ * installed providers and the number of those providers that results in a successful network
+ * connection.
+ */
+ public void updateMetrics() {
+ int numProviders = mProviders.size();
+ int numConnectedProviders = 0;
+ for (Map.Entry<String, PasspointProvider> entry : mProviders.entrySet()) {
+ if (entry.getValue().getHasEverConnected()) {
+ numConnectedProviders++;
+ }
+ }
+ mWifiMetrics.updateSavedPasspointProfiles(numProviders, numConnectedProviders);
+ }
+
+ /**
* Dump the current state of PasspointManager to the provided output stream.
*
* @param pw The output stream to write to
@@ -544,7 +630,7 @@
mSimAccessor, mProviderIndex++, wifiConfig.creatorUid,
enterpriseConfig.getCaCertificateAlias(),
enterpriseConfig.getClientCertificateAlias(),
- enterpriseConfig.getClientCertificateAlias());
+ enterpriseConfig.getClientCertificateAlias(), false);
mProviders.put(passpointConfig.getHomeSp().getFqdn(), provider);
return true;
}
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java
index 132a2f2..6ff5ee9 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java
@@ -92,6 +92,10 @@
Pair<PasspointProvider, PasspointMatch> bestProvider =
mPasspointManager.matchProvider(scanDetail.getScanResult());
if (bestProvider != null) {
+ if (bestProvider.first.isSimCredential() && !mWifiConfigManager.isSimPresent()) {
+ // Skip providers backed by SIM credential when SIM is not present.
+ continue;
+ }
candidateList.add(new PasspointNetworkCandidate(
bestProvider.first, bestProvider.second, scanDetail));
}
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java
index 33867bb..c7943ed 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java
@@ -87,14 +87,17 @@
private final int mEAPMethodID;
private final AuthParam mAuthParam;
+ private boolean mHasEverConnected;
+
public PasspointProvider(PasspointConfiguration config, WifiKeyStore keyStore,
SIMAccessor simAccessor, long providerId, int creatorUid) {
- this(config, keyStore, simAccessor, providerId, creatorUid, null, null, null);
+ this(config, keyStore, simAccessor, providerId, creatorUid, null, null, null, false);
}
public PasspointProvider(PasspointConfiguration config, WifiKeyStore keyStore,
SIMAccessor simAccessor, long providerId, int creatorUid, String caCertificateAlias,
- String clientCertificateAlias, String clientPrivateKeyAlias) {
+ String clientCertificateAlias, String clientPrivateKeyAlias,
+ boolean hasEverConnected) {
// Maintain a copy of the configuration to avoid it being updated by others.
mConfig = new PasspointConfiguration(config);
mKeyStore = keyStore;
@@ -103,6 +106,7 @@
mCaCertificateAlias = caCertificateAlias;
mClientCertificateAlias = clientCertificateAlias;
mClientPrivateKeyAlias = clientPrivateKeyAlias;
+ mHasEverConnected = hasEverConnected;
// Setup EAP method and authentication parameter based on the credential.
if (mConfig.getCredential().getUserCredential() != null) {
@@ -150,6 +154,14 @@
return mCreatorUid;
}
+ public boolean getHasEverConnected() {
+ return mHasEverConnected;
+ }
+
+ public void setHasEverConnected(boolean hasEverConnected) {
+ mHasEverConnected = hasEverConnected;
+ }
+
/**
* Install certificates and key based on current configuration.
* Note: the certificates and keys in the configuration will get cleared once
@@ -303,6 +315,13 @@
}
/**
+ * @return true if provider is backed by a SIM credential.
+ */
+ public boolean isSimCredential() {
+ return mConfig.getCredential().getSimCredential() != null;
+ }
+
+ /**
* Convert a legacy {@link WifiConfiguration} representation of a Passpoint configuration to
* a {@link PasspointConfiguration}. This is used for migrating legacy Passpoint
* configuration (release N and older).
diff --git a/service/java/com/android/server/wifi/hotspot2/Utils.java b/service/java/com/android/server/wifi/hotspot2/Utils.java
index dc04532..005af2a 100644
--- a/service/java/com/android/server/wifi/hotspot2/Utils.java
+++ b/service/java/com/android/server/wifi/hotspot2/Utils.java
@@ -44,7 +44,9 @@
}
public static long parseMac(String s) {
-
+ if (s == null) {
+ throw new IllegalArgumentException("Null MAC adddress");
+ }
long mac = 0;
int count = 0;
for (int n = 0; n < s.length(); n++) {
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/ANQPParser.java b/service/java/com/android/server/wifi/hotspot2/anqp/ANQPParser.java
index 7a06ef4..89bcfcb 100644
--- a/service/java/com/android/server/wifi/hotspot2/anqp/ANQPParser.java
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/ANQPParser.java
@@ -96,7 +96,7 @@
case HSConnCapability:
return HSConnectionCapabilityElement.parse(payload);
case HSOSUProviders:
- return RawByteElement.parse(infoID, payload);
+ return HSOsuProvidersElement.parse(payload);
default:
throw new ProtocolException("Unknown element ID: " + infoID);
}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/Constants.java b/service/java/com/android/server/wifi/hotspot2/anqp/Constants.java
index 7cf34c7..c45b3c5 100644
--- a/service/java/com/android/server/wifi/hotspot2/anqp/Constants.java
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/Constants.java
@@ -48,6 +48,7 @@
public static final int HS_NAI_HOME_REALM_QUERY = 6;
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,
@@ -64,7 +65,8 @@
HSConnCapability,
HSNAIHomeRealmQuery,
HSOSUProviders,
- HSIconRequest
+ HSIconRequest,
+ HSIconFile
}
private static final Map<Integer, ANQPElementType> sAnqpMap = new HashMap<>();
@@ -91,6 +93,7 @@
sHs20Map.put(HS_NAI_HOME_REALM_QUERY, ANQPElementType.HSNAIHomeRealmQuery);
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());
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/HSIconFileElement.java b/service/java/com/android/server/wifi/hotspot2/anqp/HSIconFileElement.java
new file mode 100644
index 0000000..ed0c472
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/HSIconFileElement.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2017 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.hotspot2.anqp;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.ByteBufferReader;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * The Icon Binary File vendor specific ANQP Element,
+ * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
+ * section 4.10.
+ *
+ * Format:
+ *
+ * | Status Code | Type Length | Type | Data Length | Data |
+ * 1 1 variable 2 variable
+ *
+ */
+public class HSIconFileElement extends ANQPElement {
+ private static final String TAG = "HSIconFileElement";
+
+ /**
+ * Icon download status code.
+ */
+ public static final int STATUS_CODE_SUCCESS = 0;
+ public static final int STATUS_CODE_FILE_NOT_FOUND = 1;
+ public static final int STATUS_CODE_UNSPECIFIED_ERROR = 2;
+
+ private final int mStatusCode;
+ private final String mIconType;
+ private final byte[] mIconData;
+
+ @VisibleForTesting
+ public HSIconFileElement(int statusCode, String iconType, byte[] iconData) {
+ super(Constants.ANQPElementType.HSIconFile);
+ mStatusCode = statusCode;
+ mIconType = iconType;
+ mIconData = iconData;
+ }
+
+ /**
+ * Parse a HSIconFileElement from the given buffer.
+ *
+ * @param payload The buffer to read from
+ * @return {@link HSIconFileElement}
+ * @throws BufferUnderflowException
+ * @throws ProtocolException
+ */
+ public static HSIconFileElement parse(ByteBuffer payload)
+ throws ProtocolException {
+ // Parse status code.
+ int status = payload.get() & 0xFF;
+ if (status != STATUS_CODE_SUCCESS) {
+ // No more data if status code is not success.
+ Log.e(TAG, "Icon file download failed: " + status);
+ return new HSIconFileElement(status, null, null);
+ }
+
+ // Parse icon type.
+ String iconType =
+ ByteBufferReader.readStringWithByteLength(payload, StandardCharsets.US_ASCII);
+
+ // Parse icon data.
+ int iconDataLength =
+ (int) ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 2) & 0xFFFF;
+ byte[] iconData = new byte[iconDataLength];
+ payload.get(iconData);
+
+ return new HSIconFileElement(status, iconType, iconData);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof HSIconFileElement)) {
+ return false;
+ }
+ HSIconFileElement that = (HSIconFileElement) thatObject;
+ return mStatusCode == that.mStatusCode
+ && TextUtils.equals(mIconType, that.mIconType)
+ && Arrays.equals(mIconData, that.mIconData);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mStatusCode, mIconType, Arrays.hashCode(mIconData));
+ }
+
+ @Override
+ public String toString() {
+ return "HSIconFileElement{" + "mStatusCode=" + mStatusCode
+ + "mIconType=" + mIconType + "}";
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/HSOsuProvidersElement.java b/service/java/com/android/server/wifi/hotspot2/anqp/HSOsuProvidersElement.java
new file mode 100644
index 0000000..146a44c
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/HSOsuProvidersElement.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2017 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.hotspot2.anqp;
+
+import android.net.wifi.WifiSsid;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The OSU Providers List vendor specific ANQP Element,
+ * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
+ * section 4.8.
+ *
+ * Format:
+ *
+ * | OSU SSID Length | OSU SSID | Number of OSU Providers | Provider #1 | ...
+ * 1 variable 1 variable
+ *
+ */
+public class HSOsuProvidersElement extends ANQPElement {
+ /**
+ * Maximum length for a SSID. Refer to IEEE 802.11-2012 Section 8.4.2.2
+ * for more info.
+ */
+ @VisibleForTesting
+ public static final int MAXIMUM_OSU_SSID_LENGTH = 32;
+
+ private final WifiSsid mOsuSsid;
+ private final List<OsuProviderInfo> mProviders;
+
+ @VisibleForTesting
+ public HSOsuProvidersElement(WifiSsid osuSsid, List<OsuProviderInfo> providers) {
+ super(Constants.ANQPElementType.HSOSUProviders);
+ mOsuSsid = osuSsid;
+ mProviders = providers;
+ }
+
+ /**
+ * Parse a HSOsuProvidersElement from the given buffer.
+ *
+ * @param payload The buffer to read from
+ * @return {@link HSOsuProvidersElement}
+ * @throws BufferUnderflowException
+ * @throws ProtocolException
+ */
+ public static HSOsuProvidersElement parse(ByteBuffer payload)
+ throws ProtocolException {
+ int ssidLength = payload.get() & 0xFF;
+ if (ssidLength > MAXIMUM_OSU_SSID_LENGTH) {
+ throw new ProtocolException("Invalid SSID length: " + ssidLength);
+ }
+ byte[] ssidBytes = new byte[ssidLength];
+ payload.get(ssidBytes);
+
+ int numProviders = payload.get() & 0xFF;
+ List<OsuProviderInfo> providers = new ArrayList<>();
+ while (numProviders > 0) {
+ providers.add(OsuProviderInfo.parse(payload));
+ numProviders--;
+ }
+
+ return new HSOsuProvidersElement(WifiSsid.createFromByteArray(ssidBytes), providers);
+ }
+
+ public WifiSsid getOsuSsid() {
+ return mOsuSsid;
+ }
+
+ public List<OsuProviderInfo> getProviders() {
+ return Collections.unmodifiableList(mProviders);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof HSOsuProvidersElement)) {
+ return false;
+ }
+ HSOsuProvidersElement that = (HSOsuProvidersElement) thatObject;
+ return (mOsuSsid == null ? that.mOsuSsid == null : mOsuSsid.equals(that.mOsuSsid))
+ && (mProviders == null ? that.mProviders == null
+ : mProviders.equals(that.mProviders));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mOsuSsid, mProviders);
+ }
+
+ @Override
+ public String toString() {
+ return "OSUProviders{" + "mOsuSsid=" + mOsuSsid + ", mProviders=" + mProviders + "}";
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/IconInfo.java b/service/java/com/android/server/wifi/hotspot2/anqp/IconInfo.java
index c961bbe..e35ca45 100644
--- a/service/java/com/android/server/wifi/hotspot2/anqp/IconInfo.java
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/IconInfo.java
@@ -1,37 +1,83 @@
+/*
+ * Copyright (C) 2017 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.hotspot2.anqp;
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.Locale;
+import android.text.TextUtils;
-import static com.android.server.wifi.hotspot2.anqp.Constants.SHORT_MASK;
-
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wifi.ByteBufferReader;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+
/**
* 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
+ *
+ * Format:
+ *
+ * | Width | Height | Language | Type Length | Type | Filename Length | Filename |
+ * 2 2 3 1 variable 1 variable
*/
public class IconInfo {
+ private static final int LANGUAGE_CODE_LENGTH = 3;
+
private final int mWidth;
private final int mHeight;
private final String mLanguage;
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");
- }
+ @VisibleForTesting
+ public IconInfo(int width, int height, String language, String iconType, String fileName) {
+ mWidth = width;
+ mHeight = height;
+ mLanguage = language;
+ mIconType = iconType;
+ mFileName = fileName;
+ }
- mWidth = payload.getShort() & SHORT_MASK;
- mHeight = payload.getShort() & SHORT_MASK;
- mLanguage = ByteBufferReader.readString(
- payload, Constants.LANG_CODE_LENGTH, StandardCharsets.US_ASCII).trim();
- mIconType = ByteBufferReader.readStringWithByteLength(payload, StandardCharsets.US_ASCII);
- mFileName = ByteBufferReader.readStringWithByteLength(payload, StandardCharsets.UTF_8);
+ /**
+ * Parse a IconInfo from the given buffer.
+ *
+ * @param payload The buffer to read from
+ * @return {@link IconInfo}
+ * @throws BufferUnderflowException
+ */
+ public static IconInfo parse(ByteBuffer payload) {
+ int width = (int) ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 2)
+ & 0xFFFF;
+ int height = (int) ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 2)
+ & 0xFFFF;
+
+ // Read the language string.
+ String language = ByteBufferReader.readString(
+ payload, LANGUAGE_CODE_LENGTH, StandardCharsets.US_ASCII).trim();
+
+ String iconType =
+ ByteBufferReader.readStringWithByteLength(payload, StandardCharsets.US_ASCII);
+ String fileName =
+ ByteBufferReader.readStringWithByteLength(payload, StandardCharsets.UTF_8);
+
+ return new IconInfo(width, height, language, iconType, fileName);
}
public int getWidth() {
@@ -59,36 +105,31 @@
if (this == thatObject) {
return true;
}
- if (thatObject == null || getClass() != thatObject.getClass()) {
+ if (!(thatObject instanceof IconInfo)) {
return false;
}
IconInfo that = (IconInfo) thatObject;
- return mHeight == that.mHeight &&
- mWidth == that.mWidth &&
- mFileName.equals(that.mFileName) &&
- mIconType.equals(that.mIconType) &&
- mLanguage.equals(that.mLanguage);
+ return mWidth == that.mWidth
+ && mHeight == that.mHeight
+ && TextUtils.equals(mLanguage, that.mLanguage)
+ && TextUtils.equals(mIconType, that.mIconType)
+ && TextUtils.equals(mFileName, that.mFileName);
}
@Override
public int hashCode() {
- int result = mWidth;
- result = 31 * result + mHeight;
- result = 31 * result + mLanguage.hashCode();
- result = 31 * result + mIconType.hashCode();
- result = 31 * result + mFileName.hashCode();
- return result;
+ return Objects.hash(mWidth, mHeight, mLanguage, mIconType, mFileName);
}
@Override
public String toString() {
- return "IconInfo{" +
- "Width=" + mWidth +
- ", Height=" + mHeight +
- ", Language=" + mLanguage +
- ", IconType='" + mIconType + '\'' +
- ", FileName='" + mFileName + '\'' +
- '}';
+ return "IconInfo{"
+ + "Width=" + mWidth
+ + ", Height=" + mHeight
+ + ", Language=" + mLanguage
+ + ", IconType='" + mIconType + "\'"
+ + ", FileName='" + mFileName + "\'"
+ + "}";
}
}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/OsuProviderInfo.java b/service/java/com/android/server/wifi/hotspot2/anqp/OsuProviderInfo.java
new file mode 100644
index 0000000..8952c5a
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/OsuProviderInfo.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2017 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.hotspot2.anqp;
+
+import android.net.Uri;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.ByteBufferReader;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+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.Locale;
+import java.util.Objects;
+
+/**
+ * The OSU Provider subfield in the OSU Providers List ANQP Element,
+ * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
+ * section 4.8.1
+ *
+ * Format:
+ *
+ * | Length | Friendly Name Length | Friendly Name #1 | ... | Friendly Name #n |
+ * 2 2 variable variable
+ * | Server URI length | Server URI | Method List Length | Method List |
+ * 1 variable 1 variable
+ * | Icon Available Length | Icon Available | NAI Length | NAI | Description Length |
+ * 2 variable 1 variable 2
+ * | Description #1 | ... | Description #n |
+ * variable variable
+ *
+ * | Operator Name Duple #N (optional) |
+ * variable
+ */
+public class OsuProviderInfo {
+ /**
+ * The raw payload should minimum include the following fields:
+ * - Friendly Name Length (2)
+ * - Server URI Length (1)
+ * - Method List Length (1)
+ * - Icon Available Length (2)
+ * - NAI Length (1)
+ * - Description Length (2)
+ */
+ @VisibleForTesting
+ public static final int MINIMUM_LENGTH = 9;
+
+ /**
+ * Maximum octets for a I18N string.
+ */
+ private static final int MAXIMUM_I18N_STRING_LENGTH = 252;
+
+ private final List<I18Name> mFriendlyNames;
+ private final Uri mServerUri;
+ private final List<Integer> mMethodList;
+ private final List<IconInfo> mIconInfoList;
+ private final String mNetworkAccessIdentifier;
+ private final List<I18Name> mServiceDescriptions;
+
+ @VisibleForTesting
+ public OsuProviderInfo(List<I18Name> friendlyNames, Uri serverUri, List<Integer> methodList,
+ List<IconInfo> iconInfoList, String nai, List<I18Name> serviceDescriptions) {
+ mFriendlyNames = friendlyNames;
+ mServerUri = serverUri;
+ mMethodList = methodList;
+ mIconInfoList = iconInfoList;
+ mNetworkAccessIdentifier = nai;
+ mServiceDescriptions = serviceDescriptions;
+ }
+
+ /**
+ * Parse a OsuProviderInfo from the given buffer.
+ *
+ * @param payload The buffer to read from
+ * @return {@link OsuProviderInfo}
+ * @throws BufferUnderflowException
+ * @throws ProtocolException
+ */
+ public static OsuProviderInfo parse(ByteBuffer payload)
+ throws ProtocolException {
+ // Parse length field.
+ int length = (int) ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 2)
+ & 0xFFFF;
+ if (length < MINIMUM_LENGTH) {
+ throw new ProtocolException("Invalid length value: " + length);
+ }
+
+ // Parse friendly names.
+ int friendlyNameLength =
+ (int) ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 2) & 0xFFFF;
+ ByteBuffer friendlyNameBuffer = getSubBuffer(payload, friendlyNameLength);
+ List<I18Name> friendlyNameList = parseI18Names(friendlyNameBuffer);
+
+ // Parse server URI.
+ Uri serverUri = Uri.parse(
+ ByteBufferReader.readStringWithByteLength(payload, StandardCharsets.UTF_8));
+
+ // Parse method list.
+ int methodListLength = payload.get() & 0xFF;
+ List<Integer> methodList = new ArrayList<>();
+ while (methodListLength > 0) {
+ methodList.add(payload.get() & 0xFF);
+ methodListLength--;
+ }
+
+ // Parse list of icon info.
+ int availableIconLength =
+ (int) ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 2) & 0xFFFF;
+ ByteBuffer iconBuffer = getSubBuffer(payload, availableIconLength);
+ List<IconInfo> iconInfoList = new ArrayList<>();
+ while (iconBuffer.hasRemaining()) {
+ iconInfoList.add(IconInfo.parse(iconBuffer));
+ }
+
+ // Parse Network Access Identifier.
+ String nai = ByteBufferReader.readStringWithByteLength(payload, StandardCharsets.UTF_8);
+
+ // Parse service descriptions.
+ int serviceDescriptionLength =
+ (int) ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 2) & 0xFFFF;
+ ByteBuffer descriptionsBuffer = getSubBuffer(payload, serviceDescriptionLength);
+ List<I18Name> serviceDescriptionList = parseI18Names(descriptionsBuffer);
+
+ return new OsuProviderInfo(friendlyNameList, serverUri, methodList, iconInfoList, nai,
+ serviceDescriptionList);
+ }
+
+ public List<I18Name> getFriendlyNames() {
+ return Collections.unmodifiableList(mFriendlyNames);
+ }
+
+ public Uri getServerUri() {
+ return mServerUri;
+ }
+
+ public List<Integer> getMethodList() {
+ return Collections.unmodifiableList(mMethodList);
+ }
+
+ public List<IconInfo> getIconInfoList() {
+ return Collections.unmodifiableList(mIconInfoList);
+ }
+
+ public String getNetworkAccessIdentifier() {
+ return mNetworkAccessIdentifier;
+ }
+
+ public List<I18Name> getServiceDescriptions() {
+ return Collections.unmodifiableList(mServiceDescriptions);
+ }
+
+ /**
+ * Return the friendly name string from the friendly name list. The string matching
+ * the default locale will be returned if it is found, otherwise the first name in the list
+ * will be returned. A null will be returned if the list is empty.
+ *
+ * @return friendly name string
+ */
+ public String getFriendlyName() {
+ return getI18String(mFriendlyNames);
+ }
+
+ /**
+ * Return the service description string from the service description list. The string
+ * matching the default locale will be returned if it is found, otherwise the first element in
+ * the list will be returned. A null will be returned if the list is empty.
+ *
+ * @return service description string
+ */
+ public String getServiceDescription() {
+ return getI18String(mServiceDescriptions);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof OsuProviderInfo)) {
+ return false;
+ }
+ OsuProviderInfo that = (OsuProviderInfo) thatObject;
+ return (mFriendlyNames == null ? that.mFriendlyNames == null
+ : mFriendlyNames.equals(that.mFriendlyNames))
+ && (mServerUri == null ? that.mServerUri == null
+ : mServerUri.equals(that.mServerUri))
+ && (mMethodList == null ? that.mMethodList == null
+ : mMethodList.equals(that.mMethodList))
+ && (mIconInfoList == null ? that.mIconInfoList == null
+ : mIconInfoList.equals(that.mIconInfoList))
+ && TextUtils.equals(mNetworkAccessIdentifier, that.mNetworkAccessIdentifier)
+ && (mServiceDescriptions == null ? that.mServiceDescriptions == null
+ : mServiceDescriptions.equals(that.mServiceDescriptions));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFriendlyNames, mServerUri, mMethodList, mIconInfoList,
+ mNetworkAccessIdentifier, mServiceDescriptions);
+ }
+
+ @Override
+ public String toString() {
+ return "OsuProviderInfo{"
+ + "mFriendlyNames=" + mFriendlyNames
+ + ", mServerUri=" + mServerUri
+ + ", mMethodList=" + mMethodList
+ + ", mIconInfoList=" + mIconInfoList
+ + ", mNetworkAccessIdentifier=" + mNetworkAccessIdentifier
+ + ", mServiceDescriptions=" + mServiceDescriptions
+ + "}";
+ }
+
+ /**
+ * Parse list of I18N string from the given payload.
+ *
+ * @param payload The payload to parse from
+ * @return List of {@link I18Name}
+ * @throws ProtocolException
+ */
+ private static List<I18Name> parseI18Names(ByteBuffer payload) throws ProtocolException {
+ List<I18Name> results = new ArrayList<>();
+ while (payload.hasRemaining()) {
+ I18Name name = I18Name.parse(payload);
+ // Verify that the number of bytes for the operator name doesn't exceed the max
+ // allowed.
+ int textBytes = name.getText().getBytes(StandardCharsets.UTF_8).length;
+ if (textBytes > MAXIMUM_I18N_STRING_LENGTH) {
+ throw new ProtocolException("I18Name string exceeds the maximum allowed "
+ + textBytes);
+ }
+ results.add(name);
+ }
+ return results;
+ }
+
+ /**
+ * Creates a new byte buffer whose content is a shared subsequence of
+ * the given buffer's content.
+ *
+ * The sub buffer will starts from |payload|'s current position
+ * and ends at |payload|'s current position plus |length|. The |payload|'s current
+ * position will advance pass |length| bytes.
+ *
+ * @param payload The original buffer
+ * @param length The length of the new buffer
+ * @return {@link ByteBuffer}
+ * @throws BufferUnderflowException
+ */
+ private static ByteBuffer getSubBuffer(ByteBuffer payload, int length) {
+ if (payload.remaining() < length) {
+ throw new BufferUnderflowException();
+ }
+ // Set the subBuffer's starting and ending position.
+ ByteBuffer subBuffer = payload.slice();
+ subBuffer.limit(length);
+ // Advance the original buffer's current position.
+ payload.position(payload.position() + length);
+ return subBuffer;
+ }
+
+ /**
+ * Return the appropriate I18 string value from the list of I18 string values.
+ * The string matching the default locale will be returned if it is found, otherwise the
+ * first string in the list will be returned. A null will be returned if the list is empty.
+ *
+ * @param i18Strings List of I18 string values
+ * @return String matching the default locale, null otherwise
+ */
+ private static String getI18String(List<I18Name> i18Strings) {
+ for (I18Name name : i18Strings) {
+ if (name.getLanguage().equals(Locale.getDefault().getLanguage())) {
+ return name.getText();
+ }
+ }
+ if (i18Strings.size() > 0) {
+ return i18Strings.get(0).getText();
+ }
+ return null;
+ }
+}
diff --git a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHal.java b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHal.java
index 3e26828..50f2ccf 100644
--- a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHal.java
+++ b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHal.java
@@ -937,7 +937,8 @@
String ssidString = null;
if (ssid != null) {
try {
- ssidString = NativeUtil.encodeSsid(ssid);
+ ssidString = NativeUtil.removeEnclosingQuotes(
+ NativeUtil.encodeSsid(ssid));
} catch (Exception e) {
Log.e(TAG, "Could not encode SSID.", e);
}
diff --git a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
index 623a2f0..d70ce41 100644
--- a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
+++ b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
@@ -24,6 +24,7 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.hardware.wifi.V1_0.IWifiP2pIface;
import android.net.ConnectivityManager;
import android.net.DhcpResults;
import android.net.InterfaceConfiguration;
@@ -77,6 +78,7 @@
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.server.wifi.HalDeviceManager;
import com.android.server.wifi.WifiInjector;
import com.android.server.wifi.WifiStateMachine;
import com.android.server.wifi.util.WifiAsyncChannel;
@@ -91,6 +93,7 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* WifiP2pService includes a state machine to perform Wi-Fi p2p operations. Applications
@@ -228,6 +231,9 @@
// the ranges defined in Tethering.java
private static final String SERVER_ADDRESS = "192.168.49.1";
+ // The empty device address set by wpa_supplicant.
+ private static final String EMPTY_DEVICE_ADDRESS = "00:00:00:00:00:00";
+
/**
* Error code definition.
* see the Table.8 in the WiFi Direct specification for the detail.
@@ -358,6 +364,25 @@
}
private ClientHandler mClientHandler;
+ private class DeathHandlerData {
+ DeathHandlerData(DeathRecipient dr, Messenger m) {
+ mDeathRecipient = dr;
+ mMessenger = m;
+ }
+
+ @Override
+ public String toString() {
+ return "deathRecipient=" + mDeathRecipient + ", messenger=" + mMessenger;
+ }
+
+ DeathRecipient mDeathRecipient;
+ Messenger mMessenger;
+ }
+ private Object mLock = new Object();
+ private final Map<IBinder, DeathHandlerData> mDeathDataByBinder = new HashMap<>();
+ private HalDeviceManager mHalDeviceManager;
+ private IWifiP2pIface mIWifiP2pIface;
+
public WifiP2pServiceImpl(Context context) {
mContext = context;
@@ -468,10 +493,47 @@
* an AsyncChannel communication with WifiP2pService
*/
@Override
- public Messenger getMessenger() {
+ public Messenger getMessenger(final IBinder binder) {
enforceAccessPermission();
enforceChangePermission();
- return new Messenger(mClientHandler);
+
+ synchronized (mLock) {
+ final Messenger messenger = new Messenger(mClientHandler);
+ if (DBG) {
+ Log.d(TAG, "getMessenger: uid=" + getCallingUid() + ", binder=" + binder
+ + ", messenger=" + messenger);
+ }
+
+ IBinder.DeathRecipient dr = () -> {
+ if (DBG) Log.d(TAG, "binderDied: binder=" + binder);
+ close(binder);
+ };
+
+ try {
+ binder.linkToDeath(dr, 0);
+ mDeathDataByBinder.put(binder, new DeathHandlerData(dr, messenger));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error on linkToDeath: e=" + e);
+ // fall-through here - won't clean up
+ }
+
+ if (mIWifiP2pIface == null) {
+ if (mHalDeviceManager == null) {
+ if (mWifiInjector == null) {
+ mWifiInjector = WifiInjector.getInstance();
+ }
+ mHalDeviceManager = mWifiInjector.getHalDeviceManager();
+ }
+ mIWifiP2pIface = mHalDeviceManager.createP2pIface(() -> {
+ if (DBG) Log.d(TAG, "IWifiP2pIface destroyedListener");
+ synchronized (mLock) {
+ mIWifiP2pIface = null;
+ }
+ }, mP2pStateMachine.getHandler().getLooper());
+ }
+
+ return messenger;
+ }
}
/**
@@ -487,6 +549,45 @@
return new Messenger(mP2pStateMachine.getHandler());
}
+ /**
+ * Clean-up the state and configuration requested by the closing app. Takes same action as
+ * when the app dies (binder death).
+ */
+ @Override
+ public void close(IBinder binder) {
+ enforceAccessPermission();
+ enforceChangePermission();
+
+ DeathHandlerData dhd;
+ synchronized (mLock) {
+ dhd = mDeathDataByBinder.get(binder);
+ if (dhd == null) {
+ Log.w(TAG, "close(): no death recipient for binder");
+ return;
+ }
+
+ binder.unlinkToDeath(dhd.mDeathRecipient, 0);
+ mDeathDataByBinder.remove(binder);
+
+ // clean-up if there are no more clients registered
+ // TODO: what does the WifiStateMachine client do? It isn't tracked through here!
+ if (dhd.mMessenger != null && mDeathDataByBinder.isEmpty()) {
+ try {
+ dhd.mMessenger.send(
+ mClientHandler.obtainMessage(WifiP2pManager.STOP_DISCOVERY));
+ dhd.mMessenger.send(mClientHandler.obtainMessage(WifiP2pManager.REMOVE_GROUP));
+ } catch (RemoteException e) {
+ Log.e(TAG, "close: Failed sending clean-up commands: e=" + e);
+ }
+
+ if (mIWifiP2pIface != null) {
+ mHalDeviceManager.removeIface(mIWifiP2pIface);
+ mIWifiP2pIface = null;
+ }
+ }
+ }
+ }
+
/** This is used to provide information to drivers to optimize performance depending
* on the current mode of operation.
* 0 - disabled
@@ -538,6 +639,7 @@
pw.println("mNetworkInfo " + mNetworkInfo);
pw.println("mTemporarilyDisconnectedWifi " + mTemporarilyDisconnectedWifi);
pw.println("mServiceDiscReqId " + mServiceDiscReqId);
+ pw.println("mDeathDataByBinder " + mDeathDataByBinder);
pw.println();
final IpManager ipManager = mIpManager;
@@ -1491,7 +1593,11 @@
}
mGroup = (WifiP2pGroup) message.obj;
if (DBG) logd(getName() + " group started");
-
+ if (mGroup.isGroupOwner()
+ && EMPTY_DEVICE_ADDRESS.equals(mGroup.getOwner().deviceAddress)) {
+ // wpa_supplicant doesn't set own device address to go_dev_addr.
+ mGroup.getOwner().deviceAddress = mThisDevice.deviceAddress;
+ }
// We hit this scenario when a persistent group is reinvoked
if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
mAutonomousGroup = false;
@@ -1824,9 +1930,14 @@
}
mGroup = (WifiP2pGroup) message.obj;
if (DBG) logd(getName() + " group started");
+ if (mGroup.isGroupOwner()
+ && EMPTY_DEVICE_ADDRESS.equals(mGroup.getOwner().deviceAddress)) {
+ // wpa_supplicant doesn't set own device address to go_dev_addr.
+ mGroup.getOwner().deviceAddress = mThisDevice.deviceAddress;
+ }
if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
// update cache information and set network id to mGroup.
- updatePersistentNetworks(NO_RELOAD);
+ updatePersistentNetworks(RELOAD);
String devAddr = mGroup.getOwner().deviceAddress;
mGroup.setNetworkId(mGroups.getNetworkId(devAddr,
mGroup.getNetworkName()));
@@ -2245,12 +2356,7 @@
int netId = mGroup.getNetworkId();
if (netId >= 0) {
if (DBG) logd("Remove unknown client from the list");
- if (!removeClientFromList(netId,
- mSavedPeerConfig.deviceAddress, false)) {
- // not found the client on the list
- loge("Already removed the client, ignore");
- break;
- }
+ removeClientFromList(netId, mSavedPeerConfig.deviceAddress, false);
// try invitation.
sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig);
}
diff --git a/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java b/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java
index 211e62a..0fadd80 100644
--- a/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java
+++ b/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java
@@ -27,6 +27,9 @@
import com.android.server.wifi.WifiMonitor;
import com.android.server.wifi.WifiNative;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
/**
* WifiScanner implementation that takes advantage of the gscan HAL API
* The gscan API is used to perform background scans and wificond is used for oneshot scans.
@@ -151,4 +154,9 @@
return mWificondScannerDelegate.shouldScheduleBackgroundScanForHwPno();
}
}
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mWificondScannerDelegate.dump(fd, pw, args);
+ }
}
diff --git a/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java b/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java
index e0fb535..5281b3a 100644
--- a/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java
+++ b/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java
@@ -26,6 +26,8 @@
import com.android.server.wifi.WifiMonitor;
import com.android.server.wifi.WifiNative;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.Comparator;
/**
@@ -158,4 +160,6 @@
* @return true if background scan needs to be started, false otherwise.
*/
public abstract boolean shouldScheduleBackgroundScanForHwPno();
+
+ protected abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
}
diff --git a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
index af874b9..ab2a5dc 100644
--- a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
+++ b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
@@ -2156,6 +2156,9 @@
}
pw.println();
}
+ if (mScannerImpl != null) {
+ mScannerImpl.dump(fd, pw, args);
+ }
}
void logScanRequest(String request, ClientInfo ci, int id, WorkSource workSource,
diff --git a/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java b/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
index fd7fddb..84105ee 100644
--- a/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
+++ b/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
@@ -32,6 +32,8 @@
import com.android.server.wifi.WifiNative;
import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
@@ -81,6 +83,7 @@
private boolean mBackgroundScanPaused = false;
private ScanBuffer mBackgroundScanBuffer = new ScanBuffer(SCAN_BUFFER_CAPACITY);
+ private ArrayList<ScanDetail> mNativeScanResults;
private WifiScanner.ScanData mLatestSingleScanResult =
new WifiScanner.ScanData(0, 0, new ScanResult[0]);
@@ -535,11 +538,11 @@
// got a scan before we started scanning or after scan was canceled
return;
}
- ArrayList<ScanDetail> nativeResults = mWifiNative.getScanResults();
+ mNativeScanResults = mWifiNative.getScanResults();
List<ScanResult> hwPnoScanResults = new ArrayList<>();
int numFilteredScanResults = 0;
- for (int i = 0; i < nativeResults.size(); ++i) {
- ScanResult result = nativeResults.get(i).getScanResult();
+ for (int i = 0; i < mNativeScanResults.size(); ++i) {
+ ScanResult result = mNativeScanResults.get(i).getScanResult();
long timestamp_ms = result.timestamp / 1000; // convert us -> ms
if (timestamp_ms > mLastScanSettings.startTime) {
if (mLastScanSettings.hwPnoScanActive) {
@@ -556,11 +559,8 @@
if (mLastScanSettings.hwPnoScanActive
&& mLastScanSettings.pnoScanEventHandler != null) {
- ScanResult[] pnoScanResultsArray = new ScanResult[hwPnoScanResults.size()];
- for (int i = 0; i < pnoScanResultsArray.length; ++i) {
- ScanResult result = nativeResults.get(i).getScanResult();
- pnoScanResultsArray[i] = hwPnoScanResults.get(i);
- }
+ ScanResult[] pnoScanResultsArray =
+ hwPnoScanResults.toArray(new ScanResult[hwPnoScanResults.size()]);
mLastScanSettings.pnoScanEventHandler.onPnoNetworkFound(pnoScanResultsArray);
}
// On pno scan result event, we are expecting a mLastScanSettings for pno scan.
@@ -598,12 +598,12 @@
}
if (DBG) Log.d(TAG, "Polling scan data for scan: " + mLastScanSettings.scanId);
- ArrayList<ScanDetail> nativeResults = mWifiNative.getScanResults();
+ mNativeScanResults = mWifiNative.getScanResults();
List<ScanResult> singleScanResults = new ArrayList<>();
List<ScanResult> backgroundScanResults = new ArrayList<>();
int numFilteredScanResults = 0;
- for (int i = 0; i < nativeResults.size(); ++i) {
- ScanResult result = nativeResults.get(i).getScanResult();
+ for (int i = 0; i < mNativeScanResults.size(); ++i) {
+ ScanResult result = mNativeScanResults.get(i).getScanResult();
long timestamp_ms = result.timestamp / 1000; // convert us -> ms
if (timestamp_ms > mLastScanSettings.startTime) {
if (mLastScanSettings.backgroundScanActive) {
@@ -772,6 +772,40 @@
return false;
}
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ synchronized (mSettingsLock) {
+ pw.println("Latest native scan results:");
+ if (mNativeScanResults != null && mNativeScanResults.size() != 0) {
+ long nowMs = mClock.getElapsedSinceBootMillis();
+ pw.println(" BSSID Frequency RSSI Age(sec) SSID "
+ + " Flags");
+ for (ScanDetail scanDetail : mNativeScanResults) {
+ ScanResult r = scanDetail.getScanResult();
+ long timeStampMs = r.timestamp / 1000;
+ String age;
+ if (timeStampMs <= 0) {
+ age = "___?___";
+ } else if (nowMs < timeStampMs) {
+ age = " 0.000";
+ } else if (timeStampMs < nowMs - 1000000) {
+ age = ">1000.0";
+ } else {
+ age = String.format("%3.3f", (nowMs - timeStampMs) / 1000.0);
+ }
+ String ssid = r.SSID == null ? "" : r.SSID;
+ pw.printf(" %17s %9d %5d %7s %-32s %s\n",
+ r.BSSID,
+ r.frequency,
+ r.level,
+ age,
+ String.format("%1.32s", ssid),
+ r.capabilities);
+ }
+ }
+ }
+ }
+
private static class LastScanSettings {
public long startTime;
diff --git a/service/java/com/android/server/wifi/util/ScanResultUtil.java b/service/java/com/android/server/wifi/util/ScanResultUtil.java
index 0e08701..4fcafb8 100644
--- a/service/java/com/android/server/wifi/util/ScanResultUtil.java
+++ b/service/java/com/android/server/wifi/util/ScanResultUtil.java
@@ -18,11 +18,9 @@
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
-import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wifi.ScanDetail;
-import com.android.server.wifi.WifiConfigurationUtil;
import com.android.server.wifi.hotspot2.NetworkDetail;
/**
@@ -89,35 +87,6 @@
}
/**
- * Checks if the provided |scanResult| match with the provided |config|. Essentially checks
- * if the network config and scan result have the same SSID and encryption type.
- */
- public static boolean doesScanResultMatchWithNetwork(
- ScanResult scanResult, WifiConfiguration config) {
- // Add the double quotes to the scan result SSID for comparison with the network configs.
- String configSSID = createQuotedSSID(scanResult.SSID);
- if (TextUtils.equals(config.SSID, configSSID)) {
- if (ScanResultUtil.isScanResultForPskNetwork(scanResult)
- && WifiConfigurationUtil.isConfigForPskNetwork(config)) {
- return true;
- }
- if (ScanResultUtil.isScanResultForEapNetwork(scanResult)
- && WifiConfigurationUtil.isConfigForEapNetwork(config)) {
- return true;
- }
- if (ScanResultUtil.isScanResultForWepNetwork(scanResult)
- && WifiConfigurationUtil.isConfigForWepNetwork(config)) {
- return true;
- }
- if (ScanResultUtil.isScanResultForOpenNetwork(scanResult)
- && WifiConfigurationUtil.isConfigForOpenNetwork(config)) {
- return true;
- }
- }
- return false;
- }
-
- /**
* Creates a network configuration object using the provided |scanResult|.
* This is used to create ephemeral network configurations.
*/
diff --git a/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java b/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java
index 90ec060..c5eea7d 100644
--- a/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java
+++ b/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java
@@ -76,6 +76,22 @@
}
/**
+ * Checks if the app has the permission to change Wi-Fi network configuration or not.
+ *
+ * @param uid uid of the app.
+ * @return true if the app does have the permission, false otherwise.
+ */
+ public boolean checkChangePermission(int uid) {
+ try {
+ int permission = mWifiPermissionsWrapper.getChangeWifiConfigPermission(uid);
+ return (permission == PackageManager.PERMISSION_GRANTED);
+ } catch (RemoteException e) {
+ mLog.err("Error checking for permission: %").r(e.getMessage()).flush();
+ return false;
+ }
+ }
+
+ /**
* Check and enforce tether change permission.
*
* @param context Context object of the caller.
@@ -240,4 +256,13 @@
return (mSettingsStore.getLocationModeSetting(mContext)
!= Settings.Secure.LOCATION_MODE_OFF);
}
+
+ /**
+ * Returns true if the |uid| holds NETWORK_SETTINGS permission.
+ */
+ public boolean checkNetworkSettingsPermission(int uid) {
+ return mWifiPermissionsWrapper.getUidPermission(
+ android.Manifest.permission.NETWORK_SETTINGS, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
}
diff --git a/service/java/com/android/server/wifi/util/WifiPermissionsWrapper.java b/service/java/com/android/server/wifi/util/WifiPermissionsWrapper.java
index 6ca2f02..6fde01e 100644
--- a/service/java/com/android/server/wifi/util/WifiPermissionsWrapper.java
+++ b/service/java/com/android/server/wifi/util/WifiPermissionsWrapper.java
@@ -16,6 +16,7 @@
package com.android.server.wifi.util;
+import android.Manifest;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.admin.DevicePolicyManagerInternal;
@@ -95,4 +96,16 @@
return AppGlobals.getPackageManager().checkUidPermission(
android.Manifest.permission.OVERRIDE_WIFI_CONFIG, uid);
}
+
+ /**
+ * Determines if the caller has the change wifi config permission.
+ *
+ * @param uid to check the permission for
+ * @return int representation of success or denied
+ * @throws RemoteException
+ */
+ public int getChangeWifiConfigPermission(int uid) throws RemoteException {
+ return AppGlobals.getPackageManager().checkUidPermission(
+ Manifest.permission.CHANGE_WIFI_STATE, uid);
+ }
}
diff --git a/tests/wifitests/Android.mk b/tests/wifitests/Android.mk
index 94f3435..1e64ddb 100644
--- a/tests/wifitests/Android.mk
+++ b/tests/wifitests/Android.mk
@@ -114,4 +114,6 @@
LOCAL_PACKAGE_NAME := FrameworksWifiTests
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
include $(BUILD_PACKAGE)
diff --git a/tests/wifitests/AndroidManifest.xml b/tests/wifitests/AndroidManifest.xml
index 63b9542..dcb61c7 100644
--- a/tests/wifitests/AndroidManifest.xml
+++ b/tests/wifitests/AndroidManifest.xml
@@ -30,7 +30,7 @@
</activity>
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="com.android.server.wifi.CustomTestRunner"
android:targetPackage="com.android.server.wifi.test"
android:label="Frameworks Wifi Tests">
</instrumentation>
diff --git a/tests/wifitests/AndroidTest.xml b/tests/wifitests/AndroidTest.xml
new file mode 100644
index 0000000..4fe21aa
--- /dev/null
+++ b/tests/wifitests/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<configuration description="Runs Frameworks Wifi Tests.">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="FrameworksWifiTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-tag" value="FrameworksWifiTests" />
+ <test class="com.android.tradefed.testtype.InstrumentationTest" >
+ <option name="package" value="com.android.server.wifi.test" />
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
diff --git a/tests/wifitests/coverage.sh b/tests/wifitests/coverage.sh
index c81af56..3ed71ad 100755
--- a/tests/wifitests/coverage.sh
+++ b/tests/wifitests/coverage.sh
@@ -49,7 +49,7 @@
adb install -r -g "$OUT/data/app/FrameworksWifiTests/FrameworksWifiTests.apk"
-adb shell am instrument -e coverage true -w 'com.android.server.wifi.test/android.support.test.runner.AndroidJUnitRunner'
+adb shell am instrument -e coverage true -w 'com.android.server.wifi.test/com.android.server.wifi.CustomTestRunner'
mkdir -p $OUTPUT_DIR
diff --git a/tests/wifitests/runtests.sh b/tests/wifitests/runtests.sh
index 529a535..7a34bf7 100755
--- a/tests/wifitests/runtests.sh
+++ b/tests/wifitests/runtests.sh
@@ -42,4 +42,4 @@
adb shell am instrument -w "$@" \
-e notAnnotation com.android.server.wifi.DisabledForUpdateToAnyMatcher \
- 'com.android.server.wifi.test/android.support.test.runner.AndroidJUnitRunner'
+ 'com.android.server.wifi.test/com.android.server.wifi.CustomTestRunner'
diff --git a/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java b/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java
index 6827d95..e30a82f 100644
--- a/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java
@@ -17,12 +17,14 @@
package com.android.server.wifi;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.when;
import android.app.test.MockAnswerUtil.AnswerWithArguments;
import android.content.pm.UserInfo;
+import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.os.UserHandle;
import android.os.UserManager;
@@ -36,6 +38,7 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -89,19 +92,38 @@
mConfigs = new ConfigurationMap(mUserManager);
}
- public void switchUser(int newUserId) {
+ private void switchUser(int newUserId) {
mCurrentUserId = newUserId;
mConfigs.setNewUser(newUserId);
mConfigs.clear();
}
- public void addNetworks(List<WifiConfiguration> configs) {
+ private Collection<WifiConfiguration> getEnabledNetworksForCurrentUser() {
+ List<WifiConfiguration> list = new ArrayList<>();
+ for (WifiConfiguration config : mConfigs.valuesForCurrentUser()) {
+ if (config.status != WifiConfiguration.Status.DISABLED) {
+ list.add(config);
+ }
+ }
+ return list;
+ }
+
+ private WifiConfiguration getEphemeralForCurrentUser(String ssid) {
+ for (WifiConfiguration config : mConfigs.valuesForCurrentUser()) {
+ if (ssid.equals(config.SSID) && config.ephemeral) {
+ return config;
+ }
+ }
+ return null;
+ }
+
+ private void addNetworks(List<WifiConfiguration> configs) {
for (WifiConfiguration config : configs) {
assertNull(mConfigs.put(config));
}
}
- public void verifyGetters(List<WifiConfiguration> configs) {
+ private void verifyGetters(List<WifiConfiguration> configs) {
final Set<WifiConfiguration> configsForCurrentUser = new HashSet<>();
final Set<WifiConfiguration> enabledConfigsForCurrentUser = new HashSet<>();
final List<WifiConfiguration> configsNotForCurrentUser = new ArrayList<>();
@@ -127,15 +149,12 @@
// visible to the current user.
for (WifiConfiguration config : configsForCurrentUser) {
assertEquals(config, mConfigs.getForCurrentUser(config.networkId));
- if (config.FQDN != null) {
- assertEquals(config, mConfigs.getByFQDNForCurrentUser(config.FQDN));
- }
assertEquals(config, mConfigs.getByConfigKeyForCurrentUser(config.configKey()));
final boolean wasEphemeral = config.ephemeral;
config.ephemeral = false;
- assertNull(mConfigs.getEphemeralForCurrentUser(config.SSID));
+ assertNull(getEphemeralForCurrentUser(config.SSID));
config.ephemeral = true;
- assertEquals(config, mConfigs.getEphemeralForCurrentUser(config.SSID));
+ assertEquals(config, getEphemeralForCurrentUser(config.SSID));
config.ephemeral = wasEphemeral;
}
@@ -143,15 +162,12 @@
// visible to the current user.
for (WifiConfiguration config : configsNotForCurrentUser) {
assertNull(mConfigs.getForCurrentUser(config.networkId));
- if (config.FQDN != null) {
- assertNull(mConfigs.getByFQDNForCurrentUser(config.FQDN));
- }
assertNull(mConfigs.getByConfigKeyForCurrentUser(config.configKey()));
final boolean wasEphemeral = config.ephemeral;
config.ephemeral = false;
- assertNull(mConfigs.getEphemeralForCurrentUser(config.SSID));
+ assertNull(getEphemeralForCurrentUser(config.SSID));
config.ephemeral = true;
- assertNull(mConfigs.getEphemeralForCurrentUser(config.SSID));
+ assertNull(getEphemeralForCurrentUser(config.SSID));
config.ephemeral = wasEphemeral;
}
@@ -160,11 +176,29 @@
assertEquals(configs.size(), mConfigs.sizeForAllUsers());
assertEquals(configsForCurrentUser.size(), mConfigs.sizeForCurrentUser());
assertEquals(enabledConfigsForCurrentUser,
- new HashSet<WifiConfiguration>(mConfigs.getEnabledNetworksForCurrentUser()));
+ new HashSet<WifiConfiguration>(getEnabledNetworksForCurrentUser()));
assertEquals(new HashSet<>(configs),
new HashSet<WifiConfiguration>(mConfigs.valuesForAllUsers()));
}
+ private ScanResult createScanResultForNetwork(WifiConfiguration config) {
+ return WifiConfigurationTestUtil.createScanDetailForNetwork(config, "", 0, 0, 0, 0)
+ .getScanResult();
+ }
+
+ /**
+ * Helper function to create a scan result matching the network and ensuring that
+ * {@link ConfigurationMap#getByScanResultForCurrentUser(ScanResult)} can match that network.
+ */
+ private void verifyScanResultMatchWithNetwork(WifiConfiguration config) {
+ mConfigs.put(config);
+ ScanResult scanResult = createScanResultForNetwork(config);
+ WifiConfiguration retrievedConfig =
+ mConfigs.getByScanResultForCurrentUser(scanResult);
+ assertNotNull(retrievedConfig);
+ assertEquals(config.configKey(), retrievedConfig.configKey());
+ }
+
/**
* Verifies that all getters return the correct network configurations, taking into account the
* current user. Also verifies that handleUserSwitch() returns the list of network
@@ -230,4 +264,67 @@
configs.clear();
verifyGetters(configs);
}
+
+ /**
+ * Verifies that {@link ConfigurationMap#getByScanResultForCurrentUser(ScanResult)} can
+ * positively match the corresponding networks.
+ */
+ @Test
+ public void testScanResultDoesMatchCorrespondingNetworks() {
+ verifyScanResultMatchWithNetwork(WifiConfigurationTestUtil.createOpenNetwork());
+ verifyScanResultMatchWithNetwork(WifiConfigurationTestUtil.createPskNetwork());
+ verifyScanResultMatchWithNetwork(WifiConfigurationTestUtil.createWepNetwork());
+ verifyScanResultMatchWithNetwork(WifiConfigurationTestUtil.createEapNetwork());
+ }
+
+ /**
+ * Verifies that {@link ConfigurationMap#getByScanResultForCurrentUser(ScanResult)} does not
+ * match other networks.
+ */
+ @Test
+ public void testScanResultDoesNotMatchWithOtherNetworks() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+ ScanResult scanResult = createScanResultForNetwork(config);
+ // Change the network security type and the old scan result should not match now.
+ config.allowedKeyManagement.clear();
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ mConfigs.put(config);
+ assertNull(mConfigs.getByScanResultForCurrentUser(scanResult));
+ }
+
+ /**
+ * Verifies that {@link ConfigurationMap#getByScanResultForCurrentUser(ScanResult)} does not
+ * match networks which have been removed.
+ */
+ @Test
+ public void testScanResultDoesNotMatchAfterNetworkRemove() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+ ScanResult scanResult = createScanResultForNetwork(config);
+ config.networkId = 5;
+ mConfigs.put(config);
+ // Create another network in the map.
+ mConfigs.put(WifiConfigurationTestUtil.createPskNetwork());
+ assertNotNull(mConfigs.getByScanResultForCurrentUser(scanResult));
+
+ mConfigs.remove(config.networkId);
+ assertNull(mConfigs.getByScanResultForCurrentUser(scanResult));
+ }
+
+ /**
+ * Verifies that {@link ConfigurationMap#getByScanResultForCurrentUser(ScanResult)} does not
+ * match networks after clear.
+ */
+ @Test
+ public void testScanResultDoesNotMatchAfterClear() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+ ScanResult scanResult = createScanResultForNetwork(config);
+ config.networkId = 5;
+ mConfigs.put(config);
+ // Create another network in the map.
+ mConfigs.put(WifiConfigurationTestUtil.createPskNetwork());
+ assertNotNull(mConfigs.getByScanResultForCurrentUser(scanResult));
+
+ mConfigs.clear();
+ assertNull(mConfigs.getByScanResultForCurrentUser(scanResult));
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/CustomTestRunner.java b/tests/wifitests/src/com/android/server/wifi/CustomTestRunner.java
new file mode 100644
index 0000000..d51f16e
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/CustomTestRunner.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.os.Bundle;
+import android.support.test.runner.AndroidJUnitRunner;
+import android.util.Log;
+
+import static org.mockito.Mockito.mock;
+
+public class CustomTestRunner extends AndroidJUnitRunner {
+ @Override
+ public void onCreate(Bundle arguments) {
+ // Override the default TerribleFailureHandler, as that handler might terminate
+ // the process (if we're on an eng build).
+ Log.setWtfHandler(mock(Log.TerribleFailureHandler.class));
+ super.onCreate(arguments);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java b/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
index 3e203a6..7a25e17 100644
--- a/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
@@ -120,6 +120,9 @@
anyLong())).thenReturn(true);
when(mServiceManagerMock.registerForNotifications(anyString(), anyString(),
any(IServiceNotification.Stub.class))).thenReturn(true);
+ when(mServiceManagerMock.getTransport(
+ eq(IWifi.kInterfaceName), eq(HalDeviceManager.HAL_INSTANCE_NAME)))
+ .thenReturn(IServiceManager.Transport.HWBINDER);
when(mWifiMock.linkToDeath(any(IHwBinder.DeathRecipient.class), anyLong())).thenReturn(
true);
when(mWifiMock.registerEventCallback(any(IWifiEventCallback.class))).thenReturn(mStatusOk);
@@ -164,6 +167,22 @@
}
/**
+ * Test the service manager notification coming in after
+ * {@link HalDeviceManager#initIWifiIfNecessary()} is already invoked as a part of
+ * {@link HalDeviceManager#initialize()}.
+ */
+ @Test
+ public void testServiceRegisterationAfterInitialize() throws Exception {
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, mManagerStatusListenerMock);
+ executeAndValidateInitializationSequence();
+
+ // This should now be ignored since IWifi is already non-null.
+ mServiceNotificationCaptor.getValue().onRegistration(IWifi.kInterfaceName, "", true);
+
+ verifyNoMoreInteractions(mManagerStatusListenerMock, mWifiMock, mServiceManagerMock);
+ }
+
+ /**
* Validate that multiple callback registrations are called and that duplicate ones are
* only called once.
*/
@@ -221,7 +240,7 @@
// verify: service and callback calls
mInOrder.verify(mWifiMock).start();
- mInOrder.verify(mManagerStatusListenerMock, times(3)).onStatusChanged();
+ mInOrder.verify(mManagerStatusListenerMock, times(2)).onStatusChanged();
verifyNoMoreInteractions(mManagerStatusListenerMock);
}
@@ -1058,17 +1077,14 @@
*/
@Test
public void testIsSupportedTrue() throws Exception {
- when(mServiceManagerMock.getTransport(
- eq(IWifi.kInterfaceName), eq(HalDeviceManager.HAL_INSTANCE_NAME)))
- .thenReturn(IServiceManager.Transport.HWBINDER);
mInOrder = inOrder(mServiceManagerMock, mWifiMock);
executeAndValidateInitializationSequence();
assertTrue(mDut.isSupported());
}
/**
- * Validate that isSupported() returns true when IServiceManager finds the vendor HAL daemon in
- * the VINTF.
+ * Validate that isSupported() returns false when IServiceManager does not find the vendor HAL
+ * daemon in the VINTF.
*/
@Test
public void testIsSupportedFalse() throws Exception {
@@ -1076,7 +1092,7 @@
eq(IWifi.kInterfaceName), eq(HalDeviceManager.HAL_INSTANCE_NAME)))
.thenReturn(IServiceManager.Transport.EMPTY);
mInOrder = inOrder(mServiceManagerMock, mWifiMock);
- executeAndValidateInitializationSequence();
+ executeAndValidateInitializationSequence(false);
assertFalse(mDut.isSupported());
}
@@ -1088,6 +1104,10 @@
}
private void executeAndValidateInitializationSequence() throws Exception {
+ executeAndValidateInitializationSequence(true);
+ }
+
+ private void executeAndValidateInitializationSequence(boolean isSupported) throws Exception {
// act:
mDut.initialize();
@@ -1097,13 +1117,20 @@
mInOrder.verify(mServiceManagerMock).registerForNotifications(eq(IWifi.kInterfaceName),
eq(""), mServiceNotificationCaptor.capture());
- // act: get the service started (which happens even when service was already up)
- mServiceNotificationCaptor.getValue().onRegistration(IWifi.kInterfaceName, "", true);
+ // The service should already be up at this point.
+ mInOrder.verify(mServiceManagerMock).getTransport(eq(IWifi.kInterfaceName),
+ eq(HalDeviceManager.HAL_INSTANCE_NAME));
- // verify: wifi initialization sequence
- mInOrder.verify(mWifiMock).linkToDeath(mDeathRecipientCaptor.capture(), anyLong());
- mInOrder.verify(mWifiMock).registerEventCallback(mWifiEventCallbackCaptor.capture());
- collector.checkThat("isReady is true", mDut.isReady(), equalTo(true));
+ // verify: wifi initialization sequence if vendor HAL is supported.
+ if (isSupported) {
+ mInOrder.verify(mWifiMock).linkToDeath(mDeathRecipientCaptor.capture(), anyLong());
+ mInOrder.verify(mWifiMock).registerEventCallback(mWifiEventCallbackCaptor.capture());
+ // verify: onStop called as a part of initialize.
+ mInOrder.verify(mWifiMock).stop();
+ collector.checkThat("isReady is true", mDut.isReady(), equalTo(true));
+ } else {
+ collector.checkThat("isReady is false", mDut.isReady(), equalTo(false));
+ }
}
private void executeAndValidateStartupSequence()throws Exception {
diff --git a/tests/wifitests/src/com/android/server/wifi/ScanResultMatchInfoTest.java b/tests/wifitests/src/com/android/server/wifi/ScanResultMatchInfoTest.java
new file mode 100644
index 0000000..e2905a6
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/ScanResultMatchInfoTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.*;
+
+import android.net.wifi.WifiConfiguration;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.ScanResultMatchInfoTest}.
+ */
+@SmallTest
+public class ScanResultMatchInfoTest {
+ /**
+ * Tests that equivalent ScanResultMatchInfo objects are created for WifiConfigurations and
+ * their associated ScanResult
+ */
+ @Test
+ public void testScanResultMatchesWifiConfiguration() {
+ WifiConfiguration conf =
+ WifiConfigurationTestUtil.createPskNetwork("\"PrettyFlyForAWifi\"");
+ ScanDetail scan = createScanDetailForNetwork(conf, "AA:AA:AA:AA:AA:AA");
+ assertEquals(ScanResultMatchInfo.fromWifiConfiguration(conf),
+ ScanResultMatchInfo.fromScanResult(scan.getScanResult()));
+
+ conf = WifiConfigurationTestUtil.createOpenNetwork("\"WIFIght the inevitable\"");
+ scan = createScanDetailForNetwork(conf, "BB:BB:BB:BB:BB:BB");
+ assertEquals(ScanResultMatchInfo.fromWifiConfiguration(conf),
+ ScanResultMatchInfo.fromScanResult(scan.getScanResult()));
+ }
+
+ /**
+ * Tests that multiple ScanResults with different BSSIDs will produce equivalent
+ * ScanResultMatchInfo objects to their associated WifiConfiguration
+ */
+ @Test
+ public void testDifferentBssidScanResultsMatch() {
+ WifiConfiguration conf =
+ WifiConfigurationTestUtil.createPskNetwork("\"PrettyFlyForAWifi-5G\"");
+ ScanDetail scan1 = createScanDetailForNetwork(conf, "AA:AA:AA:AA:AA:AA");
+ ScanDetail scan2 = createScanDetailForNetwork(conf, "BB:BB:BB:BB:BB:BB");
+ assertFalse(scan1.getScanResult().BSSID.equals(scan2.getScanResult().BSSID));
+ assertEquals(ScanResultMatchInfo.fromScanResult(scan1.getScanResult()),
+ ScanResultMatchInfo.fromScanResult(scan2.getScanResult()));
+ }
+
+ /**
+ * Tests that ScanResultMatchInfo objects created for different SSIDs or security types are not
+ * equivalent
+ */
+ @Test
+ public void testDifferentNetworkScanResultsDontMatch() {
+ WifiConfiguration psk =
+ WifiConfigurationTestUtil.createPskNetwork("\"Series Of Tubes\"");
+ WifiConfiguration open1 =
+ WifiConfigurationTestUtil.createOpenNetwork("\"Series Of Tubes\"");
+ WifiConfiguration open2 =
+ WifiConfigurationTestUtil.createOpenNetwork("\"Mom, Click Here For Internet\"");
+ ScanDetail scanOpen1 = createScanDetailForNetwork(open1, "AA:AA:AA:AA:AA:AA");
+ ScanDetail scanOpen2 = createScanDetailForNetwork(open2, "BB:BB:BB:BB:BB:BB");
+ ScanDetail scanPsk = createScanDetailForNetwork(psk, "CC:CC:CC:CC:CC:CC");
+ assertTrue(ScanResultMatchInfo.fromScanResult(scanOpen1.getScanResult())
+ != ScanResultMatchInfo.fromScanResult(scanOpen2.getScanResult()));
+ assertTrue(ScanResultMatchInfo.fromScanResult(scanOpen1.getScanResult())
+ != ScanResultMatchInfo.fromScanResult(scanPsk.getScanResult()));
+ }
+
+ /**
+ * Creates a scan detail corresponding to the provided network and given BSSID
+ */
+ private ScanDetail createScanDetailForNetwork(
+ WifiConfiguration configuration, String bssid) {
+ return WifiConfigurationTestUtil.createScanDetailForNetwork(configuration, bssid, -40,
+ 2402, 0, 0);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/ScoredNetworkEvaluatorTest.java b/tests/wifitests/src/com/android/server/wifi/ScoredNetworkEvaluatorTest.java
index 55b00c9..c9c2625 100644
--- a/tests/wifitests/src/com/android/server/wifi/ScoredNetworkEvaluatorTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/ScoredNetworkEvaluatorTest.java
@@ -231,7 +231,7 @@
scanDetails, scores, meteredHints);
// No saved networks.
- when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(any(ScanDetail.class)))
+ when(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(any(ScanDetail.class)))
.thenReturn(null);
ScanResult scanResult = scanDetails.get(1).getScanResult();
@@ -270,7 +270,7 @@
scanDetails, scores, meteredHints);
// No saved networks.
- when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(any(ScanDetail.class)))
+ when(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(any(ScanDetail.class)))
.thenReturn(null);
for (int i = 0; i < 2; i++) {
@@ -308,7 +308,7 @@
scanDetails, scores, meteredHints);
// No saved networks.
- when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(any(ScanDetail.class)))
+ when(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(any(ScanDetail.class)))
.thenReturn(null);
WifiNetworkSelectorTestUtil.setupEphemeralNetwork(
@@ -524,7 +524,7 @@
mScoreCache, scanDetails, null, meteredHints);
// No saved networks.
- when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(any(ScanDetail.class)))
+ when(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(any(ScanDetail.class)))
.thenReturn(null);
for (int i = 0; i < 2; i++) {
diff --git a/tests/wifitests/src/com/android/server/wifi/SelfRecoveryTest.java b/tests/wifitests/src/com/android/server/wifi/SelfRecoveryTest.java
index 0e55b72..c8b93e9 100644
--- a/tests/wifitests/src/com/android/server/wifi/SelfRecoveryTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/SelfRecoveryTest.java
@@ -32,11 +32,12 @@
public class SelfRecoveryTest {
SelfRecovery mSelfRecovery;
@Mock WifiController mWifiController;
+ @Mock Clock mClock;
@Before
public void setUp() throws Exception {
initMocks(this);
- mSelfRecovery = new SelfRecovery(mWifiController);
+ mSelfRecovery = new SelfRecovery(mWifiController, mClock);
}
/**
@@ -49,10 +50,14 @@
verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI));
reset(mWifiController);
+ when(mClock.getElapsedSinceBootMillis())
+ .thenReturn(SelfRecovery.MAX_RESTARTS_TIME_WINDOW_MILLIS + 1);
mSelfRecovery.trigger(SelfRecovery.REASON_HAL_CRASH);
verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI));
reset(mWifiController);
+ when(mClock.getElapsedSinceBootMillis())
+ .thenReturn(2 * (SelfRecovery.MAX_RESTARTS_TIME_WINDOW_MILLIS + 1));
mSelfRecovery.trigger(SelfRecovery.REASON_WIFICOND_CRASH);
verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI));
reset(mWifiController);
@@ -71,4 +76,73 @@
mSelfRecovery.trigger(8);
verify(mWifiController, never()).sendMessage(anyInt());
}
+
+ /**
+ * Verifies that invocations of {@link SelfRecovery#trigger(int)} for REASON_HAL_CRASH &
+ * REASON_WIFICOND_CRASH are limited to {@link SelfRecovery#MAX_RESTARTS_IN_TIME_WINDOW} in a
+ * {@link SelfRecovery#MAX_RESTARTS_TIME_WINDOW_MILLIS} millisecond time window.
+ */
+ @Test
+ public void testTimeWindowLimiting_typicalUse() {
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(0L);
+ // Fill up the SelfRecovery's restart time window buffer, ensure all the restart triggers
+ // aren't ignored
+ for (int i = 0; i < SelfRecovery.MAX_RESTARTS_IN_TIME_WINDOW / 2; i++) {
+ mSelfRecovery.trigger(SelfRecovery.REASON_HAL_CRASH);
+ verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI));
+ reset(mWifiController);
+
+ mSelfRecovery.trigger(SelfRecovery.REASON_WIFICOND_CRASH);
+ verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI));
+ reset(mWifiController);
+ }
+ if ((SelfRecovery.MAX_RESTARTS_IN_TIME_WINDOW % 2) == 1) {
+ mSelfRecovery.trigger(SelfRecovery.REASON_WIFICOND_CRASH);
+ verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI));
+ reset(mWifiController);
+ }
+
+ // Verify that further attempts to trigger restarts for are ignored
+ mSelfRecovery.trigger(SelfRecovery.REASON_HAL_CRASH);
+ verify(mWifiController, never()).sendMessage(eq(WifiController.CMD_RESTART_WIFI));
+ reset(mWifiController);
+
+ mSelfRecovery.trigger(SelfRecovery.REASON_WIFICOND_CRASH);
+ verify(mWifiController, never()).sendMessage(eq(WifiController.CMD_RESTART_WIFI));
+ reset(mWifiController);
+
+ // Verify L.R.Watchdog can still restart things (It has its own complex limiter)
+ mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG);
+ verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI));
+ reset(mWifiController);
+
+ // now TRAVEL FORWARDS IN TIME and ensure that more restarts can occur
+ when(mClock.getElapsedSinceBootMillis())
+ .thenReturn(SelfRecovery.MAX_RESTARTS_TIME_WINDOW_MILLIS + 1);
+ mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG);
+ verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI));
+ reset(mWifiController);
+
+ when(mClock.getElapsedSinceBootMillis())
+ .thenReturn(SelfRecovery.MAX_RESTARTS_TIME_WINDOW_MILLIS + 1);
+ mSelfRecovery.trigger(SelfRecovery.REASON_HAL_CRASH);
+ verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI));
+ reset(mWifiController);
+ }
+
+ /**
+ * Verifies that invocations of {@link SelfRecovery#trigger(int)} for
+ * REASON_LAST_RESORT_WATCHDOG are NOT limited to
+ * {@link SelfRecovery#MAX_RESTARTS_IN_TIME_WINDOW} in a
+ * {@link SelfRecovery#MAX_RESTARTS_TIME_WINDOW_MILLIS} millisecond time window.
+ */
+ @Test
+ public void testTimeWindowLimiting_lastResortWatchdog_noEffect() {
+ for (int i = 0; i < SelfRecovery.MAX_RESTARTS_IN_TIME_WINDOW * 2; i++) {
+ // Verify L.R.Watchdog can still restart things (It has it's own complex limiter)
+ mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG);
+ verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI));
+ reset(mWifiController);
+ }
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
index 900e6a6..892b597 100644
--- a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
@@ -137,6 +137,20 @@
startSoftApAndVerifyEnabled(config);
}
+
+ /**
+ * Verifies startSoftAp will start with the hiddenSSID param set when it is set to true in the
+ * supplied config.
+ */
+ @Test
+ public void startSoftApWithHiddenSsidTrueInConfig() throws Exception {
+ WifiConfiguration config = new WifiConfiguration();
+ config.apBand = WifiConfiguration.AP_BAND_2GHZ;
+ config.SSID = TEST_SSID;
+ config.hiddenSSID = true;
+ startSoftApAndVerifyEnabled(config);
+ }
+
/** Tests softap startup if default config fails to load. **/
@Test
public void startSoftApDefaultConfigFailedToLoad() throws Exception {
@@ -200,6 +214,7 @@
/** Starts soft AP and verifies that it is enabled successfully. */
protected void startSoftApAndVerifyEnabled(WifiConfiguration config) throws Exception {
String expectedSSID;
+ boolean expectedHiddenSsid;
InOrder order = inOrder(mListener, mApInterfaceBinder, mApInterface, mNmService);
when(mWifiNative.isHalStarted()).thenReturn(false);
@@ -210,16 +225,19 @@
if (config == null) {
when(mWifiApConfigStore.getApConfiguration()).thenReturn(mDefaultApConfig);
expectedSSID = mDefaultApConfig.SSID;
+ expectedHiddenSsid = mDefaultApConfig.hiddenSSID;
} else {
expectedSSID = config.SSID;
+ expectedHiddenSsid = config.hiddenSSID;
}
+
mSoftApManager.start();
mLooper.dispatchAll();
order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLING, 0);
order.verify(mApInterfaceBinder).linkToDeath(mDeathListenerCaptor.capture(), eq(0));
order.verify(mNmService).registerObserver(mNetworkObserverCaptor.capture());
order.verify(mApInterface).writeHostapdConfig(
- eq(expectedSSID.getBytes(StandardCharsets.UTF_8)), anyBoolean(),
+ eq(expectedSSID.getBytes(StandardCharsets.UTF_8)), eq(expectedHiddenSsid),
anyInt(), anyInt(), any());
order.verify(mApInterface).startHostapd();
mNetworkObserverCaptor.getValue().interfaceLinkStateChanged(TEST_INTERFACE_NAME, true);
diff --git a/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java b/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java
index 6c7b252..2deef52 100644
--- a/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java
@@ -54,7 +54,6 @@
import android.net.wifi.WifiSsid;
import android.os.IHwBinder;
import android.os.RemoteException;
-import android.text.TextUtils;
import android.util.SparseArray;
import com.android.server.wifi.hotspot2.AnqpEvent;
@@ -489,28 +488,6 @@
.addNetwork(any(ISupplicantStaIface.addNetworkCallback.class));
}
- @Test
- public void connectToNetworkWithSameNetworkButDifferentBssidUpdatesNetworkFromSupplicant()
- throws Exception {
- executeAndValidateInitializationSequence();
- WifiConfiguration config = executeAndValidateConnectSequence(SUPPLICANT_NETWORK_ID, false);
- String testBssid = "11:22:33:44:55:66";
- when(mSupplicantStaNetworkMock.setBssid(eq(testBssid))).thenReturn(true);
-
- // Reset mocks for mISupplicantStaIfaceMock because we finished the first connection.
- reset(mISupplicantStaIfaceMock);
- setupMocksForConnectSequence(true /*haveExistingNetwork*/);
- // Change the BSSID and connect to the same network.
- assertFalse(TextUtils.equals(
- testBssid, config.getNetworkSelectionStatus().getNetworkSelectionBSSID()));
- config.getNetworkSelectionStatus().setNetworkSelectionBSSID(testBssid);
- assertTrue(mDut.connectToNetwork(config));
- verify(mSupplicantStaNetworkMock).setBssid(eq(testBssid));
- verify(mISupplicantStaIfaceMock, never()).removeNetwork(anyInt());
- verify(mISupplicantStaIfaceMock, never())
- .addNetwork(any(ISupplicantStaIface.addNetworkCallback.class));
- }
-
/**
* Tests connection to a specified network failure due to network add.
*/
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
index 2d3b066..02064d8 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
@@ -17,6 +17,7 @@
package com.android.server.wifi;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -36,6 +37,7 @@
import java.io.File;
import java.lang.reflect.Method;
+import java.util.Random;
/**
* Unit tests for {@link com.android.server.wifi.WifiApConfigStore}.
@@ -50,12 +52,15 @@
private static final String TEST_DEFAULT_AP_SSID = "TestAP";
private static final String TEST_CONFIGURED_AP_SSID = "ConfiguredAP";
private static final String TEST_DEFAULT_HOTSPOT_SSID = "TestShare";
+ private static final String TEST_DEFAULT_HOTSPOT_PSK = "TestPassword";
private static final int RAND_SSID_INT_MIN = 1000;
private static final int RAND_SSID_INT_MAX = 9999;
+ private static final String TEST_CHAR_SET_AS_STRING = "abcdefghijklmnopqrstuvwxyz0123456789";
@Mock Context mContext;
@Mock BackupManagerProxy mBackupManagerProxy;
File mApConfigFile;
+ Random mRandom;
@Before
public void setUp() throws Exception {
@@ -73,6 +78,8 @@
resources.setString(R.string.wifi_localhotspot_configure_ssid_default,
TEST_DEFAULT_HOTSPOT_SSID);
when(mContext.getResources()).thenReturn(resources);
+
+ mRandom = new Random();
}
@After
@@ -195,6 +202,17 @@
}
/**
+ * Verify a proper WifiConfiguration is generate by getDefaultApConfiguration().
+ */
+ @Test
+ public void getDefaultApConfigurationIsValid() {
+ WifiApConfigStore store = new WifiApConfigStore(
+ mContext, mBackupManagerProxy, mApConfigFile.getPath());
+ WifiConfiguration config = store.getApConfiguration();
+ assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+ }
+
+ /**
* Verify a proper local only hotspot config is generated when called properly with the valid
* context.
*/
@@ -204,5 +222,147 @@
verifyDefaultApConfig(config, TEST_DEFAULT_HOTSPOT_SSID);
// The LOHS config should also have a specific network id set - check that as well.
assertEquals(WifiConfiguration.LOCAL_ONLY_NETWORK_ID, config.networkId);
+
+ // verify that the config passes the validateApWifiConfiguration check
+ assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+ }
+
+ /**
+ * Helper method to generate random SSIDs.
+ *
+ * Note: this method has limited use as a random SSID generator. The characters used in this
+ * method do no not cover all valid inputs.
+ * @param length number of characters to generate for the name
+ * @return String generated string of random characters
+ */
+ private String generateRandomString(int length) {
+
+ StringBuilder stringBuilder = new StringBuilder(length);
+ int index = -1;
+ while (stringBuilder.length() < length) {
+ index = mRandom.nextInt(TEST_CHAR_SET_AS_STRING.length());
+ stringBuilder.append(TEST_CHAR_SET_AS_STRING.charAt(index));
+ }
+ return stringBuilder.toString();
+ }
+
+ /**
+ * Verify the SSID checks in validateApWifiConfiguration.
+ *
+ * Cases to check and verify they trigger failed verification:
+ * null WifiConfiguration.SSID
+ * empty WifiConfiguration.SSID
+ * invalid WifiConfiguaration.SSID length
+ *
+ * Additionally check a valid SSID with a random (within valid ranges) length.
+ */
+ @Test
+ public void testSsidVerificationInValidateApWifiConfigurationCheck() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = null;
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+ config.SSID = "";
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+ // check a string that is too large
+ config.SSID = generateRandomString(WifiApConfigStore.SSID_MAX_LEN + 1);
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+
+ // now check a valid SSID with a random length
+ config.SSID = generateRandomString(mRandom.nextInt(WifiApConfigStore.SSID_MAX_LEN + 1));
+ assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+ }
+
+ /**
+ * Verify the Open network checks in validateApWifiConfiguration.
+ *
+ * If the configured network is open, it should not have a password set.
+ *
+ * Additionally verify a valid open network passes verification.
+ */
+ @Test
+ public void testOpenNetworkConfigInValidateApWifiConfigurationCheck() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = TEST_DEFAULT_HOTSPOT_SSID;
+
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ config.preSharedKey = TEST_DEFAULT_HOTSPOT_PSK;
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+
+ // open networks should not have a password set
+ config.preSharedKey = null;
+ assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+ config.preSharedKey = "";
+ assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+ }
+
+ /**
+ * Verify the WPA2_PSK network checks in validateApWifiConfiguration.
+ *
+ * If the configured network is configured with a preSharedKey, verify that the passwork is set
+ * and it meets length requirements.
+ */
+ @Test
+ public void testWpa2PskNetworkConfigInValidateApWifiConfigurationCheck() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = TEST_DEFAULT_HOTSPOT_SSID;
+
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA2_PSK);
+ config.preSharedKey = null;
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+ config.preSharedKey = "";
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+
+ // test too short
+ config.preSharedKey =
+ generateRandomString(WifiApConfigStore.PSK_MIN_LEN - 1);
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+
+ // test too long
+ config.preSharedKey =
+ generateRandomString(WifiApConfigStore.PSK_MAX_LEN + 1);
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+
+ // explicitly test min length
+ config.preSharedKey =
+ generateRandomString(WifiApConfigStore.PSK_MIN_LEN);
+ assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+
+ // explicitly test max length
+ config.preSharedKey =
+ generateRandomString(WifiApConfigStore.PSK_MAX_LEN);
+ assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+
+ // test random (valid length)
+ int maxLen = WifiApConfigStore.PSK_MAX_LEN;
+ int minLen = WifiApConfigStore.PSK_MIN_LEN;
+ config.preSharedKey =
+ generateRandomString(mRandom.nextInt(maxLen - minLen) + minLen);
+ assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+ }
+
+ /**
+ * Verify an invalid AuthType setting (that would trigger an IllegalStateException)
+ * returns false when triggered in the validateApWifiConfiguration.
+ */
+ @Test
+ public void testInvalidAuthTypeInValidateApWifiConfigurationCheck() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = TEST_DEFAULT_HOTSPOT_SSID;
+
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA2_PSK);
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+ }
+
+ /**
+ * Verify an unsupported authType returns false for validateApWifiConfigurationCheck.
+ */
+ @Test
+ public void testUnsupportedAuthTypeInValidateApWifiConfigurationCheck() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = TEST_DEFAULT_HOTSPOT_SSID;
+
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
}
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
index 9fa67a0..7509c18 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
@@ -34,7 +34,6 @@
import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiScanner;
-import android.net.wifi.WifiSsid;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
@@ -177,7 +176,7 @@
when(mDevicePolicyManagerInternal.isActiveAdminWithPolicy(anyInt(), anyInt()))
.thenReturn(false);
- when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
+ when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true);
when(mWifiPermissionsWrapper.getDevicePolicyManagerInternal())
.thenReturn(mDevicePolicyManagerInternal);
createWifiConfigManager();
@@ -326,7 +325,7 @@
// Now change BSSID of the network.
assertAndSetNetworkBSSID(openNetwork, TEST_BSSID);
- when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(false);
+ when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false);
// Update the same configuration and ensure that the operation failed.
NetworkUpdateResult result = updateNetworkToWifiConfigManager(openNetwork);
@@ -782,7 +781,7 @@
assertTrue(retrievedStatus.isNetworkEnabled());
verifyUpdateNetworkStatus(retrievedNetwork, WifiConfiguration.Status.ENABLED);
- when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(false);
+ when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false);
// Now try to set it disabled with |TEST_UPDATE_UID|, it should fail and the network
// should remain enabled.
@@ -811,7 +810,7 @@
mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
assertEquals(TEST_CREATOR_UID, retrievedNetwork.lastConnectUid);
- when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(false);
+ when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false);
// Now try to update the last connect UID with |TEST_UPDATE_UID|, it should fail and
// the lastConnectUid should remain the same.
@@ -927,6 +926,7 @@
wepKeys[0] = "";
wepTxKeyIdx = -1;
assertAndSetNetworkWepKeysAndTxIndex(network, wepKeys, wepTxKeyIdx);
+ network.allowedKeyManagement.clear();
network.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
assertAndSetNetworkPreSharedKey(network, WifiConfigurationTestUtil.TEST_PSK);
@@ -982,7 +982,6 @@
network.allowedKeyManagement.clear();
network.allowedPairwiseCiphers.clear();
network.allowedGroupCiphers.clear();
- network.setIpConfiguration(null);
network.enterpriseConfig = null;
// Update the network.
@@ -1008,6 +1007,20 @@
}
/**
+ * Verifies the addition of a single network using
+ * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)} by passing in null
+ * in IpConfiguraion fails.
+ */
+ @Test
+ public void testAddSingleNetworkWithNullIpConfigurationFails() {
+ WifiConfiguration network = WifiConfigurationTestUtil.createEapNetwork();
+ network.setIpConfiguration(null);
+ NetworkUpdateResult result =
+ mWifiConfigManager.addOrUpdateNetwork(network, TEST_CREATOR_UID);
+ assertFalse(result.isSuccess());
+ }
+
+ /**
* Verifies that the modification of a single network using
* {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)} does not modify
* existing configuration if there is a failure.
@@ -1043,7 +1056,7 @@
/**
* Verifies the matching of networks with different encryption types with the
* corresponding scan detail using
- * {@link WifiConfigManager#getSavedNetworkForScanDetailAndCache(ScanDetail)}.
+ * {@link WifiConfigManager#getConfiguredNetworkForScanDetailAndCache(ScanDetail)}.
* The test also verifies that the provided scan detail was cached,
*/
@Test
@@ -1062,7 +1075,7 @@
/**
* Verifies that scan details with wrong SSID/authentication types are not matched using
- * {@link WifiConfigManager#getSavedNetworkForScanDetailAndCache(ScanDetail)}
+ * {@link WifiConfigManager#getConfiguredNetworkForScanDetailAndCache(ScanDetail)}
* to the added networks.
*/
@Test
@@ -1096,10 +1109,14 @@
openNetworkScanDetail.getScanResult().capabilities;
// Try to lookup a saved network using the modified scan details. All of these should fail.
- assertNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(openNetworkScanDetail));
- assertNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(wepNetworkScanDetail));
- assertNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(pskNetworkScanDetail));
- assertNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(eapNetworkScanDetail));
+ assertNull(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ openNetworkScanDetail));
+ assertNull(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ wepNetworkScanDetail));
+ assertNull(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ pskNetworkScanDetail));
+ assertNull(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ eapNetworkScanDetail));
// All the cache's should be empty as well.
assertNull(mWifiConfigManager.getScanDetailCacheForNetwork(openNetwork.networkId));
@@ -1154,7 +1171,7 @@
createScanDetailForNetwork(
openNetwork, String.format("%s%02x", testBssidPrefix, scanDetailNum));
assertNotNull(
- mWifiConfigManager.getSavedNetworkForScanDetailAndCache(scanDetail));
+ mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail));
// The size of scan detail cache should keep growing until it hits
// |SCAN_CACHE_ENTRIES_MAX_SIZE|.
@@ -1167,7 +1184,7 @@
ScanDetail scanDetail =
createScanDetailForNetwork(
openNetwork, String.format("%s%02x", testBssidPrefix, scanDetailNum));
- assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(scanDetail));
+ assertNotNull(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail));
// Retrieve the scan detail cache and ensure that the size was trimmed down to
// |SCAN_CACHE_ENTRIES_TRIM_SIZE + 1|. The "+1" is to account for the new entry that
@@ -1216,8 +1233,9 @@
verifyUpdateNetworkAfterConnectHasEverConnectedTrue(pskNetwork.networkId);
// Now update the same network with a different psk.
- assertFalse(pskNetwork.preSharedKey.equals("newpassword"));
- pskNetwork.preSharedKey = "newpassword";
+ String newPsk = "\"newpassword\"";
+ assertFalse(pskNetwork.preSharedKey.equals(newPsk));
+ pskNetwork.preSharedKey = newPsk;
verifyUpdateNetworkWithCredentialChangeHasEverConnectedFalse(pskNetwork);
}
@@ -1262,6 +1280,7 @@
verifyUpdateNetworkAfterConnectHasEverConnectedTrue(pskNetwork.networkId);
assertFalse(pskNetwork.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X));
+ pskNetwork.allowedKeyManagement.clear();
pskNetwork.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
verifyUpdateNetworkWithCredentialChangeHasEverConnectedFalse(pskNetwork);
}
@@ -1473,6 +1492,35 @@
}
/**
+ * Verifies that the list of PNO networks does not contain ephemeral or passpoint networks
+ * {@link WifiConfigManager#retrievePnoNetworkList()}.
+ */
+ @Test
+ public void testRetrievePnoListDoesNotContainEphemeralOrPasspointNetworks() throws Exception {
+ WifiConfiguration savedOpenNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ WifiConfiguration ephemeralNetwork = WifiConfigurationTestUtil.createEphemeralNetwork();
+ WifiConfiguration passpointNetwork = WifiConfigurationTestUtil.createPasspointNetwork();
+
+ verifyAddNetworkToWifiConfigManager(savedOpenNetwork);
+ verifyAddEphemeralNetworkToWifiConfigManager(ephemeralNetwork);
+ verifyAddPasspointNetworkToWifiConfigManager(passpointNetwork);
+
+ // Enable all of them.
+ assertTrue(mWifiConfigManager.enableNetwork(
+ savedOpenNetwork.networkId, false, TEST_CREATOR_UID));
+ assertTrue(mWifiConfigManager.enableNetwork(
+ ephemeralNetwork.networkId, false, TEST_CREATOR_UID));
+ assertTrue(mWifiConfigManager.enableNetwork(
+ passpointNetwork.networkId, false, TEST_CREATOR_UID));
+
+ // Retrieve the Pno network list & verify the order of the networks returned.
+ List<WifiScanner.PnoSettings.PnoNetwork> pnoNetworks =
+ mWifiConfigManager.retrievePnoNetworkList();
+ assertEquals(1, pnoNetworks.size());
+ assertEquals(savedOpenNetwork.SSID, pnoNetworks.get(0).ssid);
+ }
+
+ /**
* Verifies the linking of networks when they have the same default GW Mac address in
* {@link WifiConfigManager#getOrCreateScanDetailCacheForNetwork(WifiConfiguration)}.
*/
@@ -1500,9 +1548,12 @@
// Now save all these scan details corresponding to each of this network and expect
// all of these networks to be linked with each other.
- assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail1));
- assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail2));
- assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail3));
+ assertNotNull(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail1));
+ assertNotNull(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail2));
+ assertNotNull(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail3));
List<WifiConfiguration> retrievedNetworks =
mWifiConfigManager.getConfiguredNetworks();
@@ -1538,9 +1589,12 @@
// Now save all these scan details corresponding to each of this network and expect
// all of these networks to be linked with each other.
- assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail1));
- assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail2));
- assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail3));
+ assertNotNull(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail1));
+ assertNotNull(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail2));
+ assertNotNull(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail3));
List<WifiConfiguration> retrievedNetworks =
mWifiConfigManager.getConfiguredNetworks();
@@ -1571,8 +1625,10 @@
ScanDetail networkScanDetail1 = createScanDetailForNetwork(network1, "af:89:56:34:56:67");
ScanDetail networkScanDetail2 = createScanDetailForNetwork(network2, "af:89:56:34:56:68");
- assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail1));
- assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail2));
+ assertNotNull(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail1));
+ assertNotNull(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail2));
List<WifiConfiguration> retrievedNetworks =
mWifiConfigManager.getConfiguredNetworks();
@@ -1603,7 +1659,8 @@
createScanDetailForNetwork(
network1, test_bssid_base + Integer.toString(scan_result_num));
assertNotNull(
- mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail));
+ mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail));
}
// Now add 1 scan result to the other network with bssid which is different in only the
@@ -1611,7 +1668,8 @@
ScanDetail networkScanDetail2 =
createScanDetailForNetwork(
network2, test_bssid_base + Integer.toString(scan_result_num++));
- assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail2));
+ assertNotNull(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail2));
List<WifiConfiguration> retrievedNetworks =
mWifiConfigManager.getConfiguredNetworks();
@@ -1638,8 +1696,10 @@
// Now save all these scan details corresponding to each of this network and expect
// all of these networks to be linked with each other.
- assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail1));
- assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail2));
+ assertNotNull(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail1));
+ assertNotNull(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail2));
List<WifiConfiguration> retrievedNetworks =
mWifiConfigManager.getConfiguredNetworks();
@@ -1660,9 +1720,9 @@
network2.networkId, "ad:de:fe:45:23:34"));
// Add some dummy scan results again to re-evaluate the linking of networks.
- assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(
+ assertNotNull(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
createScanDetailForNetwork(network1, "af:89:56:34:45:67")));
- assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(
+ assertNotNull(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
createScanDetailForNetwork(network1, "af:89:56:34:45:68")));
retrievedNetworks = mWifiConfigManager.getConfiguredNetworks();
@@ -1687,7 +1747,8 @@
createScanDetailForNetwork(
network, test_bssid_base + Integer.toString(i), 0, TEST_FREQ_LIST[i]);
assertNotNull(
- mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail));
+ mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail));
}
assertEquals(new HashSet<Integer>(Arrays.asList(TEST_FREQ_LIST)),
@@ -1713,7 +1774,8 @@
createScanDetailForNetwork(
network, test_bssid_base + Integer.toString(i), 0, TEST_FREQ_LIST[i]);
assertNotNull(
- mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail));
+ mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail));
}
@@ -1745,7 +1807,8 @@
createScanDetailForNetwork(
network, test_bssid_base + Integer.toString(i), 0, TEST_FREQ_LIST[i]);
assertNotNull(
- mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail));
+ mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail));
}
int ageInMillis = 4;
@@ -1785,7 +1848,8 @@
createScanDetailForNetwork(
network, test_bssid_base + Integer.toString(i), 0, TEST_FREQ_LIST[i]);
assertNotNull(
- mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail));
+ mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail));
}
// Ensure that the fetched list size is limited.
@@ -1815,7 +1879,8 @@
network1, test_bssid_base + Integer.toString(TEST_FREQ_LISTIdx), 0,
TEST_FREQ_LIST[TEST_FREQ_LISTIdx]);
assertNotNull(
- mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail));
+ mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail));
}
// Create 3 scan results with different bssid's & frequencies for network 2.
@@ -1825,7 +1890,8 @@
network2, test_bssid_base + Integer.toString(TEST_FREQ_LISTIdx), 0,
TEST_FREQ_LIST[TEST_FREQ_LISTIdx]);
assertNotNull(
- mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail));
+ mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail));
}
// Link the 2 configurations together using the GwMacAddress.
@@ -1873,7 +1939,8 @@
network1, test_bssid_base + Integer.toString(TEST_FREQ_LISTIdx), 0,
TEST_FREQ_LIST[TEST_FREQ_LISTIdx]);
assertNotNull(
- mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail));
+ mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail));
}
// Create 3 scan results with different bssid's & frequencies for network 2.
@@ -1883,7 +1950,8 @@
network2, test_bssid_base + Integer.toString(TEST_FREQ_LISTIdx), 0,
TEST_FREQ_LIST[TEST_FREQ_LISTIdx]);
assertNotNull(
- mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail));
+ mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
+ networkScanDetail));
}
// Link the 2 configurations together using the GwMacAddress.
@@ -2676,6 +2744,40 @@
}
/**
+ * Verifies that all the ephemeral and passpoint networks are removed when
+ * {@link WifiConfigManager#removeAllEphemeralOrPasspointConfiguredNetworks()} is invoked.
+ */
+ @Test
+ public void testRemoveAllEphemeralOrPasspointConfiguredNetworks() throws Exception {
+ WifiConfiguration savedOpenNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ WifiConfiguration ephemeralNetwork = WifiConfigurationTestUtil.createEphemeralNetwork();
+ WifiConfiguration passpointNetwork = WifiConfigurationTestUtil.createPasspointNetwork();
+
+ verifyAddNetworkToWifiConfigManager(savedOpenNetwork);
+ verifyAddEphemeralNetworkToWifiConfigManager(ephemeralNetwork);
+ verifyAddPasspointNetworkToWifiConfigManager(passpointNetwork);
+
+ List<WifiConfiguration> expectedConfigsBeforeRemove = new ArrayList<WifiConfiguration>() {{
+ add(savedOpenNetwork);
+ add(ephemeralNetwork);
+ add(passpointNetwork);
+ }};
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ expectedConfigsBeforeRemove, mWifiConfigManager.getConfiguredNetworks());
+
+ assertTrue(mWifiConfigManager.removeAllEphemeralOrPasspointConfiguredNetworks());
+
+ List<WifiConfiguration> expectedConfigsAfterRemove = new ArrayList<WifiConfiguration>() {{
+ add(savedOpenNetwork);
+ }};
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ expectedConfigsAfterRemove, mWifiConfigManager.getConfiguredNetworks());
+
+ // No more ephemeral or passpoint networks to remove now.
+ assertFalse(mWifiConfigManager.removeAllEphemeralOrPasspointConfiguredNetworks());
+ }
+
+ /**
* Verifies that the modification of a single network using
* {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)} and ensures that any
* updates to the network config in
@@ -2799,7 +2901,7 @@
*/
@Test
public void testAddMultipleNetworksWithSameSSIDAndDefaultKeyMgmt() {
- final String ssid = "test_blah";
+ final String ssid = "\"test_blah\"";
// Add a network with the above SSID and default key mgmt and ensure it was added
// successfully.
WifiConfiguration network1 = new WifiConfiguration();
@@ -2835,12 +2937,13 @@
*/
@Test
public void testAddMultipleNetworksWithSameSSIDAndDifferentKeyMgmt() {
- final String ssid = "test_blah";
+ final String ssid = "\"test_blah\"";
// Add a network with the above SSID and WPA_PSK key mgmt and ensure it was added
// successfully.
WifiConfiguration network1 = new WifiConfiguration();
network1.SSID = ssid;
network1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ network1.preSharedKey = "\"test_blah\"";
NetworkUpdateResult result = addNetworkToWifiConfigManager(network1);
assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
assertTrue(result.isNewNetwork());
@@ -2874,14 +2977,14 @@
@Test
public void testAddNetworkWithProxyFails() {
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
false, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy(),
false, // assertSuccess
WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
false, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
WifiConfigurationTestUtil.createDHCPIpConfigurationWithStaticProxy(),
@@ -2896,14 +2999,14 @@
@Test
public void testAddNetworkWithProxyWithConfOverride() {
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- true, // withConfOverride
+ true, // withNetworkSettings
false, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy(),
true, // assertSuccess
WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- true, // withConfOverride
+ true, // withNetworkSettings
false, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
WifiConfigurationTestUtil.createDHCPIpConfigurationWithStaticProxy(),
@@ -2918,14 +3021,14 @@
@Test
public void testAddNetworkWithProxyAsProfileOwner() {
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
true, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy(),
true, // assertSuccess
WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
true, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
WifiConfigurationTestUtil.createDHCPIpConfigurationWithStaticProxy(),
@@ -2939,14 +3042,14 @@
@Test
public void testAddNetworkWithProxyAsDeviceOwner() {
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
false, // withProfileOwnerPolicy
true, // withDeviceOwnerPolicy
WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy(),
true, // assertSuccess
WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
false, // withProfileOwnerPolicy
true, // withDeviceOwnerPolicy
WifiConfigurationTestUtil.createDHCPIpConfigurationWithStaticProxy(),
@@ -2962,14 +3065,14 @@
WifiConfiguration network = WifiConfigurationTestUtil.createOpenHiddenNetwork();
NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(network);
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
false, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy(),
false, // assertSuccess
result.getNetworkId()); // Update networkID
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
false, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
WifiConfigurationTestUtil.createDHCPIpConfigurationWithStaticProxy(),
@@ -2989,7 +3092,7 @@
NetworkUpdateResult result = addNetworkToWifiConfigManager(network, TEST_CREATOR_UID);
assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- true, // withConfOverride
+ true, // withNetworkSettings
false, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy(),
@@ -3001,7 +3104,7 @@
result = addNetworkToWifiConfigManager(network, TEST_NO_PERM_UID);
assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
true, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy(),
@@ -3013,7 +3116,7 @@
result = addNetworkToWifiConfigManager(network, TEST_NO_PERM_UID);
assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
false, // withProfileOwnerPolicy
true, // withDeviceOwnerPolicy
WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy(),
@@ -3030,7 +3133,7 @@
IpConfiguration ipConf = WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy();
// First create a WifiConfiguration with proxy
NetworkUpdateResult result = verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
true, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
ipConf,
@@ -3038,7 +3141,7 @@
WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
// Update the network while using the same ipConf, and no proxy specific permissions
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
false, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
ipConf,
@@ -3072,14 +3175,14 @@
// Update with Conf Override
NetworkUpdateResult result = verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- true, // withConfOverride
+ true, // withNetworkSettings
false, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
ipConf1,
true, // assertSuccess
WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- true, // withConfOverride
+ true, // withNetworkSettings
false, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
ipConf2,
@@ -3088,14 +3191,14 @@
// Update as Device Owner
result = verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
false, // withProfileOwnerPolicy
true, // withDeviceOwnerPolicy
ipConf1,
true, // assertSuccess
WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
false, // withProfileOwnerPolicy
true, // withDeviceOwnerPolicy
ipConf2,
@@ -3104,14 +3207,14 @@
// Update as Profile Owner
result = verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
true, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
ipConf1,
true, // assertSuccess
WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
true, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
ipConf2,
@@ -3120,14 +3223,14 @@
// Update with no permissions (should fail)
result = verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
true, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
ipConf1,
true, // assertSuccess
WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
false, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
ipConf2,
@@ -3160,14 +3263,14 @@
// Update with Conf Override
NetworkUpdateResult result = verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- true, // withConfOverride
+ true, // withNetworkSettings
false, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
ipConf1,
true, // assertSuccess
WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- true, // withConfOverride
+ true, // withNetworkSettings
false, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
ipConf2,
@@ -3176,14 +3279,14 @@
// Update as Device Owner
result = verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
false, // withProfileOwnerPolicy
true, // withDeviceOwnerPolicy
ipConf1,
true, // assertSuccess
WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
false, // withProfileOwnerPolicy
true, // withDeviceOwnerPolicy
ipConf2,
@@ -3192,14 +3295,14 @@
// Update as Profile Owner
result = verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
true, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
ipConf1,
true, // assertSuccess
WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
true, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
ipConf2,
@@ -3208,14 +3311,14 @@
// Update with no permissions (should fail)
result = verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
true, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
ipConf1,
true, // assertSuccess
WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- false, // withConfOverride
+ false, // withNetworkSettings
false, // withProfileOwnerPolicy
false, // withDeviceOwnerPolicy
ipConf2,
@@ -3242,7 +3345,7 @@
}
private NetworkUpdateResult verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
- boolean withConfOverride,
+ boolean withNetworkSettings,
boolean withProfileOwnerPolicy,
boolean withDeviceOwnerPolicy,
IpConfiguration ipConfiguration,
@@ -3261,9 +3364,9 @@
when(mDevicePolicyManagerInternal.isActiveAdminWithPolicy(anyInt(),
eq(DeviceAdminInfo.USES_POLICY_DEVICE_OWNER)))
.thenReturn(withDeviceOwnerPolicy);
- when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt()))
- .thenReturn(withConfOverride);
- int uid = withConfOverride ? TEST_CREATOR_UID : TEST_NO_PERM_UID;
+ when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt()))
+ .thenReturn(withNetworkSettings);
+ int uid = withNetworkSettings ? TEST_CREATOR_UID : TEST_NO_PERM_UID;
NetworkUpdateResult result = addNetworkToWifiConfigManager(network, uid);
assertEquals(assertSuccess, result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
return result;
@@ -3586,6 +3689,7 @@
*/
private NetworkUpdateResult addNetworkToWifiConfigManager(WifiConfiguration configuration,
int uid) {
+ clearInvocations(mContext, mWifiConfigStore, mNetworkListStoreData);
triggerStoreReadIfNeeded();
when(mClock.getWallClockMillis()).thenReturn(TEST_WALLCLOCK_CREATION_TIME_MILLIS);
NetworkUpdateResult result =
@@ -3657,6 +3761,7 @@
* to modify the configuration before we compare the added network with the retrieved network.
*/
private NetworkUpdateResult updateNetworkToWifiConfigManager(WifiConfiguration configuration) {
+ clearInvocations(mContext, mWifiConfigStore, mNetworkListStoreData);
when(mClock.getWallClockMillis()).thenReturn(TEST_WALLCLOCK_UPDATE_TIME_MILLIS);
NetworkUpdateResult result =
mWifiConfigManager.addOrUpdateNetwork(configuration, TEST_UPDATE_UID);
@@ -3820,25 +3925,9 @@
*/
private ScanDetail createScanDetailForNetwork(
WifiConfiguration configuration, String bssid, int level, int frequency) {
- String caps;
- if (configuration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
- caps = "[WPA2-PSK-CCMP]";
- } else if (configuration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)
- || configuration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) {
- caps = "[WPA2-EAP-CCMP]";
- } else if (configuration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)
- && WifiConfigurationUtil.hasAnyValidWepKey(configuration.wepKeys)) {
- caps = "[WEP]";
- } else {
- caps = "[]";
- }
- WifiSsid ssid = WifiSsid.createFromAsciiEncoded(configuration.getPrintableSsid());
- // Fill in 0's in the fields we don't care about.
- return new ScanDetail(
- ssid, bssid, caps, level, frequency, mClock.getUptimeSinceBootMillis(),
- mClock.getWallClockMillis());
+ return WifiConfigurationTestUtil.createScanDetailForNetwork(configuration, bssid, level,
+ frequency, mClock.getUptimeSinceBootMillis(), mClock.getWallClockMillis());
}
-
/**
* Creates a scan detail corresponding to the provided network and BSSID value.
*/
@@ -3857,7 +3946,7 @@
* Adds the provided network and then creates a scan detail corresponding to the network. The
* method then creates a ScanDetail corresponding to the network and ensures that the network
* is properly matched using
- * {@link WifiConfigManager#getSavedNetworkForScanDetailAndCache(ScanDetail)} and also
+ * {@link WifiConfigManager#getConfiguredNetworkForScanDetailAndCache(ScanDetail)} and also
* verifies that the provided scan detail was cached,
*/
private void verifyAddSingleNetworkAndMatchScanDetailToNetworkAndCache(
@@ -3870,7 +3959,7 @@
ScanResult scanResult = scanDetail.getScanResult();
WifiConfiguration retrievedNetwork =
- mWifiConfigManager.getSavedNetworkForScanDetailAndCache(scanDetail);
+ mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail);
// Retrieve the network with password data for comparison.
retrievedNetwork =
mWifiConfigManager.getConfiguredNetworkWithPassword(retrievedNetwork.networkId);
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java
index f7bf5b0..16c9f30 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java
@@ -26,6 +26,7 @@
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.WifiSsid;
import android.text.TextUtils;
import java.net.InetAddress;
@@ -224,10 +225,20 @@
* use a static index to avoid duplicate configurations.
*/
public static WifiConfiguration createOpenNetwork() {
- return generateWifiConfig(TEST_NETWORK_ID, TEST_UID, createNewSSID(), true, true, null,
+ return createOpenNetwork(createNewSSID());
+ }
+
+ public static WifiConfiguration createOpenNetwork(String ssid) {
+ return generateWifiConfig(TEST_NETWORK_ID, TEST_UID, ssid, true, true, null,
null, SECURITY_NONE);
}
+ public static WifiConfiguration createEphemeralNetwork() {
+ WifiConfiguration configuration = createOpenNetwork();
+ configuration.ephemeral = true;
+ return configuration;
+ }
+
public static WifiConfiguration createOpenHiddenNetwork() {
WifiConfiguration configuration = createOpenNetwork();
configuration.hiddenSSID = true;
@@ -415,6 +426,29 @@
}
/**
+ * Creates a scan detail corresponding to the provided network and given BSSID, level &frequency
+ * values.
+ */
+ public static ScanDetail createScanDetailForNetwork(
+ WifiConfiguration configuration, String bssid, int level, int frequency,
+ long tsf, long seen) {
+ String caps;
+ if (configuration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
+ caps = "[WPA2-PSK-CCMP]";
+ } else if (configuration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)
+ || configuration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) {
+ caps = "[WPA2-EAP-CCMP]";
+ } else if (configuration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)
+ && WifiConfigurationUtil.hasAnyValidWepKey(configuration.wepKeys)) {
+ caps = "[WEP]";
+ } else {
+ caps = "[]";
+ }
+ WifiSsid ssid = WifiSsid.createFromAsciiEncoded(configuration.getPrintableSsid());
+ return new ScanDetail(ssid, bssid, caps, level, frequency, tsf, seen);
+ }
+
+ /**
* Asserts that the 2 WifiConfigurations are equal in the elements saved for both backup/restore
* and config store.
*/
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java
index 2b3de3f..2925273 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.*;
import android.content.pm.UserInfo;
+import android.net.IpConfiguration;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiScanner;
@@ -43,8 +44,6 @@
static final int OTHER_USER_ID = 11;
static final String TEST_SSID = "test_ssid";
static final String TEST_SSID_1 = "test_ssid_1";
- static final String TEST_BSSID = "aa:aa:11:22:cc:dd";
- static final String TEST_BSSID_1 = "11:22:11:22:cc:dd";
static final List<UserInfo> PROFILES = Arrays.asList(
new UserInfo(CURRENT_USER_ID, "owner", 0),
new UserInfo(CURRENT_USER_MANAGED_PROFILE_USER_ID, "managed profile", 0));
@@ -191,6 +190,227 @@
}
/**
+ * Verify that the validate method successfully validates good WifiConfigurations with ASCII
+ * values.
+ */
+ @Test
+ public void testValidatePositiveCases_Ascii() {
+ assertTrue(WifiConfigurationUtil.validate(
+ WifiConfigurationTestUtil.createOpenNetwork(),
+ WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ assertTrue(WifiConfigurationUtil.validate(
+ WifiConfigurationTestUtil.createPskNetwork(),
+ WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ assertTrue(WifiConfigurationUtil.validate(
+ WifiConfigurationTestUtil.createWepNetwork(),
+ WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ assertTrue(WifiConfigurationUtil.validate(
+ WifiConfigurationTestUtil.createEapNetwork(),
+ WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
+
+ /**
+ * Verify that the validate method successfully validates good WifiConfigurations with hex
+ * values.
+ */
+ @Test
+ public void testValidatePositiveCases_Hex() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork();
+ config.SSID = "abcd1234555a";
+ config.preSharedKey = "abcd123455151234556788990034556667332345667322344556676743233445";
+ assertTrue(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
+
+ /**
+ * Verify that the validate method validates WifiConfiguration with masked psk string only for
+ * an update.
+ */
+ @Test
+ public void testValidatePositiveCases_MaskedPskString() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork();
+ assertTrue(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+
+ config.preSharedKey = WifiConfigurationUtil.PASSWORD_MASK;
+ assertFalse(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ assertTrue(WifiConfigurationUtil.validate(
+ config, WifiConfigurationUtil.VALIDATE_FOR_UPDATE));
+ }
+
+ /**
+ * Verify that the validate method validates WifiConfiguration with null ssid only for an
+ * update.
+ */
+ @Test
+ public void testValidatePositiveCases_OnlyUpdateIgnoresNullSsid() {
+ WifiConfiguration config = new WifiConfiguration();
+ assertFalse(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ assertTrue(WifiConfigurationUtil.validate(
+ config, WifiConfigurationUtil.VALIDATE_FOR_UPDATE));
+ }
+
+ /**
+ * Verify that the validate method fails to validate WifiConfiguration with bad ssid length.
+ */
+ @Test
+ public void testValidateNegativeCases_BadAsciiSsidLength() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+ assertTrue(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+
+ config.SSID = "\"abcdfefeeretretyetretetetetetrertertrsreqwrwe\"";
+ assertFalse(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ config.SSID = "\"\"";
+ assertFalse(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
+
+ /**
+ * Verify that the validate method fails to validate WifiConfiguration with malformed ssid
+ * string.
+ */
+ @Test
+ public void testValidateNegativeCases_MalformedAsciiSsidString() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+ assertTrue(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+
+ config.SSID = "\"ab";
+ assertFalse(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
+
+ /**
+ * Verify that the validate method fails to validate WifiConfiguration with bad ssid length.
+ */
+ @Test
+ public void testValidateNegativeCases_BadHexSsidLength() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+ assertTrue(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+
+ config.SSID = "abcdfe012345632423343543453456464545656464545646454ace34534545634535";
+ assertFalse(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ config.SSID = "";
+ assertFalse(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
+
+ /**
+ * Verify that the validate method fails to validate WifiConfiguration with malformed ssid
+ * string.
+ */
+ @Test
+ public void testValidateNegativeCases_MalformedHexSsidString() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+ assertTrue(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+
+ config.SSID = "hello";
+ assertFalse(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
+
+ /**
+ * Verify that the validate method fails to validate WifiConfiguration with bad psk length.
+ */
+ @Test
+ public void testValidateNegativeCases_BadAsciiPskLength() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork();
+ assertTrue(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+
+ config.preSharedKey = "\"abcdffeeretretyetreteteteabe34tetrertertrsraaaaaaaaaaa345eqwrweewq"
+ + "weqe\"";
+ assertFalse(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ config.preSharedKey = "\"454\"";
+ assertFalse(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
+
+ /**
+ * Verify that the validate method fails to validate WifiConfiguration with malformed psk
+ * string.
+ */
+ @Test
+ public void testValidateNegativeCases_MalformedAsciiPskString() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork();
+ assertTrue(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+
+ config.preSharedKey = "\"abcdfefeeretrety";
+ assertFalse(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
+
+ /**
+ * Verify that the validate method fails to validate WifiConfiguration with bad psk length.
+ */
+ @Test
+ public void testValidateNegativeCases_BadHexPskLength() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork();
+ assertTrue(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+
+ config.preSharedKey = "abcd123456788990013453445345465465476546";
+ assertFalse(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ config.preSharedKey = "";
+ assertFalse(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
+
+ /**
+ * Verify that the validate method fails to validate WifiConfiguration with malformed psk
+ * string.
+ */
+ @Test
+ public void testValidateNegativeCases_MalformedHexPskString() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork();
+ assertTrue(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+
+ config.preSharedKey = "adbdfgretrtyrtyrty";
+ assertFalse(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
+
+ /**
+ * Verify that the validate method fails to validate WifiConfiguration with bad key mgmt values.
+ */
+ @Test
+ public void testValidateNegativeCases_BadKeyMgmtPskEap() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork();
+ assertTrue(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
+ assertFalse(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
+
+ /**
+ * Verify that the validate method fails to validate WifiConfiguration with bad key mgmt values.
+ */
+ @Test
+ public void testValidateNegativeCases_BadKeyMgmtOpenPsk() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+ assertTrue(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ assertFalse(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
+
+ /**
+ * Verify that the validate method fails to validate WifiConfiguration with bad key mgmt values.
+ */
+ @Test
+ public void testValidateNegativeCases_BadKeyMgmt() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork();
+ assertTrue(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
+ assertFalse(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
+
+ /**
+ * Verify that the validate method fails to validate WifiConfiguration with bad ipconfiguration
+ * values.
+ */
+ @Test
+ public void testValidateNegativeCases_BadIpconfiguration() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork();
+ IpConfiguration ipConfig =
+ WifiConfigurationTestUtil.createStaticIpConfigurationWithPacProxy();
+ config.setIpConfiguration(ipConfig);
+ assertTrue(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+
+ ipConfig.setStaticIpConfiguration(null);
+ config.setIpConfiguration(ipConfig);
+ assertFalse(WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD));
+ }
+
+ /**
* Verify the instance of {@link android.net.wifi.WifiScanner.PnoSettings.PnoNetwork} created
* for an open network using {@link WifiConfigurationUtil#createPnoNetwork(
* WifiConfiguration, int)}.
@@ -254,19 +474,6 @@
}
/**
- * Verify that WifiConfigurationUtil.isSameNetwork returns true when two WifiConfiguration
- * objects have the same parameters but different network selection BSSID's.
- */
- @Test
- public void testIsSameNetworkReturnsTrueOnSameNetworkWithDifferentBSSID() {
- WifiConfiguration network = WifiConfigurationTestUtil.createPskNetwork(TEST_SSID);
- network.getNetworkSelectionStatus().setNetworkSelectionBSSID(TEST_BSSID);
- WifiConfiguration network1 = WifiConfigurationTestUtil.createPskNetwork(TEST_SSID);
- network1.getNetworkSelectionStatus().setNetworkSelectionBSSID(TEST_BSSID_1);
- assertTrue(WifiConfigurationUtil.isSameNetwork(network, network1));
- }
-
- /**
* Verify that WifiConfigurationUtil.isSameNetwork returns false when two WifiConfiguration
* objects have the different SSIDs.
*/
@@ -288,6 +495,31 @@
assertFalse(WifiConfigurationUtil.isSameNetwork(network, network1));
}
+ /**
+ * Verify that WifiConfigurationUtil.isSameNetwork returns false when two WifiConfiguration
+ * objects have the different EAP identity.
+ */
+ @Test
+ public void testIsSameNetworkReturnsFalseOnDifferentEapIdentity() {
+ WifiConfiguration network1 = WifiConfigurationTestUtil.createEapNetwork(TEST_SSID);
+ WifiConfiguration network2 = WifiConfigurationTestUtil.createEapNetwork(TEST_SSID);
+ network1.enterpriseConfig.setIdentity("Identity1");
+ network2.enterpriseConfig.setIdentity("Identity2");
+ assertFalse(WifiConfigurationUtil.isSameNetwork(network1, network2));
+ }
+
+ /**
+ * Verify that WifiConfigurationUtil.isSameNetwork returns false when two WifiConfiguration
+ * objects have the different EAP anonymous identity.
+ */
+ @Test
+ public void testIsSameNetworkReturnsFalseOnDifferentEapAnonymousIdentity() {
+ WifiConfiguration network1 = WifiConfigurationTestUtil.createEapNetwork(TEST_SSID);
+ WifiConfiguration network2 = WifiConfigurationTestUtil.createEapNetwork(TEST_SSID);
+ network1.enterpriseConfig.setAnonymousIdentity("Identity1");
+ network2.enterpriseConfig.setAnonymousIdentity("Identity2");
+ assertFalse(WifiConfigurationUtil.isSameNetwork(network1, network2));
+ }
/**
* Verify the instance of {@link android.net.wifi.WifiScanner.PnoSettings.PnoNetwork} created
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
index 68c2ca3..bdb6b14 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
@@ -25,6 +25,7 @@
import android.app.test.MockAnswerUtil.AnswerWithArguments;
import android.app.test.TestAlarmManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.NetworkScoreManager;
import android.net.wifi.ScanResult;
@@ -50,7 +51,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
@@ -63,6 +63,7 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
/**
* Unit tests for {@link com.android.server.wifi.WifiConnectivityManager}.
@@ -90,6 +91,10 @@
verify(mWifiConfigManager).setOnSavedNetworkUpdateListener(anyObject());
mWifiConnectivityManager.setWifiEnabled(true);
when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime());
+ mFullScanMaxTxPacketRate = mResource.getInteger(
+ R.integer.config_wifi_framework_max_tx_rate_for_full_scan);
+ mFullScanMaxRxPacketRate = mResource.getInteger(
+ R.integer.config_wifi_framework_max_rx_rate_for_full_scan);
}
/**
@@ -118,12 +123,15 @@
@Mock private NetworkScoreManager mNetworkScoreManager;
@Mock private Clock mClock;
@Mock private WifiLastResortWatchdog mWifiLastResortWatchdog;
+ @Mock private WifiNotificationController mWifiNotificationController;
@Mock private WifiMetrics mWifiMetrics;
@Mock private WifiNetworkScoreCache mScoreCache;
@Captor ArgumentCaptor<ScanResult> mCandidateScanResultCaptor;
@Captor ArgumentCaptor<ArrayList<String>> mBssidBlacklistCaptor;
@Captor ArgumentCaptor<ArrayList<String>> mSsidWhitelistCaptor;
private MockResources mResources;
+ private int mFullScanMaxTxPacketRate;
+ private int mFullScanMaxRxPacketRate;
private static final int CANDIDATE_NETWORK_ID = 0;
private static final String CANDIDATE_SSID = "\"AnSsid\"";
@@ -145,6 +153,10 @@
.thenReturn(-60);
when(resource.getInteger(
R.integer.config_wifi_framework_current_network_boost)).thenReturn(16);
+ when(resource.getInteger(
+ R.integer.config_wifi_framework_max_tx_rate_for_full_scan)).thenReturn(8);
+ when(resource.getInteger(
+ R.integer.config_wifi_framework_max_rx_rate_for_full_scan)).thenReturn(16);
return resource;
}
@@ -154,6 +166,7 @@
when(context.getResources()).thenReturn(mResource);
when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn(
mAlarmManager.getAlarmManager());
+ when(context.getPackageManager()).thenReturn(mock(PackageManager.class));
return context;
}
@@ -281,8 +294,8 @@
WifiConnectivityManager createConnectivityManager() {
return new WifiConnectivityManager(mContext, mWifiStateMachine, mWifiScanner,
mWifiConfigManager, mWifiInfo, mWifiNS, mWifiConnectivityHelper,
- mWifiLastResortWatchdog, mWifiMetrics, mLooper.getLooper(), mClock,
- mLocalLog, true, mFrameworkFacade, null, null, null);
+ mWifiLastResortWatchdog, mWifiNotificationController, mWifiMetrics,
+ mLooper.getLooper(), mClock, mLocalLog, true, mFrameworkFacade, null, null, null);
}
/**
@@ -521,7 +534,6 @@
* because of their low RSSI values.
*/
@Test
- @Ignore("b/32977707")
public void pnoRetryForLowRssiNetwork() {
when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
anyBoolean(), anyBoolean())).thenReturn(null);
@@ -553,7 +565,6 @@
* a candidate while watchdog single scan did.
*/
@Test
- @Ignore("b/32977707")
public void watchdogBitePnoBadIncrementsMetrics() {
// Set screen to off
mWifiConnectivityManager.handleScreenStateChanged(false);
@@ -577,7 +588,6 @@
* a candidate which was the same with watchdog single scan.
*/
@Test
- @Ignore("b/32977707")
public void watchdogBitePnoGoodIncrementsMetrics() {
// Qns returns no candidate after watchdog single scan.
when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
@@ -599,6 +609,90 @@
}
/**
+ * {@link WifiNotificationController} handles scan results on network selection.
+ *
+ * Expected behavior: ONA handles scan results
+ */
+ @Test
+ public void wifiDisconnected_noConnectionCandidate_openNetworkNotificationScanResultsHandled() {
+ // no connection candidate selected
+ when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
+ anyBoolean(), anyBoolean())).thenReturn(null);
+
+ List<ScanDetail> expectedOpenNetworks = new ArrayList<>();
+ expectedOpenNetworks.add(
+ new ScanDetail(
+ new ScanResult(WifiSsid.createFromAsciiEncoded(CANDIDATE_SSID),
+ CANDIDATE_SSID, CANDIDATE_BSSID, 1245, 0, "some caps", -78, 2450,
+ 1025, 22, 33, 20, 0, 0, true), null));
+
+ when(mWifiNS.getFilteredScanDetailsForOpenUnsavedNetworks())
+ .thenReturn(expectedOpenNetworks);
+
+ // Set WiFi to disconnected state to trigger PNO scan
+ mWifiConnectivityManager.handleConnectionStateChanged(
+ WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
+
+ verify(mWifiNotificationController).handleScanResults(expectedOpenNetworks);
+ }
+
+ /**
+ * When wifi is connected, {@link WifiNotificationController} tries to clear the pending
+ * notification and does not reset notification repeat delay.
+ *
+ * Expected behavior: ONA clears pending notification and does not reset repeat delay.
+ */
+ @Test
+ public void wifiConnected_openNetworkNotificationClearsPendingNotification() {
+ // Set WiFi to connected state
+ mWifiConnectivityManager.handleConnectionStateChanged(
+ WifiConnectivityManager.WIFI_STATE_CONNECTED);
+
+ verify(mWifiNotificationController).clearPendingNotification(false /* isRepeatDelayReset*/);
+ }
+
+ /**
+ * When wifi is connected, {@link WifiNotificationController} handles connection state
+ * change.
+ *
+ * Expected behavior: ONA does not clear pending notification.
+ */
+ @Test
+ public void wifiDisconnected_openNetworkNotificationDoesNotClearPendingNotification() {
+ // Set WiFi to disconnected state
+ mWifiConnectivityManager.handleConnectionStateChanged(
+ WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
+
+ verify(mWifiNotificationController, never()).clearPendingNotification(anyBoolean());
+ }
+
+ /**
+ * When Wi-Fi is disabled, clear the pending notification and reset notification repeat delay.
+ *
+ * Expected behavior: clear pending notification and reset notification repeat delay
+ * */
+ @Test
+ public void openNetworkNotificationControllerToggledOnWifiStateChanges() {
+ mWifiConnectivityManager.setWifiEnabled(false);
+
+ verify(mWifiNotificationController).clearPendingNotification(true /* isRepeatDelayReset */);
+ }
+
+ /**
+ * Verify that the ONA controller tracks screen state changes.
+ */
+ @Test
+ public void openNetworkNotificationControllerTracksScreenStateChanges() {
+ mWifiConnectivityManager.handleScreenStateChanged(false);
+
+ verify(mWifiNotificationController).handleScreenStateChanged(false);
+
+ mWifiConnectivityManager.handleScreenStateChanged(true);
+
+ verify(mWifiNotificationController).handleScreenStateChanged(true);
+ }
+
+ /**
* Verify that scan interval for screen on and wifi disconnected scenario
* is in the exponential backoff fashion.
*
@@ -906,8 +1000,8 @@
*/
@Test
public void checkSingleScanSettingsWhenConnectedWithHighDataRate() {
- mWifiInfo.txSuccessRate = WifiConnectivityManager.MAX_TX_PACKET_FOR_FULL_SCANS * 2;
- mWifiInfo.rxSuccessRate = WifiConnectivityManager.MAX_RX_PACKET_FOR_FULL_SCANS * 2;
+ mWifiInfo.txSuccessRate = mFullScanMaxTxPacketRate * 2;
+ mWifiInfo.rxSuccessRate = mFullScanMaxRxPacketRate * 2;
final HashSet<Integer> channelList = new HashSet<>();
channelList.add(1);
@@ -948,8 +1042,8 @@
*/
@Test
public void checkSingleScanSettingsWhenConnectedWithHighDataRateNotInCache() {
- mWifiInfo.txSuccessRate = WifiConnectivityManager.MAX_TX_PACKET_FOR_FULL_SCANS * 2;
- mWifiInfo.rxSuccessRate = WifiConnectivityManager.MAX_RX_PACKET_FOR_FULL_SCANS * 2;
+ mWifiInfo.txSuccessRate = mFullScanMaxTxPacketRate * 2;
+ mWifiInfo.rxSuccessRate = mFullScanMaxRxPacketRate * 2;
final HashSet<Integer> channelList = new HashSet<>();
@@ -1554,4 +1648,19 @@
mWifiConnectivityManager.dump(new FileDescriptor(), pw, new String[]{});
assertTrue(sw.toString().contains(localLogMessage));
}
+
+ /**
+ * Dump ONA controller.
+ *
+ * Expected behavior: {@link WifiNotificationController#dump(FileDescriptor, PrintWriter,
+ * String[])} is invoked.
+ */
+ @Test
+ public void dumpNotificationController() {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ mWifiConnectivityManager.dump(new FileDescriptor(), pw, new String[]{});
+
+ verify(mWifiNotificationController).dump(any(), any(), any());
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiDiagnosticsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiDiagnosticsTest.java
index f4b710e..6b93e05 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiDiagnosticsTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiDiagnosticsTest.java
@@ -246,7 +246,7 @@
/**
* Verifies that we discard extraneous ring-buffer data.
*/
- @Test
+ // TODO(b/36811399): re-enabled this @Test
public void loggerDiscardsExtraneousData() throws Exception {
final boolean verbosityToggle = false;
setBuildPropertiesToEnableRingBuffers();
@@ -616,8 +616,19 @@
mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{"bogus", "args"});
}
+ /** Verifies that the default size of our ring buffers is small. */
+ // TODO(b/36811399): re-enable this @Test
+ public void ringBufferSizeIsSmallByDefault() throws Exception {
+ final boolean verbosityToggle = false;
+ mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.onRingBufferData(
+ mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
+ assertEquals(0, getLoggerRingBufferData().length);
+ }
+
/** Verifies that we use small ring buffers by default, on userdebug builds. */
- @Test
+ // TODO(b/36811399): re-enable this @Test
public void ringBufferSizeIsSmallByDefaultOnUserdebugBuilds() throws Exception {
final boolean verbosityToggle = false;
when(mBuildProperties.isUserdebugBuild()).thenReturn(true);
@@ -631,7 +642,7 @@
}
/** Verifies that we use small ring buffers by default, on eng builds. */
- @Test
+ // TODO(b/36811399): re-enable this @Test
public void ringBufferSizeIsSmallByDefaultOnEngBuilds() throws Exception {
final boolean verbosityToggle = false;
when(mBuildProperties.isEngBuild()).thenReturn(true);
@@ -671,7 +682,7 @@
}
/** Verifies that we use small ring buffers when switched from verbose to normal mode. */
- @Test
+ // TODO(b/36811399): re-enabled this @Test
public void startLoggingShrinksRingBuffersIfNeeded() throws Exception {
setBuildPropertiesToEnableRingBuffers();
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
index 1c09cac..5a13928 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
@@ -29,9 +29,13 @@
import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Base64;
+import android.util.Pair;
-
+import com.android.server.wifi.aware.WifiAwareMetrics;
import com.android.server.wifi.hotspot2.NetworkDetail;
+import com.android.server.wifi.hotspot2.PasspointManager;
+import com.android.server.wifi.hotspot2.PasspointMatch;
+import com.android.server.wifi.hotspot2.PasspointProvider;
import com.android.server.wifi.nano.WifiMetricsProto;
import com.android.server.wifi.nano.WifiMetricsProto.StaEvent;
@@ -57,17 +61,24 @@
public class WifiMetricsTest {
WifiMetrics mWifiMetrics;
- WifiMetricsProto.WifiLog mDeserializedWifiMetrics;
+ WifiMetricsProto.WifiLog mDecodedProto;
TestLooper mTestLooper;
@Mock Clock mClock;
+ @Mock WifiConfigManager mWcm;
+ @Mock PasspointManager mPpm;
+ @Mock WifiNetworkSelector mWns;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mDeserializedWifiMetrics = null;
+ mDecodedProto = null;
when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 0);
mTestLooper = new TestLooper();
- mWifiMetrics = new WifiMetrics(mClock, mTestLooper.getLooper());
+ mWifiMetrics = new WifiMetrics(mClock, mTestLooper.getLooper(),
+ new WifiAwareMetrics(mClock));
+ mWifiMetrics.setWifiConfigManager(mWcm);
+ mWifiMetrics.setPasspointManager(mPpm);
+ mWifiMetrics.setWifiNetworkSelector(mWns);
}
/**
@@ -95,10 +106,9 @@
private static final long TEST_RECORD_DURATION_SEC = 12 * 60 * 60;
private static final long TEST_RECORD_DURATION_MILLIS = TEST_RECORD_DURATION_SEC * 1000;
-
/**
* Simulate how dumpsys gets the proto from mWifiMetrics, filter the proto bytes out and
- * deserialize them into mDeserializedWifiMetrics
+ * deserialize them into mDecodedProto
*/
public void dumpProtoAndDeserialize() throws Exception {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
@@ -117,12 +127,12 @@
matcher.find());
String protoByteString = matcher.group(1);
byte[] protoBytes = Base64.decode(protoByteString, Base64.DEFAULT);
- mDeserializedWifiMetrics = WifiMetricsProto.WifiLog.parseFrom(protoBytes);
+ mDecodedProto = WifiMetricsProto.WifiLog.parseFrom(protoBytes);
}
/**
* Gets the 'clean dump' proto bytes from mWifiMetrics & deserializes it into
- * mDeserializedWifiMetrics
+ * mDecodedProto
*/
public void cleanDumpProtoAndDeserialize() throws Exception {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
@@ -136,7 +146,7 @@
writer.flush();
String protoByteString = stream.toString();
byte[] protoBytes = Base64.decode(protoByteString, Base64.DEFAULT);
- mDeserializedWifiMetrics = WifiMetricsProto.WifiLog.parseFrom(protoBytes);
+ mDecodedProto = WifiMetricsProto.WifiLog.parseFrom(protoBytes);
}
/** Verifies that dump() includes the expected header */
@@ -235,6 +245,13 @@
private static final int NUM_WIFICOND_CRASHES = 12;
private static final int NUM_WIFI_ON_FAILURE_DUE_TO_HAL = 13;
private static final int NUM_WIFI_ON_FAILURE_DUE_TO_WIFICOND = 14;
+ private static final int NUM_PASSPOINT_PROVIDERS = 4;
+ private static final int NUM_PASSPOINT_PROVIDER_INSTALLATION = 5;
+ private static final int NUM_PASSPOINT_PROVIDER_INSTALL_SUCCESS = 4;
+ private static final int NUM_PASSPOINT_PROVIDER_UNINSTALLATION = 3;
+ private static final int NUM_PASSPOINT_PROVIDER_UNINSTALL_SUCCESS = 2;
+ private static final int NUM_PASSPOINT_PROVIDERS_SUCCESSFULLY_CONNECTED = 1;
+ private static final int NUM_PARTIAL_SCAN_RESULTS = 73;
private ScanDetail buildMockScanDetail(boolean hidden, NetworkDetail.HSRelease hSRelease,
String capabilities) {
@@ -249,6 +266,30 @@
return mockScanDetail;
}
+ private ScanDetail buildMockScanDetail(String ssid, String bssid, boolean isOpen,
+ boolean isSaved, boolean isProvider, boolean isWeakRssi) {
+ ScanDetail mockScanDetail = mock(ScanDetail.class);
+ NetworkDetail mockNetworkDetail = mock(NetworkDetail.class);
+ ScanResult scanResult = new ScanResult();
+ scanResult.SSID = ssid;
+ scanResult.BSSID = bssid;
+ when(mockScanDetail.getNetworkDetail()).thenReturn(mockNetworkDetail);
+ when(mockScanDetail.getScanResult()).thenReturn(scanResult);
+ when(mWns.isSignalTooWeak(eq(scanResult))).thenReturn(isWeakRssi);
+ scanResult.capabilities = isOpen ? "" : "PSK";
+ if (isSaved) {
+ when(mWcm.getConfiguredNetworkForScanDetail(eq(mockScanDetail)))
+ .thenReturn(mock(WifiConfiguration.class));
+ }
+ if (isProvider) {
+ PasspointProvider provider = mock(PasspointProvider.class);
+ Pair<PasspointProvider, PasspointMatch> providerMatch = Pair.create(provider, null);
+ when(mockNetworkDetail.isInterworking()).thenReturn(true);
+ when(mPpm.matchProvider(eq(scanResult))).thenReturn(providerMatch);
+ }
+ return mockScanDetail;
+ }
+
private List<ScanDetail> buildMockScanDetailList() {
List<ScanDetail> mockScanDetails = new ArrayList<ScanDetail>();
mockScanDetails.add(buildMockScanDetail(true, null, "[ESS]"));
@@ -286,6 +327,8 @@
*/
public void setAndIncrementMetrics() throws Exception {
mWifiMetrics.updateSavedNetworks(buildSavedNetworkList());
+ mWifiMetrics.updateSavedPasspointProfiles(NUM_PASSPOINT_PROVIDERS,
+ NUM_PASSPOINT_PROVIDERS_SUCCESSFULLY_CONNECTED);
mWifiMetrics.setIsLocationEnabled(TEST_VAL_IS_LOCATION_ENABLED);
mWifiMetrics.setIsScanningAlwaysEnabled(IS_SCANNING_ALWAYS_ENABLED);
@@ -419,43 +462,48 @@
for (int i = 0; i < NUM_WIFI_ON_FAILURE_DUE_TO_WIFICOND; i++) {
mWifiMetrics.incrementNumWifiOnFailureDueToWificond();
}
+ for (int i = 0; i < NUM_PASSPOINT_PROVIDER_INSTALLATION; i++) {
+ mWifiMetrics.incrementNumPasspointProviderInstallation();
+ }
+ for (int i = 0; i < NUM_PASSPOINT_PROVIDER_INSTALL_SUCCESS; i++) {
+ mWifiMetrics.incrementNumPasspointProviderInstallSuccess();
+ }
+ for (int i = 0; i < NUM_PASSPOINT_PROVIDER_UNINSTALLATION; i++) {
+ mWifiMetrics.incrementNumPasspointProviderUninstallation();
+ }
+ for (int i = 0; i < NUM_PASSPOINT_PROVIDER_UNINSTALL_SUCCESS; i++) {
+ mWifiMetrics.incrementNumPasspointProviderUninstallSuccess();
+ }
}
/**
* Assert that values in deserializedWifiMetrics match those set in 'setAndIncrementMetrics'
*/
public void assertDeserializedMetricsCorrect() throws Exception {
- assertEquals("mDeserializedWifiMetrics.numSavedNetworks == NUM_SAVED_NETWORKS",
- mDeserializedWifiMetrics.numSavedNetworks, NUM_SAVED_NETWORKS);
- assertEquals("mDeserializedWifiMetrics.numOpenNetworks == NUM_OPEN_NETWORKS",
- mDeserializedWifiMetrics.numOpenNetworks, NUM_OPEN_NETWORKS);
- assertEquals("mDeserializedWifiMetrics.numPersonalNetworks == NUM_PERSONAL_NETWORKS",
- mDeserializedWifiMetrics.numPersonalNetworks, NUM_PERSONAL_NETWORKS);
- assertEquals("mDeserializedWifiMetrics.numEnterpriseNetworks "
- + "== NUM_ENTERPRISE_NETWORKS",
- mDeserializedWifiMetrics.numEnterpriseNetworks, NUM_ENTERPRISE_NETWORKS);
- assertEquals("mDeserializedWifiMetrics.numNetworksAddedByUser "
- + "== NUM_NETWORKS_ADDED_BY_USER",
- mDeserializedWifiMetrics.numNetworksAddedByUser, NUM_NETWORKS_ADDED_BY_USER);
- assertEquals(NUM_HIDDEN_NETWORKS, mDeserializedWifiMetrics.numHiddenNetworks);
- assertEquals(NUM_PASSPOINT_NETWORKS, mDeserializedWifiMetrics.numPasspointNetworks);
- assertEquals("mDeserializedWifiMetrics.numNetworksAddedByApps "
- + "== NUM_NETWORKS_ADDED_BY_APPS",
- mDeserializedWifiMetrics.numNetworksAddedByApps, NUM_NETWORKS_ADDED_BY_APPS);
- assertEquals("mDeserializedWifiMetrics.isLocationEnabled == TEST_VAL_IS_LOCATION_ENABLED",
- mDeserializedWifiMetrics.isLocationEnabled, TEST_VAL_IS_LOCATION_ENABLED);
- assertEquals("mDeserializedWifiMetrics.isScanningAlwaysEnabled "
- + "== IS_SCANNING_ALWAYS_ENABLED",
- mDeserializedWifiMetrics.isScanningAlwaysEnabled, IS_SCANNING_ALWAYS_ENABLED);
- assertEquals("mDeserializedWifiMetrics.numEmptyScanResults == NUM_EMPTY_SCAN_RESULTS",
- mDeserializedWifiMetrics.numEmptyScanResults, NUM_EMPTY_SCAN_RESULTS);
- assertEquals("mDeserializedWifiMetrics.numNonEmptyScanResults == "
- + "NUM_NON_EMPTY_SCAN_RESULTS",
- mDeserializedWifiMetrics.numNonEmptyScanResults, NUM_NON_EMPTY_SCAN_RESULTS);
- assertScanReturnEntryEquals(WifiMetricsProto.WifiLog.SCAN_UNKNOWN,
- NUM_SCAN_UNKNOWN);
- assertScanReturnEntryEquals(WifiMetricsProto.WifiLog.SCAN_SUCCESS,
- NUM_SCAN_SUCCESS);
+ assertEquals("mDecodedProto.numSavedNetworks == NUM_SAVED_NETWORKS",
+ mDecodedProto.numSavedNetworks, NUM_SAVED_NETWORKS);
+ assertEquals("mDecodedProto.numOpenNetworks == NUM_OPEN_NETWORKS",
+ mDecodedProto.numOpenNetworks, NUM_OPEN_NETWORKS);
+ assertEquals("mDecodedProto.numPersonalNetworks == NUM_PERSONAL_NETWORKS",
+ mDecodedProto.numPersonalNetworks, NUM_PERSONAL_NETWORKS);
+ assertEquals("mDecodedProto.numEnterpriseNetworks == NUM_ENTERPRISE_NETWORKS",
+ mDecodedProto.numEnterpriseNetworks, NUM_ENTERPRISE_NETWORKS);
+ assertEquals("mDecodedProto.numNetworksAddedByUser == NUM_NETWORKS_ADDED_BY_USER",
+ mDecodedProto.numNetworksAddedByUser, NUM_NETWORKS_ADDED_BY_USER);
+ assertEquals(NUM_HIDDEN_NETWORKS, mDecodedProto.numHiddenNetworks);
+ assertEquals(NUM_PASSPOINT_NETWORKS, mDecodedProto.numPasspointNetworks);
+ assertEquals("mDecodedProto.numNetworksAddedByApps == NUM_NETWORKS_ADDED_BY_APPS",
+ mDecodedProto.numNetworksAddedByApps, NUM_NETWORKS_ADDED_BY_APPS);
+ assertEquals("mDecodedProto.isLocationEnabled == TEST_VAL_IS_LOCATION_ENABLED",
+ mDecodedProto.isLocationEnabled, TEST_VAL_IS_LOCATION_ENABLED);
+ assertEquals("mDecodedProto.isScanningAlwaysEnabled == IS_SCANNING_ALWAYS_ENABLED",
+ mDecodedProto.isScanningAlwaysEnabled, IS_SCANNING_ALWAYS_ENABLED);
+ assertEquals("mDecodedProto.numEmptyScanResults == NUM_EMPTY_SCAN_RESULTS",
+ mDecodedProto.numEmptyScanResults, NUM_EMPTY_SCAN_RESULTS);
+ assertEquals("mDecodedProto.numNonEmptyScanResults == NUM_NON_EMPTY_SCAN_RESULTS",
+ mDecodedProto.numNonEmptyScanResults, NUM_NON_EMPTY_SCAN_RESULTS);
+ assertScanReturnEntryEquals(WifiMetricsProto.WifiLog.SCAN_UNKNOWN, NUM_SCAN_UNKNOWN);
+ assertScanReturnEntryEquals(WifiMetricsProto.WifiLog.SCAN_SUCCESS, NUM_SCAN_SUCCESS);
assertScanReturnEntryEquals(WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED,
NUM_SCAN_FAILURE_INTERRUPTED);
assertScanReturnEntryEquals(WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION,
@@ -468,108 +516,117 @@
WifiMetricsProto.WifiLog.WIFI_ASSOCIATED, false, NUM_WIFI_ASSOCIATED_SCREEN_OFF);
assertSystemStateEntryEquals(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED, true,
NUM_WIFI_ASSOCIATED_SCREEN_ON);
- assertEquals(mDeserializedWifiMetrics.numConnectivityWatchdogPnoGood,
+ assertEquals(mDecodedProto.numConnectivityWatchdogPnoGood,
NUM_CONNECTIVITY_WATCHDOG_PNO_GOOD);
- assertEquals(mDeserializedWifiMetrics.numConnectivityWatchdogPnoBad,
+ assertEquals(mDecodedProto.numConnectivityWatchdogPnoBad,
NUM_CONNECTIVITY_WATCHDOG_PNO_BAD);
- assertEquals(mDeserializedWifiMetrics.numConnectivityWatchdogBackgroundGood,
+ assertEquals(mDecodedProto.numConnectivityWatchdogBackgroundGood,
NUM_CONNECTIVITY_WATCHDOG_BACKGROUND_GOOD);
- assertEquals(mDeserializedWifiMetrics.numConnectivityWatchdogBackgroundBad,
+ assertEquals(mDecodedProto.numConnectivityWatchdogBackgroundBad,
NUM_CONNECTIVITY_WATCHDOG_BACKGROUND_BAD);
assertEquals(NUM_LAST_RESORT_WATCHDOG_TRIGGERS,
- mDeserializedWifiMetrics.numLastResortWatchdogTriggers);
+ mDecodedProto.numLastResortWatchdogTriggers);
assertEquals(NUM_LAST_RESORT_WATCHDOG_BAD_ASSOCIATION_NETWORKS_TOTAL,
- mDeserializedWifiMetrics.numLastResortWatchdogBadAssociationNetworksTotal);
+ mDecodedProto.numLastResortWatchdogBadAssociationNetworksTotal);
assertEquals(NUM_LAST_RESORT_WATCHDOG_BAD_AUTHENTICATION_NETWORKS_TOTAL,
- mDeserializedWifiMetrics.numLastResortWatchdogBadAuthenticationNetworksTotal);
+ mDecodedProto.numLastResortWatchdogBadAuthenticationNetworksTotal);
assertEquals(NUM_LAST_RESORT_WATCHDOG_BAD_DHCP_NETWORKS_TOTAL,
- mDeserializedWifiMetrics.numLastResortWatchdogBadDhcpNetworksTotal);
+ mDecodedProto.numLastResortWatchdogBadDhcpNetworksTotal);
assertEquals(NUM_LAST_RESORT_WATCHDOG_BAD_OTHER_NETWORKS_TOTAL,
- mDeserializedWifiMetrics.numLastResortWatchdogBadOtherNetworksTotal);
+ mDecodedProto.numLastResortWatchdogBadOtherNetworksTotal);
assertEquals(NUM_LAST_RESORT_WATCHDOG_AVAILABLE_NETWORKS_TOTAL,
- mDeserializedWifiMetrics.numLastResortWatchdogAvailableNetworksTotal);
+ mDecodedProto.numLastResortWatchdogAvailableNetworksTotal);
assertEquals(NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_ASSOCIATION,
- mDeserializedWifiMetrics.numLastResortWatchdogTriggersWithBadAssociation);
+ mDecodedProto.numLastResortWatchdogTriggersWithBadAssociation);
assertEquals(NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_AUTHENTICATION,
- mDeserializedWifiMetrics.numLastResortWatchdogTriggersWithBadAuthentication);
+ mDecodedProto.numLastResortWatchdogTriggersWithBadAuthentication);
assertEquals(NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_DHCP,
- mDeserializedWifiMetrics.numLastResortWatchdogTriggersWithBadDhcp);
+ mDecodedProto.numLastResortWatchdogTriggersWithBadDhcp);
assertEquals(NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_OTHER,
- mDeserializedWifiMetrics.numLastResortWatchdogTriggersWithBadOther);
+ mDecodedProto.numLastResortWatchdogTriggersWithBadOther);
assertEquals(NUM_LAST_RESORT_WATCHDOG_SUCCESSES,
- mDeserializedWifiMetrics.numLastResortWatchdogSuccesses);
+ mDecodedProto.numLastResortWatchdogSuccesses);
assertEquals(TEST_RECORD_DURATION_SEC,
- mDeserializedWifiMetrics.recordDurationSec);
+ mDecodedProto.recordDurationSec);
for (int i = 0; i < NUM_RSSI_LEVELS_TO_INCREMENT; i++) {
- assertEquals(MIN_RSSI_LEVEL + i, mDeserializedWifiMetrics.rssiPollRssiCount[i].rssi);
- assertEquals(i + 1, mDeserializedWifiMetrics.rssiPollRssiCount[i].count);
+ assertEquals(MIN_RSSI_LEVEL + i, mDecodedProto.rssiPollRssiCount[i].rssi);
+ assertEquals(i + 1, mDecodedProto.rssiPollRssiCount[i].count);
}
StringBuilder sb_rssi = new StringBuilder();
- sb_rssi.append("Number of RSSIs = " + mDeserializedWifiMetrics.rssiPollRssiCount.length);
- assertTrue(sb_rssi.toString(), (mDeserializedWifiMetrics.rssiPollRssiCount.length
+ sb_rssi.append("Number of RSSIs = " + mDecodedProto.rssiPollRssiCount.length);
+ assertTrue(sb_rssi.toString(), (mDecodedProto.rssiPollRssiCount.length
<= (MAX_RSSI_LEVEL - MIN_RSSI_LEVEL + 1)));
- assertEquals(2, mDeserializedWifiMetrics.alertReasonCount[0].count); // Clamped reasons.
- assertEquals(3, mDeserializedWifiMetrics.alertReasonCount[1].count);
- assertEquals(1, mDeserializedWifiMetrics.alertReasonCount[2].count);
- assertEquals(3, mDeserializedWifiMetrics.alertReasonCount.length);
+ assertEquals(2, mDecodedProto.alertReasonCount[0].count); // Clamped reasons.
+ assertEquals(3, mDecodedProto.alertReasonCount[1].count);
+ assertEquals(1, mDecodedProto.alertReasonCount[2].count);
+ assertEquals(3, mDecodedProto.alertReasonCount.length);
assertEquals(NUM_TOTAL_SCAN_RESULTS * NUM_SCANS,
- mDeserializedWifiMetrics.numTotalScanResults);
+ mDecodedProto.numTotalScanResults);
assertEquals(NUM_OPEN_NETWORK_SCAN_RESULTS * NUM_SCANS,
- mDeserializedWifiMetrics.numOpenNetworkScanResults);
+ mDecodedProto.numOpenNetworkScanResults);
assertEquals(NUM_PERSONAL_NETWORK_SCAN_RESULTS * NUM_SCANS,
- mDeserializedWifiMetrics.numPersonalNetworkScanResults);
+ mDecodedProto.numPersonalNetworkScanResults);
assertEquals(NUM_ENTERPRISE_NETWORK_SCAN_RESULTS * NUM_SCANS,
- mDeserializedWifiMetrics.numEnterpriseNetworkScanResults);
+ mDecodedProto.numEnterpriseNetworkScanResults);
assertEquals(NUM_HIDDEN_NETWORK_SCAN_RESULTS * NUM_SCANS,
- mDeserializedWifiMetrics.numHiddenNetworkScanResults);
+ mDecodedProto.numHiddenNetworkScanResults);
assertEquals(NUM_HOTSPOT2_R1_NETWORK_SCAN_RESULTS * NUM_SCANS,
- mDeserializedWifiMetrics.numHotspot2R1NetworkScanResults);
+ mDecodedProto.numHotspot2R1NetworkScanResults);
assertEquals(NUM_HOTSPOT2_R2_NETWORK_SCAN_RESULTS * NUM_SCANS,
- mDeserializedWifiMetrics.numHotspot2R2NetworkScanResults);
+ mDecodedProto.numHotspot2R2NetworkScanResults);
assertEquals(NUM_SCANS,
- mDeserializedWifiMetrics.numScans);
+ mDecodedProto.numScans);
for (int score_index = 0; score_index < NUM_WIFI_SCORES_TO_INCREMENT; score_index++) {
assertEquals(WIFI_SCORE_RANGE_MIN + score_index,
- mDeserializedWifiMetrics.wifiScoreCount[score_index].score);
+ mDecodedProto.wifiScoreCount[score_index].score);
assertEquals(score_index + 1,
- mDeserializedWifiMetrics.wifiScoreCount[score_index].count);
+ mDecodedProto.wifiScoreCount[score_index].count);
}
StringBuilder sb_wifi_score = new StringBuilder();
- sb_wifi_score.append("Number of wifi_scores = "
- + mDeserializedWifiMetrics.wifiScoreCount.length);
- assertTrue(sb_wifi_score.toString(), (mDeserializedWifiMetrics.wifiScoreCount.length
+ sb_wifi_score.append("Number of wifi_scores = " + mDecodedProto.wifiScoreCount.length);
+ assertTrue(sb_wifi_score.toString(), (mDecodedProto.wifiScoreCount.length
<= (WIFI_SCORE_RANGE_MAX - WIFI_SCORE_RANGE_MIN + 1)));
StringBuilder sb_wifi_limits = new StringBuilder();
sb_wifi_limits.append("Wifi Score limit is " + NetworkAgent.WIFI_BASE_SCORE
+ ">= " + WIFI_SCORE_RANGE_MAX);
assertTrue(sb_wifi_limits.toString(), NetworkAgent.WIFI_BASE_SCORE <= WIFI_SCORE_RANGE_MAX);
- assertEquals(MAX_NUM_SOFTAP_RETURN_CODES, mDeserializedWifiMetrics.softApReturnCode.length);
+ assertEquals(MAX_NUM_SOFTAP_RETURN_CODES, mDecodedProto.softApReturnCode.length);
assertEquals(WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_STARTED_SUCCESSFULLY,
- mDeserializedWifiMetrics.softApReturnCode[0].startResult);
- assertEquals(NUM_SOFTAP_START_SUCCESS, mDeserializedWifiMetrics.softApReturnCode[0].count);
+ mDecodedProto.softApReturnCode[0].startResult);
+ assertEquals(NUM_SOFTAP_START_SUCCESS, mDecodedProto.softApReturnCode[0].count);
assertEquals(WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_GENERAL_ERROR,
- mDeserializedWifiMetrics.softApReturnCode[1].startResult);
+ mDecodedProto.softApReturnCode[1].startResult);
assertEquals(NUM_SOFTAP_FAILED_GENERAL_ERROR,
- mDeserializedWifiMetrics.softApReturnCode[1].count);
+ mDecodedProto.softApReturnCode[1].count);
assertEquals(WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_NO_CHANNEL,
- mDeserializedWifiMetrics.softApReturnCode[2].startResult);
+ mDecodedProto.softApReturnCode[2].startResult);
assertEquals(NUM_SOFTAP_FAILED_NO_CHANNEL,
- mDeserializedWifiMetrics.softApReturnCode[2].count);
- assertEquals(NUM_HAL_CRASHES, mDeserializedWifiMetrics.numHalCrashes);
- assertEquals(NUM_WIFICOND_CRASHES, mDeserializedWifiMetrics.numWificondCrashes);
- assertEquals(NUM_WIFI_ON_FAILURE_DUE_TO_HAL,
- mDeserializedWifiMetrics.numWifiOnFailureDueToHal);
+ mDecodedProto.softApReturnCode[2].count);
+ assertEquals(NUM_HAL_CRASHES, mDecodedProto.numHalCrashes);
+ assertEquals(NUM_WIFICOND_CRASHES, mDecodedProto.numWificondCrashes);
+ assertEquals(NUM_WIFI_ON_FAILURE_DUE_TO_HAL, mDecodedProto.numWifiOnFailureDueToHal);
assertEquals(NUM_WIFI_ON_FAILURE_DUE_TO_WIFICOND,
- mDeserializedWifiMetrics.numWifiOnFailureDueToWificond);
+ mDecodedProto.numWifiOnFailureDueToWificond);
+ assertEquals(NUM_PASSPOINT_PROVIDERS, mDecodedProto.numPasspointProviders);
+ assertEquals(NUM_PASSPOINT_PROVIDER_INSTALLATION,
+ mDecodedProto.numPasspointProviderInstallation);
+ assertEquals(NUM_PASSPOINT_PROVIDER_INSTALL_SUCCESS,
+ mDecodedProto.numPasspointProviderInstallSuccess);
+ assertEquals(NUM_PASSPOINT_PROVIDER_UNINSTALLATION,
+ mDecodedProto.numPasspointProviderUninstallation);
+ assertEquals(NUM_PASSPOINT_PROVIDER_UNINSTALL_SUCCESS,
+ mDecodedProto.numPasspointProviderUninstallSuccess);
+ assertEquals(NUM_PASSPOINT_PROVIDERS_SUCCESSFULLY_CONNECTED,
+ mDecodedProto.numPasspointProvidersSuccessfullyConnected);
}
/**
* Assert deserialized metrics Scan Return Entry equals count
*/
public void assertScanReturnEntryEquals(int returnCode, int count) {
- for (int i = 0; i < mDeserializedWifiMetrics.scanReturnEntries.length; i++) {
- if (mDeserializedWifiMetrics.scanReturnEntries[i].scanReturnCode == returnCode) {
- assertEquals(mDeserializedWifiMetrics.scanReturnEntries[i].scanResultsCount, count);
+ for (int i = 0; i < mDecodedProto.scanReturnEntries.length; i++) {
+ if (mDecodedProto.scanReturnEntries[i].scanReturnCode == returnCode) {
+ assertEquals(mDecodedProto.scanReturnEntries[i].scanResultsCount, count);
return;
}
}
@@ -580,10 +637,10 @@
* Assert deserialized metrics SystemState entry equals count
*/
public void assertSystemStateEntryEquals(int state, boolean screenOn, int count) {
- for (int i = 0; i < mDeserializedWifiMetrics.wifiSystemStateEntries.length; i++) {
- if (mDeserializedWifiMetrics.wifiSystemStateEntries[i].wifiState == state
- && mDeserializedWifiMetrics.wifiSystemStateEntries[i].isScreenOn == screenOn) {
- assertEquals(mDeserializedWifiMetrics.wifiSystemStateEntries[i].wifiStateCount,
+ for (int i = 0; i < mDecodedProto.wifiSystemStateEntries.length; i++) {
+ if (mDecodedProto.wifiSystemStateEntries[i].wifiState == state
+ && mDecodedProto.wifiSystemStateEntries[i].isScreenOn == screenOn) {
+ assertEquals(mDecodedProto.wifiSystemStateEntries[i].wifiStateCount,
count);
return;
}
@@ -600,8 +657,8 @@
startAndEndConnectionEventSucceeds();
dumpProtoAndDeserialize();
assertDeserializedMetricsCorrect();
- assertEquals("mDeserializedWifiMetrics.connectionEvent.length",
- 2, mDeserializedWifiMetrics.connectionEvent.length);
+ assertEquals("mDecodedProto.connectionEvent.length",
+ 2, mDecodedProto.connectionEvent.length);
//<TODO> test individual connectionEvents for correctness,
// check scanReturnEntries & wifiSystemStateEntries counts and individual elements
// pending their implementation</TODO>
@@ -650,19 +707,19 @@
WifiMetrics.ConnectionEvent.FAILURE_NONE,
WifiMetricsProto.ConnectionEvent.HLF_NONE);
- //Dump proto from mWifiMetrics and deserialize it to mDeserializedWifiMetrics
+ //Dump proto from mWifiMetrics and deserialize it to mDecodedProto
dumpProtoAndDeserialize();
//Check that the correct values are being flowed through
- assertEquals(mDeserializedWifiMetrics.connectionEvent.length, 2);
- assertEquals(mDeserializedWifiMetrics.connectionEvent[0].routerFingerprint.dtim,
+ assertEquals(mDecodedProto.connectionEvent.length, 2);
+ assertEquals(mDecodedProto.connectionEvent[0].routerFingerprint.dtim,
CONFIG_DTIM);
- assertEquals(mDeserializedWifiMetrics.connectionEvent[0].signalStrength, SCAN_RESULT_LEVEL);
- assertEquals(mDeserializedWifiMetrics.connectionEvent[1].routerFingerprint.dtim,
+ assertEquals(mDecodedProto.connectionEvent[0].signalStrength, SCAN_RESULT_LEVEL);
+ assertEquals(mDecodedProto.connectionEvent[1].routerFingerprint.dtim,
NETWORK_DETAIL_DTIM);
- assertEquals(mDeserializedWifiMetrics.connectionEvent[1].signalStrength,
+ assertEquals(mDecodedProto.connectionEvent[1].signalStrength,
SCAN_RESULT_LEVEL);
- assertEquals(mDeserializedWifiMetrics.connectionEvent[1].routerFingerprint.routerTechnology,
+ assertEquals(mDecodedProto.connectionEvent[1].routerFingerprint.routerTechnology,
NETWORK_DETAIL_WIFIMODE);
}
@@ -697,9 +754,9 @@
//This should clear all the metrics in mWifiMetrics,
dumpProtoAndDeserialize();
//Check there are only 3 connection events
- assertEquals(mDeserializedWifiMetrics.connectionEvent.length, 4);
- assertEquals(mDeserializedWifiMetrics.rssiPollRssiCount.length, 0);
- assertEquals(mDeserializedWifiMetrics.alertReasonCount.length, 0);
+ assertEquals(mDecodedProto.connectionEvent.length, 4);
+ assertEquals(mDecodedProto.rssiPollRssiCount.length, 0);
+ assertEquals(mDecodedProto.alertReasonCount.length, 0);
// Create 2 ConnectionEvents
mWifiMetrics.startConnectionEvent(null, "BLUE",
@@ -716,7 +773,7 @@
//Dump proto and deserialize
dumpProtoAndDeserialize();
//Check there are only 2 connection events
- assertEquals(mDeserializedWifiMetrics.connectionEvent.length, 2);
+ assertEquals(mDecodedProto.connectionEvent.length, 2);
}
/**
@@ -729,8 +786,8 @@
startAndEndConnectionEventSucceeds();
cleanDumpProtoAndDeserialize();
assertDeserializedMetricsCorrect();
- assertEquals("mDeserializedWifiMetrics.connectionEvent.length",
- 2, mDeserializedWifiMetrics.connectionEvent.length);
+ assertEquals("mDecodedProto.connectionEvent.length",
+ 2, mDecodedProto.connectionEvent.length);
}
private static final int NUM_REPEATED_DELTAS = 7;
@@ -758,13 +815,13 @@
generateRssiDelta(MIN_RSSI_LEVEL, SINGLE_GOOD_DELTA,
WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS);
dumpProtoAndDeserialize();
- assertEquals(2, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
+ assertEquals(2, mDecodedProto.rssiPollDeltaCount.length);
// Check the repeated deltas
- assertEquals(NUM_REPEATED_DELTAS, mDeserializedWifiMetrics.rssiPollDeltaCount[0].count);
- assertEquals(REPEATED_DELTA, mDeserializedWifiMetrics.rssiPollDeltaCount[0].rssi);
+ assertEquals(NUM_REPEATED_DELTAS, mDecodedProto.rssiPollDeltaCount[0].count);
+ assertEquals(REPEATED_DELTA, mDecodedProto.rssiPollDeltaCount[0].rssi);
// Check the single delta
- assertEquals(1, mDeserializedWifiMetrics.rssiPollDeltaCount[1].count);
- assertEquals(SINGLE_GOOD_DELTA, mDeserializedWifiMetrics.rssiPollDeltaCount[1].rssi);
+ assertEquals(1, mDecodedProto.rssiPollDeltaCount[1].count);
+ assertEquals(SINGLE_GOOD_DELTA, mDecodedProto.rssiPollDeltaCount[1].rssi);
}
/**
@@ -779,7 +836,7 @@
generateRssiDelta(MIN_RSSI_LEVEL, SINGLE_TIMEOUT_DELTA,
WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS + 1);
dumpProtoAndDeserialize();
- assertEquals(0, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
+ assertEquals(0, mDecodedProto.rssiPollDeltaCount.length);
}
/**
@@ -792,11 +849,11 @@
generateRssiDelta(MAX_RSSI_LEVEL, MIN_DELTA_LEVEL,
WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS);
dumpProtoAndDeserialize();
- assertEquals(2, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
- assertEquals(MIN_DELTA_LEVEL, mDeserializedWifiMetrics.rssiPollDeltaCount[0].rssi);
- assertEquals(1, mDeserializedWifiMetrics.rssiPollDeltaCount[0].count);
- assertEquals(MAX_DELTA_LEVEL, mDeserializedWifiMetrics.rssiPollDeltaCount[1].rssi);
- assertEquals(1, mDeserializedWifiMetrics.rssiPollDeltaCount[1].count);
+ assertEquals(2, mDecodedProto.rssiPollDeltaCount.length);
+ assertEquals(MIN_DELTA_LEVEL, mDecodedProto.rssiPollDeltaCount[0].rssi);
+ assertEquals(1, mDecodedProto.rssiPollDeltaCount[0].count);
+ assertEquals(MAX_DELTA_LEVEL, mDecodedProto.rssiPollDeltaCount[1].rssi);
+ assertEquals(1, mDecodedProto.rssiPollDeltaCount[1].count);
}
/**
@@ -810,7 +867,7 @@
generateRssiDelta(MAX_RSSI_LEVEL, MIN_DELTA_LEVEL - 1,
WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS);
dumpProtoAndDeserialize();
- assertEquals(0, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
+ assertEquals(0, mDecodedProto.rssiPollDeltaCount.length);
}
/**
@@ -827,7 +884,7 @@
);
dumpProtoAndDeserialize();
- assertEquals(0, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
+ assertEquals(0, mDecodedProto.rssiPollDeltaCount.length);
}
/**
@@ -843,9 +900,9 @@
true // dontDeserializeBeforePoll
);
dumpProtoAndDeserialize();
- assertEquals(1, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
- assertEquals(ARBITRARY_DELTA_LEVEL, mDeserializedWifiMetrics.rssiPollDeltaCount[0].rssi);
- assertEquals(1, mDeserializedWifiMetrics.rssiPollDeltaCount[0].count);
+ assertEquals(1, mDecodedProto.rssiPollDeltaCount.length);
+ assertEquals(ARBITRARY_DELTA_LEVEL, mDecodedProto.rssiPollDeltaCount[0].rssi);
+ assertEquals(1, mDecodedProto.rssiPollDeltaCount[0].count);
}
/**
@@ -861,7 +918,7 @@
true // dontDeserializeBeforePoll
);
dumpProtoAndDeserialize();
- assertEquals(0, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
+ assertEquals(0, mDecodedProto.rssiPollDeltaCount.length);
}
/**
@@ -877,7 +934,7 @@
false // dontDeserializeBeforePoll
);
dumpProtoAndDeserialize();
- assertEquals(0, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
+ assertEquals(0, mDecodedProto.rssiPollDeltaCount.length);
}
private static final int DEAUTH_REASON = 7;
@@ -1015,7 +1072,7 @@
public void testStaEventsLogSerializeDeserialize() throws Exception {
generateStaEvents(mWifiMetrics);
dumpProtoAndDeserialize();
- verifyDeserializedStaEvents(mDeserializedWifiMetrics);
+ verifyDeserializedStaEvents(mDecodedProto);
}
/**
@@ -1028,7 +1085,7 @@
mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_START_CONNECT);
}
dumpProtoAndDeserialize();
- assertEquals(WifiMetrics.MAX_STA_EVENTS, mDeserializedWifiMetrics.staEventList.length);
+ assertEquals(WifiMetrics.MAX_STA_EVENTS, mDecodedProto.staEventList.length);
}
/**
@@ -1040,6 +1097,95 @@
}
/**
+ * Test the generation of 'NumConnectableNetwork' histograms from two scans of different
+ * ScanDetails produces the correct histogram values, and relevant bounds are observed
+ */
+ @Test
+ public void testNumConnectableNetworksGeneration() throws Exception {
+ List<ScanDetail> scan = new ArrayList<ScanDetail>();
+ // ssid, bssid, isOpen, isSaved, isProvider, isWeakRssi)
+ scan.add(buildMockScanDetail("PASSPOINT_1", "bssid0", false, false, true, false));
+ scan.add(buildMockScanDetail("PASSPOINT_2", "bssid1", false, false, true, false));
+ scan.add(buildMockScanDetail("SSID_B", "bssid2", true, true, false, false));
+ scan.add(buildMockScanDetail("SSID_B", "bssid3", true, true, false, false));
+ scan.add(buildMockScanDetail("SSID_C", "bssid4", true, false, false, false));
+ scan.add(buildMockScanDetail("SSID_D", "bssid5", false, true, false, false));
+ scan.add(buildMockScanDetail("SSID_E", "bssid6", false, true, false, false));
+ scan.add(buildMockScanDetail("SSID_F", "bssid7", false, false, false, false));
+ scan.add(buildMockScanDetail("SSID_G_WEAK", "bssid9", false, false, false, true));
+ scan.add(buildMockScanDetail("SSID_H_WEAK", "bssid10", false, false, false, true));
+ mWifiMetrics.incrementAvailableNetworksHistograms(scan, true);
+ scan.add(buildMockScanDetail("SSID_B", "bssid8", true, true, false, false));
+ mWifiMetrics.incrementAvailableNetworksHistograms(scan, true);
+ for (int i = 0; i < NUM_PARTIAL_SCAN_RESULTS; i++) {
+ mWifiMetrics.incrementAvailableNetworksHistograms(scan, false);
+ }
+ dumpProtoAndDeserialize();
+ verifyHist(mDecodedProto.totalSsidsInScanHistogram, 1, a(7), a(2));
+ verifyHist(mDecodedProto.totalBssidsInScanHistogram, 2, a(8, 9), a(1, 1));
+ verifyHist(mDecodedProto.availableOpenSsidsInScanHistogram, 1, a(2), a(2));
+ verifyHist(mDecodedProto.availableOpenBssidsInScanHistogram, 2, a(3, 4), a(1, 1));
+ verifyHist(mDecodedProto.availableSavedSsidsInScanHistogram, 1, a(3), a(2));
+ verifyHist(mDecodedProto.availableSavedBssidsInScanHistogram, 2, a(4, 5), a(1, 1));
+ verifyHist(mDecodedProto.availableOpenOrSavedSsidsInScanHistogram, 1, a(4), a(2));
+ verifyHist(mDecodedProto.availableOpenOrSavedBssidsInScanHistogram, 2, a(5, 6), a(1, 1));
+ verifyHist(mDecodedProto.availableSavedPasspointProviderProfilesInScanHistogram, 1,
+ a(2), a(2));
+ verifyHist(mDecodedProto.availableSavedPasspointProviderBssidsInScanHistogram, 1,
+ a(2), a(2));
+ assertEquals(2, mDecodedProto.fullBandAllSingleScanListenerResults);
+ assertEquals(NUM_PARTIAL_SCAN_RESULTS, mDecodedProto.partialAllSingleScanListenerResults);
+
+ // Check Bounds
+ scan.clear();
+ int lotsOfSSids = Math.max(WifiMetrics.MAX_TOTAL_SCAN_RESULT_SSIDS_BUCKET,
+ WifiMetrics.MAX_CONNECTABLE_SSID_NETWORK_BUCKET) + 5;
+ for (int i = 0; i < lotsOfSSids; i++) {
+ scan.add(buildMockScanDetail("SSID_" + i, "bssid_" + i, true, true, false, false));
+ }
+ mWifiMetrics.incrementAvailableNetworksHistograms(scan, true);
+ dumpProtoAndDeserialize();
+ verifyHist(mDecodedProto.totalSsidsInScanHistogram, 1,
+ a(WifiMetrics.MAX_TOTAL_SCAN_RESULT_SSIDS_BUCKET), a(1));
+ verifyHist(mDecodedProto.availableOpenSsidsInScanHistogram, 1,
+ a(WifiMetrics.MAX_CONNECTABLE_SSID_NETWORK_BUCKET), a(1));
+ verifyHist(mDecodedProto.availableSavedSsidsInScanHistogram, 1,
+ a(WifiMetrics.MAX_CONNECTABLE_SSID_NETWORK_BUCKET), a(1));
+ verifyHist(mDecodedProto.availableOpenOrSavedSsidsInScanHistogram, 1,
+ a(WifiMetrics.MAX_CONNECTABLE_SSID_NETWORK_BUCKET), a(1));
+ scan.clear();
+ int lotsOfBssids = Math.max(WifiMetrics.MAX_TOTAL_SCAN_RESULTS_BUCKET,
+ WifiMetrics.MAX_CONNECTABLE_BSSID_NETWORK_BUCKET) + 5;
+ for (int i = 0; i < lotsOfBssids; i++) {
+ scan.add(buildMockScanDetail("SSID", "bssid_" + i, true, true, false, false));
+ }
+ mWifiMetrics.incrementAvailableNetworksHistograms(scan, true);
+ dumpProtoAndDeserialize();
+ verifyHist(mDecodedProto.totalBssidsInScanHistogram, 1,
+ a(WifiMetrics.MAX_TOTAL_SCAN_RESULTS_BUCKET), a(1));
+ verifyHist(mDecodedProto.availableOpenBssidsInScanHistogram, 1,
+ a(WifiMetrics.MAX_CONNECTABLE_BSSID_NETWORK_BUCKET), a(1));
+ verifyHist(mDecodedProto.availableSavedBssidsInScanHistogram, 1,
+ a(WifiMetrics.MAX_CONNECTABLE_BSSID_NETWORK_BUCKET), a(1));
+ verifyHist(mDecodedProto.availableOpenOrSavedBssidsInScanHistogram, 1,
+ a(WifiMetrics.MAX_CONNECTABLE_BSSID_NETWORK_BUCKET), a(1));
+ }
+
+ /** short hand for instantiating an anonymous int array, instead of 'new int[]{a1, a2, ...}' */
+ private int[] a(int... element) {
+ return element;
+ }
+
+ private void verifyHist(WifiMetricsProto.NumConnectableNetworksBucket[] hist, int size,
+ int[] keys, int[] counts) throws Exception {
+ assertEquals(size, hist.length);
+ for (int i = 0; i < keys.length; i++) {
+ assertEquals(keys[i], hist[i].numConnectableNetworks);
+ assertEquals(counts[i], hist[i].count);
+ }
+ }
+
+ /**
* Generate an RSSI delta event by creating a connection event and an RSSI poll within
* 'interArrivalTime' milliseconds of each other.
* Event will not be logged if interArrivalTime > mWifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS
@@ -1081,6 +1227,7 @@
}
mWifiMetrics.incrementRssiPollRssiCount(scanRssi + rssiDelta);
}
+
/**
* Generate an RSSI delta event, with all extra conditions set to true.
*/
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java
index 2f13baf..32d1daa 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java
@@ -31,6 +31,7 @@
import android.net.wifi.IClientInterface;
import android.net.wifi.WifiConfiguration;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
import org.junit.Before;
import org.junit.Test;
@@ -487,8 +488,9 @@
IClientInterface clientInterface = mock(IClientInterface.class);
when(mWificondControl.setupDriverForClientMode()).thenReturn(clientInterface);
- IClientInterface returnedClientInterface = mWifiNative.setupForClientMode();
- assertEquals(clientInterface, returnedClientInterface);
+ Pair<Integer, IClientInterface> statusAndClientInterface = mWifiNative.setupForClientMode();
+ assertTrue(WifiNative.SETUP_SUCCESS == statusAndClientInterface.first);
+ assertEquals(clientInterface, statusAndClientInterface.second);
verify(mWifiVendorHal).startVendorHal(eq(true));
verify(mWificondControl).setupDriverForClientMode();
}
@@ -503,8 +505,9 @@
IClientInterface clientInterface = mock(IClientInterface.class);
when(mWificondControl.setupDriverForClientMode()).thenReturn(clientInterface);
- IClientInterface returnedClientInterface = mWifiNative.setupForClientMode();
- assertEquals(clientInterface, returnedClientInterface);
+ Pair<Integer, IClientInterface> statusAndClientInterface = mWifiNative.setupForClientMode();
+ assertTrue(WifiNative.SETUP_SUCCESS == statusAndClientInterface.first);
+ assertEquals(clientInterface, statusAndClientInterface.second);
verify(mWifiVendorHal, never()).startVendorHal(anyBoolean());
verify(mWificondControl).setupDriverForClientMode();
}
@@ -517,8 +520,9 @@
public void testSetupDriverForClientModeWificondError() {
when(mWificondControl.setupDriverForClientMode()).thenReturn(null);
- IClientInterface returnedClientInterface = mWifiNative.setupForClientMode();
- assertEquals(null, returnedClientInterface);
+ Pair<Integer, IClientInterface> statusAndClientInterface = mWifiNative.setupForClientMode();
+ assertTrue(WifiNative.SETUP_FAILURE_WIFICOND == statusAndClientInterface.first);
+ assertEquals(null, statusAndClientInterface.second);
verify(mWifiVendorHal).startVendorHal(eq(true));
verify(mWificondControl).setupDriverForClientMode();
}
@@ -530,8 +534,9 @@
public void testSetupDriverForClientModeHalError() {
when(mWifiVendorHal.startVendorHal(anyBoolean())).thenReturn(false);
- IClientInterface returnedClientInterface = mWifiNative.setupForClientMode();
- assertEquals(null, returnedClientInterface);
+ Pair<Integer, IClientInterface> statusAndClientInterface = mWifiNative.setupForClientMode();
+ assertTrue(WifiNative.SETUP_FAILURE_HAL == statusAndClientInterface.first);
+ assertEquals(null, statusAndClientInterface.second);
verify(mWifiVendorHal).startVendorHal(eq(true));
verify(mWificondControl, never()).setupDriverForClientMode();
}
@@ -544,8 +549,9 @@
IApInterface apInterface = mock(IApInterface.class);
when(mWificondControl.setupDriverForSoftApMode()).thenReturn(apInterface);
- IApInterface returnedApInterface = mWifiNative.setupForSoftApMode();
- assertEquals(apInterface, returnedApInterface);
+ Pair<Integer, IApInterface> statusAndApInterface = mWifiNative.setupForSoftApMode();
+ assertTrue(WifiNative.SETUP_SUCCESS == statusAndApInterface.first);
+ assertEquals(apInterface, statusAndApInterface.second);
verify(mWifiVendorHal).startVendorHal(eq(false));
verify(mWificondControl).setupDriverForSoftApMode();
}
@@ -560,8 +566,9 @@
IApInterface apInterface = mock(IApInterface.class);
when(mWificondControl.setupDriverForSoftApMode()).thenReturn(apInterface);
- IApInterface returnedApInterface = mWifiNative.setupForSoftApMode();
- assertEquals(apInterface, returnedApInterface);
+ Pair<Integer, IApInterface> statusAndApInterface = mWifiNative.setupForSoftApMode();
+ assertTrue(WifiNative.SETUP_SUCCESS == statusAndApInterface.first);
+ assertEquals(apInterface, statusAndApInterface.second);
verify(mWifiVendorHal, never()).startVendorHal(anyBoolean());
verify(mWificondControl).setupDriverForSoftApMode();
}
@@ -573,9 +580,11 @@
@Test
public void testSetupDriverForSoftApModeWificondError() {
when(mWificondControl.setupDriverForSoftApMode()).thenReturn(null);
- IApInterface returnedApInterface = mWifiNative.setupForSoftApMode();
- assertEquals(null, returnedApInterface);
+ Pair<Integer, IApInterface> statusAndApInterface = mWifiNative.setupForSoftApMode();
+ assertTrue(WifiNative.SETUP_FAILURE_WIFICOND == statusAndApInterface.first);
+ assertEquals(null, statusAndApInterface.second);
+
verify(mWifiVendorHal).startVendorHal(eq(false));
verify(mWificondControl).setupDriverForSoftApMode();
}
@@ -587,8 +596,10 @@
public void testSetupDriverForSoftApModeHalError() {
when(mWifiVendorHal.startVendorHal(anyBoolean())).thenReturn(false);
- IApInterface returnedApInterface = mWifiNative.setupForSoftApMode();
- assertEquals(null, returnedApInterface);
+ Pair<Integer, IApInterface> statusAndApInterface = mWifiNative.setupForSoftApMode();
+ assertTrue(WifiNative.SETUP_FAILURE_HAL == statusAndApInterface.first);
+ assertEquals(null, statusAndApInterface.second);
+
verify(mWifiVendorHal).startVendorHal(eq(false));
verify(mWificondControl, never()).setupDriverForSoftApMode();
}
@@ -616,13 +627,27 @@
}
/**
- * Verifies that tearDownInterfaces() calls underlying WificondControl.
+ * Verifies that tearDownInterfaces() calls underlying WificondControl and WifiVendorHal
+ * methods.
*/
@Test
public void testTearDown() {
when(mWificondControl.tearDownInterfaces()).thenReturn(true);
- assertTrue(mWifiNative.tearDown());
+ mWifiNative.tearDown();
+ verify(mWificondControl).tearDownInterfaces();
+ verify(mWifiVendorHal).stopVendorHal();
+ }
+
+ /**
+ * Verifies that tearDownInterfaces() calls underlying WificondControl and WifiVendorHal
+ * methods even if wificond returns an error.
+ */
+ @Test
+ public void testTearDownWificondError() {
+ when(mWificondControl.tearDownInterfaces()).thenReturn(false);
+
+ mWifiNative.tearDown();
verify(mWificondControl).tearDownInterfaces();
verify(mWifiVendorHal).stopVendorHal();
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
index 5eae11e..4965a35 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
@@ -42,6 +42,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -64,6 +65,7 @@
mWifiNetworkSelector = new WifiNetworkSelector(mContext, mWifiConfigManager, mClock,
mLocalLog);
mWifiNetworkSelector.registerNetworkEvaluator(mDummyEvaluator, 1);
+ mDummyEvaluator.setEvaluatorToSelectCandidate(true);
when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime());
mThresholdMinimumRssi2G = mResource.getInteger(
@@ -74,6 +76,10 @@
R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz);
mThresholdQualifiedRssi5G = mResource.getInteger(
R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz);
+ mStayOnNetworkMinimumTxRate = mResource.getInteger(
+ R.integer.config_wifi_framework_min_tx_rate_for_staying_on_network);
+ mStayOnNetworkMinimumRxRate = mResource.getInteger(
+ R.integer.config_wifi_framework_min_rx_rate_for_staying_on_network);
mThresholdSaturatedRssi2G = mResource.getInteger(
R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz);
mThresholdSaturatedRssi5G = mResource.getInteger(
@@ -93,6 +99,8 @@
public class DummyNetworkEvaluator implements WifiNetworkSelector.NetworkEvaluator {
private static final String NAME = "DummyNetworkEvaluator";
+ private boolean mEvaluatorShouldSelectCandidate = true;
+
@Override
public String getName() {
return NAME;
@@ -102,20 +110,34 @@
public void update(List<ScanDetail> scanDetails) {}
/**
- * Always return the first network in the scan results for connection.
+ * Sets whether the evaluator should return a candidate for connection or null.
+ */
+ public void setEvaluatorToSelectCandidate(boolean shouldSelectCandidate) {
+ mEvaluatorShouldSelectCandidate = shouldSelectCandidate;
+ }
+
+ /**
+ * This NetworkEvaluator can be configured to return a candidate or null. If returning a
+ * candidate, the first entry in the provided scanDetails will be selected. This requires
+ * that the mock WifiConfigManager be set up to return a WifiConfiguration for the first
+ * scanDetail entry, through
+ * {@link WifiNetworkSelectorTestUtil#setupScanDetailsAndConfigStore}.
*/
@Override
public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails,
WifiConfiguration currentNetwork, String currentBssid, boolean connected,
boolean untrustedNetworkAllowed,
List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks) {
+ if (!mEvaluatorShouldSelectCandidate) {
+ return null;
+ }
ScanDetail scanDetail = scanDetails.get(0);
mWifiConfigManager.setNetworkCandidateScanResult(0, scanDetail.getScanResult(), 100);
assertNotNull("Saved network must not be null",
- mWifiConfigManager.getSavedNetworkForScanDetailAndCache(scanDetail));
+ mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail));
- return mWifiConfigManager.getSavedNetworkForScanDetailAndCache(scanDetail);
+ return mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail);
}
}
@@ -131,6 +153,8 @@
private int mThresholdMinimumRssi5G;
private int mThresholdQualifiedRssi2G;
private int mThresholdQualifiedRssi5G;
+ private int mStayOnNetworkMinimumTxRate;
+ private int mStayOnNetworkMinimumRxRate;
private int mThresholdSaturatedRssi2G;
private int mThresholdSaturatedRssi5G;
@@ -154,6 +178,12 @@
R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz))
.thenReturn(-85);
when(mResource.getInteger(
+ R.integer.config_wifi_framework_max_tx_rate_for_full_scan))
+ .thenReturn(8);
+ when(mResource.getInteger(
+ R.integer.config_wifi_framework_max_rx_rate_for_full_scan))
+ .thenReturn(8);
+ when(mResource.getInteger(
R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz))
.thenReturn(-57);
when(mResource.getInteger(
@@ -200,6 +230,7 @@
WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
blacklist, mWifiInfo, false, true, false);
assertEquals("Expect null configuration", null, candidate);
+ assertTrue(mWifiNetworkSelector.getConnectableScanDetails().isEmpty());
}
@@ -229,6 +260,7 @@
WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
blacklist, mWifiInfo, false, true, false);
assertEquals("Expect null configuration", null, candidate);
+ assertTrue(mWifiNetworkSelector.getConnectableScanDetails().isEmpty());
}
/**
@@ -267,6 +299,7 @@
blacklist, mWifiInfo, true, false, false);
assertEquals("Expect null configuration", null, candidate);
+ assertTrue(mWifiNetworkSelector.getConnectableScanDetails().isEmpty());
}
/**
@@ -431,6 +464,7 @@
WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
blacklist, mWifiInfo, false, true, false);
assertEquals("Expect null configuration", null, candidate);
+ assertTrue(mWifiNetworkSelector.getConnectableScanDetails().isEmpty());
}
/**
@@ -483,6 +517,7 @@
// The second network selection is skipped since current connected network is
// missing from the scan results.
assertEquals("Expect null configuration", null, candidate);
+ assertTrue(mWifiNetworkSelector.getConnectableScanDetails().isEmpty());
}
/**
@@ -592,9 +627,19 @@
*/
@Test
public void test2GhzQualifiedNo5GhzAvailable() {
+ // Rssi after connected.
+ when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
+ // No streaming traffic.
+ when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
+ when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+
// Do not perform selection on 2GHz if current network is good and no 5GHz available
- testStayOrSwitch(mThresholdQualifiedRssi2G, false,
- mThresholdQualifiedRssi2G + 10, false, false);
+ testStayOrTryToSwitch(
+ mThresholdQualifiedRssi2G + 1 /* rssi before connected */,
+ false /* not a 5G network */,
+ false /* not open network */,
+ // Should not try to switch.
+ false);
}
/**
@@ -609,24 +654,24 @@
*/
@Test
public void test2GhzHighQuality5GhzAvailable() {
- // When on 2GHz, even with "good" signal strength, run selection if 5GHz available
- testStayOrSwitch(mThresholdQualifiedRssi2G, false,
- mThresholdQualifiedRssi5G - 1, true, true);
- }
+ // Rssi after connected.
+ when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
+ // No streaming traffic.
+ when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
+ when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
- /**
- * Wifi network selector performs network selection on 2Ghz networks if the current network
- * is not of high quality.
- *
- * WifiStateMachine is under connected state and 2.4GHz test1 is connected.
- *
- * Expected behavior: network selection is performed
- */
- @Test
- public void test2GhzNotQualifiedOther2GhzAvailable() {
- // Run Selection on 2Ghz networks when not qualified even if no 5GHz available
- testStayOrSwitch(mThresholdQualifiedRssi2G - 1, false,
- mThresholdQualifiedRssi2G - 10, false, true);
+ // When on 2GHz, even with "good" signal strength, run selection if 5GHz available
+ testStayOrTryToSwitch(
+ // Parameters for network1:
+ mThresholdQualifiedRssi2G + 1 /* rssi before connected */,
+ false /* not a 5G network */,
+ false /* not open network */,
+ // Parameters for network2:
+ mThresholdQualifiedRssi5G + 1 /* rssi */,
+ true /* a 5G network */,
+ false /* not open network */,
+ // Should try to switch.
+ true);
}
/**
@@ -639,10 +684,19 @@
*/
@Test
public void test5GhzNotQualifiedLowRssi() {
- // Run Selection when the current 5Ghz network has low RSSI, regardless of what may
- // be available. The second scan result is irrelevant.
- testStayOrSwitch(mThresholdQualifiedRssi5G - 1, true,
- mThresholdQualifiedRssi2G - 1, false, true);
+ // Rssi after connected.
+ when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G - 1);
+ // No streaming traffic.
+ when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
+ when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+
+ // Run Selection when the current 5Ghz network has low RSSI.
+ testStayOrTryToSwitch(
+ mThresholdQualifiedRssi5G + 1 /* rssi before connected */,
+ true /* a 5G network */,
+ false /* not open network */,
+ // Should try to switch.
+ true);
}
/**
@@ -655,9 +709,162 @@
*/
@Test
public void test5GhzQualified() {
+ // Rssi after connected.
+ when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G + 1);
+ // No streaming traffic.
+ when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
+ when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+
// Connected to a high quality 5Ghz network, so the other result is irrelevant
- testStayOrSwitch(mThresholdQualifiedRssi5G, true,
- mThresholdQualifiedRssi5G + 10, true, false);
+ testStayOrTryToSwitch(
+ mThresholdQualifiedRssi5G + 1 /* rssi before connected */,
+ true /* a 5G network */,
+ false /* not open network */,
+ // Should not try to switch.
+ false);
+ }
+
+ /**
+ * New network selection is performed if the currently connected network
+ * band is 2G and there is no sign of streaming traffic.
+ *
+ * Expected behavior: Network Selector perform network selection after connected
+ * to the first one.
+ */
+ @Test
+ public void band2GNetworkIsNotSufficientWhenNoOngoingTrafficAnd5GhzAvailable() {
+ // Rssi after connected.
+ when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
+ // No streaming traffic.
+ when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
+ when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+
+ testStayOrTryToSwitch(
+ // Parameters for network1:
+ mThresholdQualifiedRssi2G + 1 /* rssi before connected */,
+ false /* not a 5G network */,
+ false /* not open network */,
+ // Parameters for network2:
+ mThresholdQualifiedRssi5G + 1 /* rssi */,
+ true /* a 5G network */,
+ false /* not open network */,
+ // Should try to switch.
+ true);
+ }
+
+ /**
+ * New network selection is performed if the currently connected network
+ * band is 2G with bad rssi.
+ *
+ * Expected behavior: Network Selector perform network selection after connected
+ * to the first one.
+ */
+ @Test
+ public void band2GNetworkIsNotSufficientWithBadRssi() {
+ // Rssi after connected.
+ when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G - 1);
+ // No streaming traffic.
+ when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
+ when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+
+ testStayOrTryToSwitch(
+ mThresholdQualifiedRssi2G + 1 /* rssi before connected */,
+ false /* not a 5G network */,
+ false /* not open network */,
+ // Should try to switch.
+ true);
+ }
+
+ /**
+ * New network selection is not performed if the currently connected 2G network
+ * has good Rssi and sign of streaming tx traffic.
+ *
+ * Expected behavior: Network selector does not perform network selection.
+ */
+ @Test
+ public void band2GNetworkIsSufficientWhenOnGoingTxTrafficCombinedWithGoodRssi() {
+ // Rssi after connected.
+ when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
+ // Streaming traffic
+ when(mWifiInfo.getTxSuccessRatePps()).thenReturn(
+ (double) (mStayOnNetworkMinimumTxRate + 1));
+ when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+
+ testStayOrTryToSwitch(
+ mThresholdQualifiedRssi2G + 1 /* rssi before connected */,
+ false /* not a 5G network */,
+ true /* open network */,
+ // Should not try to switch.
+ false);
+ }
+
+ /**
+ * New network selection is not performed if the currently connected 2G network
+ * has good Rssi and sign of streaming rx traffic.
+ *
+ * Expected behavior: Network selector does not perform network selection.
+ */
+ @Test
+ public void band2GNetworkIsSufficientWhenOnGoingRxTrafficCombinedWithGoodRssi() {
+ // Rssi after connected.
+ when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
+ // Streaming traffic
+ when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
+ when(mWifiInfo.getRxSuccessRatePps()).thenReturn(
+ (double) (mStayOnNetworkMinimumRxRate + 1));
+
+ testStayOrTryToSwitch(
+ mThresholdQualifiedRssi2G + 1 /* rssi before connected */,
+ false /* not a 5G network */,
+ true /* open network */,
+ // Should not try to switch.
+ false);
+ }
+
+ /**
+ * New network selection is not performed if the currently connected 5G network
+ * has good Rssi and sign of streaming tx traffic.
+ *
+ * Expected behavior: Network selector does not perform network selection.
+ */
+ @Test
+ public void band5GNetworkIsSufficientWhenOnGoingTxTrafficCombinedWithGoodRssi() {
+ // Rssi after connected.
+ when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G + 1);
+ // Streaming traffic
+ when(mWifiInfo.getTxSuccessRatePps()).thenReturn(
+ (double) (mStayOnNetworkMinimumTxRate + 1));
+ when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+
+ testStayOrTryToSwitch(
+ mThresholdQualifiedRssi5G + 1 /* rssi before connected */,
+ true /* a 5G network */,
+ true /* open network */,
+ // Should not try to switch.
+ false);
+ }
+
+ /**
+ * New network selection is not performed if the currently connected 5G network
+ * has good Rssi and sign of streaming rx traffic.
+ *
+ * Expected behavior: Network selector does not perform network selection.
+ */
+ @Test
+ public void band5GNetworkIsSufficientWhenOnGoingRxTrafficCombinedWithGoodRssi() {
+ // Rssi after connected.
+ when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G + 1);
+ // Streaming traffic
+ when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
+ when(mWifiInfo.getRxSuccessRatePps()).thenReturn(
+ (double) (mStayOnNetworkMinimumRxRate + 1));
+
+ testStayOrTryToSwitch(
+ mThresholdQualifiedRssi5G + 1 /* rssi before connected */,
+ true /* a 5G network */,
+ true /* open network */,
+ // Should not try to switch.
+ false);
}
/**
@@ -667,45 +874,207 @@
* It sets up two networks, connects to the first, and then ensures that
* both are available in the scan results for the NetworkSelector.
*/
- private void testStayOrSwitch(int levelNetwork1, boolean is5GHzNetwork1,
- int levelNetwork2, boolean is5GHzNetwork2, boolean shouldSelect) {
- // Create an updated ScanDetails that includes a new network
+ private void testStayOrTryToSwitch(
+ int rssiNetwork1, boolean is5GHzNetwork1, boolean isOpenNetwork1,
+ int rssiNetwork2, boolean is5GHzNetwork2, boolean isOpenNetwork2,
+ boolean shouldSelect) {
String[] ssids = {"\"test1\"", "\"test2\""};
String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
- int[] freqs = new int[2];
- freqs[0] = is5GHzNetwork1 ? 5180 : 2437;
- freqs[1] = is5GHzNetwork2 ? 5180 : 2437;
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
- int[] levels = {levelNetwork1, levelNetwork2};
- int[] securities = {SECURITY_PSK, SECURITY_PSK};
+ int[] freqs = {is5GHzNetwork1 ? 5180 : 2437, is5GHzNetwork2 ? 5180 : 2437};
+ String[] caps = {isOpenNetwork1 ? "[ESS]" : "[WPA2-EAP-CCMP][ESS]",
+ isOpenNetwork2 ? "[ESS]" : "[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {rssiNetwork1, rssiNetwork2};
+ int[] securities = {isOpenNetwork1 ? SECURITY_NONE : SECURITY_PSK,
+ isOpenNetwork2 ? SECURITY_NONE : SECURITY_PSK};
+ testStayOrTryToSwitchImpl(ssids, bssids, freqs, caps, levels, securities, shouldSelect);
+ }
+ /**
+ * This is a meta-test that given one scan results, will
+ * determine whether or not network selection should be performed.
+ *
+ * It sets up two networks, connects to the first, and then ensures that
+ * the scan results for the NetworkSelector.
+ */
+ private void testStayOrTryToSwitch(
+ int rssi, boolean is5GHz, boolean isOpenNetwork,
+ boolean shouldSelect) {
+ String[] ssids = {"\"test1\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3"};
+ int[] freqs = {is5GHz ? 5180 : 2437};
+ String[] caps = {isOpenNetwork ? "[ESS]" : "[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {rssi};
+ int[] securities = {isOpenNetwork ? SECURITY_NONE : SECURITY_PSK};
+ testStayOrTryToSwitchImpl(ssids, bssids, freqs, caps, levels, securities, shouldSelect);
+ }
+
+ private void testStayOrTryToSwitchImpl(String[] ssids, String[] bssids, int[] freqs,
+ String[] caps, int[] levels, int[] securities,
+ boolean shouldSelect) {
// Make a network selection to connect to test1.
ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
- freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
HashSet<String> blacklist = new HashSet<String>();
- WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
- blacklist, mWifiInfo, false, true, false);
+ // DummyNetworkEvaluator always return the first network in the scan results
+ // for connection, so this should connect to the first network.
+ WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(
+ scanDetails,
+ blacklist, mWifiInfo, false, true, true);
+ assertNotNull("Result should be not null", candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ scanDetails.get(0).getScanResult(), candidate);
when(mWifiInfo.getNetworkId()).thenReturn(0);
when(mWifiInfo.getBSSID()).thenReturn(bssids[0]);
- when(mWifiInfo.is24GHz()).thenReturn(!is5GHzNetwork1);
- when(mWifiInfo.is5GHz()).thenReturn(is5GHzNetwork1);
- when(mWifiInfo.getRssi()).thenReturn(levels[0]);
+ when(mWifiInfo.is24GHz()).thenReturn(!ScanResult.is5GHz(freqs[0]));
+ when(mWifiInfo.is5GHz()).thenReturn(ScanResult.is5GHz(freqs[0]));
+
when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
+ WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000);
candidate = mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo,
true, false, false);
- if (!shouldSelect) {
- assertEquals("Expect null configuration", null, candidate);
- } else {
+ // DummyNetworkEvaluator always return the first network in the scan results
+ // for connection, so if nework selection is performed, the first network should
+ // be returned as candidate.
+ if (shouldSelect) {
assertNotNull("Result should be not null", candidate);
- ScanResult expectedResult = scanDetails.get(0).getScanResult();
WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
- expectedResult, candidate);
+ scanDetails.get(0).getScanResult(), candidate);
+ } else {
+ assertEquals("Expect null configuration", null, candidate);
}
}
+
+ /**
+ * {@link WifiNetworkSelector#getFilteredScanDetailsForOpenUnsavedNetworks()} should filter out
+ * networks that are not open after network selection is made.
+ *
+ * Expected behavior: return open networks only
+ */
+ @Test
+ public void getfilterOpenUnsavedNetworks_filtersForOpenNetworks() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2437, 5180};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"};
+ int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1};
+ mDummyEvaluator.setEvaluatorToSelectCandidate(false);
+
+ List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails(
+ ssids, bssids, freqs, caps, levels, mClock);
+ HashSet<String> blacklist = new HashSet<>();
+
+ mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false);
+ List<ScanDetail> expectedOpenUnsavedNetworks = new ArrayList<>();
+ expectedOpenUnsavedNetworks.add(scanDetails.get(1));
+ assertEquals("Expect open unsaved networks",
+ expectedOpenUnsavedNetworks,
+ mWifiNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks());
+ }
+
+ /**
+ * {@link WifiNetworkSelector#getFilteredScanDetailsForOpenUnsavedNetworks()} should filter out
+ * saved networks after network selection is made. This should return an empty list when there
+ * are no unsaved networks available.
+ *
+ * Expected behavior: return unsaved networks only. Return empty list if there are no unsaved
+ * networks.
+ */
+ @Test
+ public void getfilterOpenUnsavedNetworks_filtersOutSavedNetworks() {
+ String[] ssids = {"\"test1\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3"};
+ int[] freqs = {2437, 5180};
+ String[] caps = {"[ESS]"};
+ int[] levels = {mThresholdMinimumRssi2G + 1};
+ int[] securities = {SECURITY_NONE};
+ mDummyEvaluator.setEvaluatorToSelectCandidate(false);
+
+ List<ScanDetail> unSavedScanDetails = WifiNetworkSelectorTestUtil.buildScanDetails(
+ ssids, bssids, freqs, caps, levels, mClock);
+ HashSet<String> blacklist = new HashSet<>();
+
+ mWifiNetworkSelector.selectNetwork(
+ unSavedScanDetails, blacklist, mWifiInfo, false, true, false);
+ assertEquals("Expect open unsaved networks",
+ unSavedScanDetails,
+ mWifiNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks());
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> savedScanDetails = scanDetailsAndConfigs.getScanDetails();
+
+ mWifiNetworkSelector.selectNetwork(
+ savedScanDetails, blacklist, mWifiInfo, false, true, false);
+ // Saved networks are filtered out.
+ assertTrue(mWifiNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks().isEmpty());
+ }
+
+ /**
+ * {@link WifiNetworkSelector#getFilteredScanDetailsForOpenUnsavedNetworks()} should filter out
+ * bssid blacklisted networks.
+ *
+ * Expected behavior: do not return blacklisted network
+ */
+ @Test
+ public void getfilterOpenUnsavedNetworks_filtersOutBlacklistedNetworks() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2437, 5180};
+ String[] caps = {"[ESS]", "[ESS]"};
+ int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1};
+ mDummyEvaluator.setEvaluatorToSelectCandidate(false);
+
+ List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails(
+ ssids, bssids, freqs, caps, levels, mClock);
+ HashSet<String> blacklist = new HashSet<>();
+ blacklist.add(bssids[0]);
+
+ mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false);
+ List<ScanDetail> expectedOpenUnsavedNetworks = new ArrayList<>();
+ expectedOpenUnsavedNetworks.add(scanDetails.get(1));
+ assertEquals("Expect open unsaved networks",
+ expectedOpenUnsavedNetworks,
+ mWifiNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks());
+ }
+
+ /**
+ * {@link WifiNetworkSelector#getFilteredScanDetailsForOpenUnsavedNetworks()} should return
+ * empty list when there are no open networks after network selection is made.
+ *
+ * Expected behavior: return empty list
+ */
+ @Test
+ public void getfilterOpenUnsavedNetworks_returnsEmptyListWhenNoOpenNetworksPresent() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2437, 5180};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1};
+ mDummyEvaluator.setEvaluatorToSelectCandidate(false);
+
+ List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails(
+ ssids, bssids, freqs, caps, levels, mClock);
+ HashSet<String> blacklist = new HashSet<>();
+
+ mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false);
+ assertTrue(mWifiNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks().isEmpty());
+ }
+
+ /**
+ * {@link WifiNetworkSelector#getFilteredScanDetailsForOpenUnsavedNetworks()} should return
+ * empty list when no network selection has been made.
+ *
+ * Expected behavior: return empty list
+ */
+ @Test
+ public void getfilterOpenUnsavedNetworks_returnsEmptyListWhenNoNetworkSelectionMade() {
+ assertTrue(mWifiNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks().isEmpty());
+ }
}
+
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTestUtil.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTestUtil.java
index 9c78c9b..66507f5 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTestUtil.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTestUtil.java
@@ -292,19 +292,19 @@
if (scanDetails.size() <= configs.length) {
for (int i = 0; i < scanDetails.size(); i++) {
ScanDetail scanDetail = scanDetails.get(i);
- when(wifiConfigManager.getSavedNetworkForScanDetailAndCache(eq(scanDetail)))
+ when(wifiConfigManager.getConfiguredNetworkForScanDetailAndCache(eq(scanDetail)))
.thenReturn(configs[i]);
}
} else {
for (int i = 0; i < configs.length; i++) {
ScanDetail scanDetail = scanDetails.get(i);
- when(wifiConfigManager.getSavedNetworkForScanDetailAndCache(eq(scanDetail)))
+ when(wifiConfigManager.getConfiguredNetworkForScanDetailAndCache(eq(scanDetail)))
.thenReturn(configs[i]);
}
// associated the remaining scan details with a NULL config.
for (int i = configs.length; i < scanDetails.size(); i++) {
- when(wifiConfigManager.getSavedNetworkForScanDetailAndCache(
+ when(wifiConfigManager.getConfiguredNetworkForScanDetailAndCache(
eq(scanDetails.get(i)))).thenReturn(null);
}
}
@@ -370,7 +370,7 @@
config.networkId = networkId;
config.meteredHint = meteredHint;
- when(wifiConfigManager.getSavedNetworkForScanDetailAndCache(eq(scanDetail)))
+ when(wifiConfigManager.getConfiguredNetworkForScanDetailAndCache(eq(scanDetail)))
.thenReturn(new WifiConfiguration(config));
when(wifiConfigManager.getConfiguredNetwork(eq(networkId)))
.then(new AnswerWithArguments() {
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNotificationControllerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNotificationControllerTest.java
index f7b3bf6..27055a8 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNotificationControllerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNotificationControllerTest.java
@@ -16,7 +16,7 @@
package com.android.server.wifi;
-import static org.mockito.Mockito.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -25,23 +25,16 @@
import android.app.Notification;
import android.app.NotificationManager;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.IntentFilter;
import android.content.res.Resources;
-import android.net.NetworkInfo;
import android.net.wifi.ScanResult;
-import android.net.wifi.WifiManager;
-import android.net.wifi.WifiScanner;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.test.TestLooper;
import android.provider.Settings;
-import android.test.suitebuilder.annotation.SmallTest;
import org.junit.Before;
import org.junit.Test;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -49,26 +42,17 @@
import java.util.List;
/**
- * Unit tests for {@link com.android.server.wifi.WifiScanningServiceImpl}.
+ * Unit tests for {@link WifiNotificationController}.
*/
-@SmallTest
public class WifiNotificationControllerTest {
- public static final String TAG = "WifiScanningServiceTest";
@Mock private Context mContext;
@Mock private Resources mResources;
@Mock private FrameworkFacade mFrameworkFacade;
@Mock private NotificationManager mNotificationManager;
@Mock private UserManager mUserManager;
- @Mock private WifiInjector mWifiInjector;
- @Mock private WifiScanner mWifiScanner;
- WifiNotificationController mWifiNotificationController;
+ private WifiNotificationController mNotificationController;
- /**
- * Internal BroadcastReceiver that WifiNotificationController uses to listen for broadcasts
- * this is initialized by calling startServiceAndLoadDriver
- */
- BroadcastReceiver mBroadcastReceiver;
/** Initialize objects before each test run. */
@Before
@@ -76,86 +60,149 @@
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(Context.NOTIFICATION_SERVICE))
.thenReturn(mNotificationManager);
-
when(mFrameworkFacade.getIntegerSetting(mContext,
Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1)).thenReturn(1);
-
when(mContext.getSystemService(Context.USER_SERVICE))
.thenReturn(mUserManager);
when(mContext.getResources()).thenReturn(mResources);
- when(mWifiInjector.getWifiScanner()).thenReturn(mWifiScanner);
-
TestLooper mock_looper = new TestLooper();
- mWifiNotificationController = new WifiNotificationController(
+ mNotificationController = new WifiNotificationController(
mContext, mock_looper.getLooper(), mFrameworkFacade,
- mock(Notification.Builder.class), mWifiInjector);
- ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
- ArgumentCaptor.forClass(BroadcastReceiver.class);
-
- verify(mContext)
- .registerReceiver(broadcastReceiverCaptor.capture(), any(IntentFilter.class));
- mBroadcastReceiver = broadcastReceiverCaptor.getValue();
+ mock(Notification.Builder.class));
+ mNotificationController.handleScreenStateChanged(true);
}
- private void setOpenAccessPoint() {
- List<ScanResult> scanResults = new ArrayList<>();
+ private List<ScanDetail> createOpenScanResults() {
+ List<ScanDetail> scanResults = new ArrayList<>();
ScanResult scanResult = new ScanResult();
scanResult.capabilities = "[ESS]";
- scanResults.add(scanResult);
- when(mWifiScanner.getSingleScanResults()).thenReturn(scanResults);
+ scanResults.add(new ScanDetail(scanResult, null /* networkDetail */));
+ return scanResults;
}
- /** Verifies that a notification is displayed (and retracted) given system events. */
+ /**
+ * When scan results with open networks are handled, a notification is posted.
+ */
@Test
- public void verifyNotificationDisplayed() throws Exception {
- TestUtil.sendWifiStateChanged(mBroadcastReceiver, mContext, WifiManager.WIFI_STATE_ENABLED);
- TestUtil.sendNetworkStateChanged(mBroadcastReceiver, mContext,
- NetworkInfo.DetailedState.DISCONNECTED);
- setOpenAccessPoint();
+ public void handleScanResults_hasOpenNetworks_notificationDisplayed() {
+ mNotificationController.handleScanResults(createOpenScanResults());
- // The notification should not be displayed after only two scan results.
- TestUtil.sendScanResultsAvailable(mBroadcastReceiver, mContext);
- TestUtil.sendScanResultsAvailable(mBroadcastReceiver, mContext);
- verify(mNotificationManager, never())
- .notifyAsUser(any(), anyInt(), any(), any(UserHandle.class));
-
- // Changing to and from "SCANNING" state should not affect the counter.
- TestUtil.sendNetworkStateChanged(mBroadcastReceiver, mContext,
- NetworkInfo.DetailedState.SCANNING);
- TestUtil.sendNetworkStateChanged(mBroadcastReceiver, mContext,
- NetworkInfo.DetailedState.DISCONNECTED);
-
- // The third scan result notification will trigger the notification.
- TestUtil.sendScanResultsAvailable(mBroadcastReceiver, mContext);
- verify(mNotificationManager)
- .notifyAsUser(any(), anyInt(), any(), any(UserHandle.class));
- verify(mNotificationManager, never())
- .cancelAsUser(any(), anyInt(), any(UserHandle.class));
-
- // Changing network state should cause the notification to go away.
- TestUtil.sendNetworkStateChanged(mBroadcastReceiver, mContext,
- NetworkInfo.DetailedState.CONNECTED);
- verify(mNotificationManager)
- .cancelAsUser(any(), anyInt(), any(UserHandle.class));
+ verify(mNotificationManager).notifyAsUser(any(), anyInt(), any(), any());
}
+ /**
+ * When scan results with no open networks are handled, a notification is not posted.
+ */
@Test
- public void verifyNotificationNotDisplayed_userHasDisallowConfigWifiRestriction() {
+ public void handleScanResults_emptyList_notificationNotDisplayed() {
+ mNotificationController.handleScanResults(new ArrayList<>());
+
+ verify(mNotificationManager, never()).notifyAsUser(any(), anyInt(), any(), any());
+ }
+
+ /**
+ * When a notification is showing and scan results with no open networks are handled, the
+ * notification is cleared.
+ */
+ @Test
+ public void handleScanResults_notificationShown_emptyList_notificationCleared() {
+ mNotificationController.handleScanResults(createOpenScanResults());
+
+ verify(mNotificationManager).notifyAsUser(any(), anyInt(), any(), any());
+
+ mNotificationController.handleScanResults(new ArrayList<>());
+
+ verify(mNotificationManager).cancelAsUser(any(), anyInt(), any());
+ }
+ /**
+ * When a notification is showing, screen is off, and scan results with no open networks are
+ * handled, the notification is cleared.
+ */
+ @Test
+ public void handleScanResults_notificationShown_screenOff_emptyList_notificationCleared() {
+ mNotificationController.handleScanResults(createOpenScanResults());
+
+ verify(mNotificationManager).notifyAsUser(any(), anyInt(), any(), any());
+
+ mNotificationController.handleScreenStateChanged(false);
+ mNotificationController.handleScanResults(new ArrayList<>());
+
+ verify(mNotificationManager).cancelAsUser(any(), anyInt(), any());
+ }
+
+ /**
+ * If notification is showing, do not post another notification.
+ */
+ @Test
+ public void handleScanResults_notificationShowing_doesNotRepostNotification() {
+ mNotificationController.handleScanResults(createOpenScanResults());
+ mNotificationController.handleScanResults(createOpenScanResults());
+
+ verify(mNotificationManager).notifyAsUser(any(), anyInt(), any(), any());
+ }
+
+ /**
+ * When {@link WifiNotificationController#clearPendingNotification(boolean)} is called and a
+ * notification is shown, clear the notification.
+ */
+ @Test
+ public void clearPendingNotification_clearsNotificationIfOneIsShowing() {
+ mNotificationController.handleScanResults(createOpenScanResults());
+
+ verify(mNotificationManager).notifyAsUser(any(), anyInt(), any(), any());
+
+ mNotificationController.clearPendingNotification(true);
+
+ verify(mNotificationManager).cancelAsUser(any(), anyInt(), any());
+ }
+
+ /**
+ * When {@link WifiNotificationController#clearPendingNotification(boolean)} is called and a
+ * notification was not previously shown, do not clear the notification.
+ */
+ @Test
+ public void clearPendingNotification_doesNotClearNotificationIfNoneShowing() {
+ mNotificationController.clearPendingNotification(true);
+
+ verify(mNotificationManager, never()).cancelAsUser(any(), anyInt(), any());
+ }
+
+ /**
+ * When screen is off and notification is not displayed, notification is not posted on handling
+ * new scan results with open networks.
+ */
+ @Test
+ public void screenOff_handleScanResults_notificationNotDisplayed() {
+ mNotificationController.handleScreenStateChanged(false);
+ mNotificationController.handleScanResults(createOpenScanResults());
+
+ verify(mNotificationManager, never()).notifyAsUser(any(), anyInt(), any(), any());
+ }
+
+ /** Verifies that {@link UserManager#DISALLOW_CONFIG_WIFI} disables the feature. */
+ @Test
+ public void userHasDisallowConfigWifiRestriction_notificationNotDisplayed() {
when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT))
.thenReturn(true);
- TestUtil.sendWifiStateChanged(mBroadcastReceiver, mContext, WifiManager.WIFI_STATE_ENABLED);
- TestUtil.sendNetworkStateChanged(mBroadcastReceiver, mContext,
- NetworkInfo.DetailedState.DISCONNECTED);
- setOpenAccessPoint();
+ mNotificationController.handleScanResults(createOpenScanResults());
- // The notification should be displayed after three scan results.
- TestUtil.sendScanResultsAvailable(mBroadcastReceiver, mContext);
- TestUtil.sendScanResultsAvailable(mBroadcastReceiver, mContext);
- TestUtil.sendScanResultsAvailable(mBroadcastReceiver, mContext);
+ verify(mNotificationManager, never()).notifyAsUser(any(), anyInt(), any(), any());
+ }
- verify(mNotificationManager, never())
- .notifyAsUser(any(), anyInt(), any(), any(UserHandle.class));
+ /** Verifies that {@link UserManager#DISALLOW_CONFIG_WIFI} clears the showing notification. */
+ @Test
+ public void userHasDisallowConfigWifiRestriction_showingNotificationIsCleared() {
+ mNotificationController.handleScanResults(createOpenScanResults());
+
+ verify(mNotificationManager).notifyAsUser(any(), anyInt(), any(), any());
+
+ when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT))
+ .thenReturn(true);
+
+ mNotificationController.handleScanResults(createOpenScanResults());
+
+ verify(mNotificationManager).cancelAsUser(any(), anyInt(), any());
}
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java b/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java
index 41f14dd..24d3afa 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java
@@ -17,8 +17,10 @@
package com.android.server.wifi;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -36,6 +38,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.PrintWriter;
import java.util.Arrays;
/**
@@ -49,11 +52,13 @@
WifiScoreReport mWifiScoreReport;
ScanDetailCache mScanDetailCache;
WifiInfo mWifiInfo;
+ int mAggr; // Aggressive handover
@Mock Context mContext;
@Mock NetworkAgent mNetworkAgent;
@Mock Resources mResources;
@Mock WifiConfigManager mWifiConfigManager;
@Mock WifiMetrics mWifiMetrics;
+ @Mock PrintWriter mPrintWriter;
/**
* Sets up resource values for testing
@@ -106,6 +111,7 @@
config.hiddenSSID = false;
mWifiInfo = new WifiInfo();
mWifiInfo.setFrequency(2412);
+ mAggr = 0;
when(mWifiConfigManager.getSavedNetworks()).thenReturn(Arrays.asList(config));
when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(config);
mWifiConfiguration = config;
@@ -116,7 +122,7 @@
when(mWifiConfigManager.getScanDetailCacheForNetwork(anyInt()))
.thenReturn(mScanDetailCache);
when(mContext.getResources()).thenReturn(mResources);
- mWifiScoreReport = new WifiScoreReport(mContext, mWifiConfigManager);
+ mWifiScoreReport = new WifiScoreReport(mContext, mWifiConfigManager, new Clock());
}
/**
@@ -138,10 +144,9 @@
*/
@Test
public void calculateAndReportScoreSucceeds() throws Exception {
- int aggressiveHandover = 0;
mWifiInfo.setRssi(-77);
mWifiScoreReport.calculateAndReportScore(mWifiInfo,
- mNetworkAgent, aggressiveHandover, mWifiMetrics);
+ mNetworkAgent, mAggr, mWifiMetrics);
verify(mNetworkAgent).sendNetworkScore(anyInt());
verify(mWifiMetrics).incrementWifiScoreCount(anyInt());
}
@@ -155,7 +160,7 @@
public void networkAgentMayBeNull() throws Exception {
mWifiInfo.setRssi(-33);
mWifiScoreReport.enableVerboseLogging(true);
- mWifiScoreReport.calculateAndReportScore(mWifiInfo, null, 0, mWifiMetrics);
+ mWifiScoreReport.calculateAndReportScore(mWifiInfo, null, mAggr, mWifiMetrics);
verify(mWifiMetrics).incrementWifiScoreCount(anyInt());
}
@@ -174,7 +179,7 @@
mWifiInfo.txSuccessRate = 5.1; // proportional to pps
mWifiInfo.rxSuccessRate = 5.1;
for (int i = 0; i < 10; i++) {
- mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, 0, mWifiMetrics);
+ mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mAggr, mWifiMetrics);
}
int score = mWifiInfo.score;
assertTrue(score > CELLULAR_THRESHOLD_SCORE);
@@ -197,10 +202,89 @@
mWifiInfo.txSuccessRate = 0.1;
mWifiInfo.rxSuccessRate = 0.1;
for (int i = 0; i < 10; i++) {
- mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, 0, mWifiMetrics);
+ mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mAggr, mWifiMetrics);
}
int score = mWifiInfo.score;
assertTrue(score < CELLULAR_THRESHOLD_SCORE);
verify(mNetworkAgent, atLeast(1)).sendNetworkScore(score);
}
+
+ /**
+ * Test reporting with aggressive handover
+ */
+ @Test
+ public void calculateAndReportScoreSucceedsAggressively() throws Exception {
+ mAggr = 1;
+ mWifiInfo.setRssi(-77);
+ mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mAggr, mWifiMetrics);
+ verify(mNetworkAgent).sendNetworkScore(anyInt());
+ verify(mWifiMetrics).incrementWifiScoreCount(anyInt());
+ }
+
+ /**
+ * Test low rssi with aggressive handover
+ */
+ @Test
+ public void giveUpOnBadRssiAggressively() throws Exception {
+ mAggr = 1;
+ mWifiInfo.setRssi(-83);
+ mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mAggr, mWifiMetrics);
+ int score = mWifiInfo.score;
+ verify(mNetworkAgent, atLeast(1)).sendNetworkScore(score);
+ assertTrue(score < CELLULAR_THRESHOLD_SCORE);
+ }
+
+ /**
+ * Test high rssi with aggressive handover
+ */
+ @Test
+ public void allowGoodRssiAggressively() throws Exception {
+ mAggr = 1;
+ mWifiInfo.setRssi(-65);
+ mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mAggr, mWifiMetrics);
+ int score = mWifiInfo.score;
+ verify(mNetworkAgent, atLeast(1)).sendNetworkScore(score);
+ assertTrue(score > CELLULAR_THRESHOLD_SCORE);
+ }
+
+ /**
+ * Test data logging
+ */
+ @Test
+ public void testDataLogging() throws Exception {
+ mAggr = 1;
+ for (int i = 0; i < 10; i++) {
+ mWifiInfo.setRssi(-65 + i);
+ mWifiInfo.setLinkSpeed(300);
+ mWifiInfo.setFrequency(5220);
+ mWifiInfo.txSuccessRate = 0.1 + i;
+ mWifiInfo.txRetriesRate = 0.2 + i;
+ mWifiInfo.txBadRate = 0.01 * i;
+ mWifiInfo.rxSuccessRate = 0.3 + i;
+ mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mAggr, mWifiMetrics);
+ }
+ mWifiScoreReport.dump(null, mPrintWriter, null);
+ verify(mPrintWriter, atLeast(11)).println(anyString());
+ }
+
+ /**
+ * Test data logging limit
+ * <p>
+ * Check that only a bounded amount of data is collected for dumpsys report
+ */
+ @Test
+ public void testDataLoggingLimit() throws Exception {
+ for (int i = 0; i < 14500; i++) {
+ mWifiInfo.setRssi(-65 + i % 20);
+ mWifiInfo.setLinkSpeed(300);
+ mWifiInfo.setFrequency(5220);
+ mWifiInfo.txSuccessRate = 0.1 + i % 100;
+ mWifiInfo.txRetriesRate = 0.2 + i % 100;
+ mWifiInfo.txBadRate = 0.0001 * i;
+ mWifiInfo.rxSuccessRate = 0.3 + i % 200;
+ mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, mAggr, mWifiMetrics);
+ }
+ mWifiScoreReport.dump(null, mPrintWriter, null);
+ verify(mPrintWriter, atMost(14401)).println(anyString());
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
index d5bfb20..055050d 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
@@ -155,12 +155,12 @@
@Mock WifiPermissionsUtil mWifiPermissionsUtil;
@Mock WifiSettingsStore mSettingsStore;
@Mock ContentResolver mContentResolver;
+ @Mock PackageManager mPackageManager;
@Mock UserManager mUserManager;
@Mock WifiConfiguration mApConfig;
@Mock ActivityManager mActivityManager;
@Mock AppOpsManager mAppOpsManager;
@Mock IBinder mAppBinder;
- @Mock WifiNotificationController mWifiNotificationController;
@Mock LocalOnlyHotspotRequestInfo mRequestInfo;
@Mock LocalOnlyHotspotRequestInfo mRequestInfo2;
@@ -217,6 +217,14 @@
};
mChannel.connect(null, handler, messenger);
}
+
+ private Message sendMessageSynchronously(Message request) {
+ return mChannel.sendMessageSynchronously(request);
+ }
+
+ private void sendMessage(Message request) {
+ mChannel.sendMessage(request);
+ }
}
@Before public void setUp() {
@@ -236,6 +244,7 @@
when(mHandlerThread.getLooper()).thenReturn(mLooper.getLooper());
when(mContext.getResources()).thenReturn(mResources);
when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
doNothing().when(mFrameworkFacade).registerContentObserver(eq(mContext), any(),
anyBoolean(), any());
when(mContext.getSystemService(Context.ACTIVITY_SERVICE)).thenReturn(mActivityManager);
@@ -268,18 +277,11 @@
when(mWifiInjector.getWifiPermissionsUtil()).thenReturn(mWifiPermissionsUtil);
when(mWifiInjector.getWifiSettingsStore()).thenReturn(mSettingsStore);
when(mWifiInjector.getClock()).thenReturn(mClock);
- when(mWifiInjector.getWifiNotificationController()).thenReturn(mWifiNotificationController);
mWifiServiceImpl = new WifiServiceImpl(mContext, mWifiInjector, mAsyncChannel);
mWifiServiceImpl.setWifiHandlerLogForTest(mLog);
}
- @Test
- public void testRemoveNetworkUnknown() {
- assertFalse(mWifiServiceImpl.removeNetwork(-1));
- }
-
- @Test
- public void testAsyncChannelHalfConnected() {
+ private WifiAsyncChannelTester verifyAsyncChannelHalfConnected() {
WifiAsyncChannelTester channelTester = new WifiAsyncChannelTester(mWifiInjector);
Handler handler = mock(Handler.class);
TestLooper looper = new TestLooper();
@@ -289,6 +291,26 @@
assertEquals("AsyncChannel must be half connected",
WifiAsyncChannelTester.CHANNEL_STATE_HALF_CONNECTED,
channelTester.getChannelState());
+ return channelTester;
+ }
+
+ /**
+ * Verifies that any operations on WifiServiceImpl without setting up the WifiStateMachine
+ * channel would fail.
+ */
+ @Test
+ public void testRemoveNetworkUnknown() {
+ assertFalse(mWifiServiceImpl.removeNetwork(-1));
+ verify(mWifiStateMachine, never()).syncRemoveNetwork(any(), anyInt());
+ }
+
+ /**
+ * Tests whether we're able to set up an async channel connection with WifiServiceImpl.
+ * This is the path used by some WifiManager public API calls.
+ */
+ @Test
+ public void testAsyncChannelHalfConnected() {
+ verifyAsyncChannelHalfConnected();
}
/**
@@ -340,6 +362,7 @@
public void testSetWifiEnabledSuccess() throws Exception {
when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED);
when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
+ when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED));
}
@@ -364,21 +387,47 @@
doThrow(new SecurityException()).when(mContext)
.enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE),
eq("WifiService"));
+ when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true);
verify(mWifiStateMachine, never()).syncGetWifiApState();
}
/**
* Verify that a call from an app with the NETWORK_SETTINGS permission can enable wifi if we
+ * are in airplane mode.
+ */
+ @Test
+ public void testSetWifiEnabledFromNetworkSettingsHolderWhenInAirplaneMode() throws Exception {
+ when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
+ when(mSettingsStore.isAirplaneModeOn()).thenReturn(true);
+ when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true);
+ assertTrue(mWifiServiceImpl.setWifiEnabled(SYSUI_PACKAGE_NAME, true));
+ verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED));
+ }
+
+ /**
+ * Verify that a caller without the NETWORK_SETTINGS permission can't enable wifi
+ * if we are in airplane mode.
+ */
+ @Test
+ public void testSetWifiEnabledFromAppFailsWhenInAirplaneMode() throws Exception {
+ when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
+ when(mSettingsStore.isAirplaneModeOn()).thenReturn(true);
+ when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false);
+ assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
+ verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED));
+ }
+
+ /**
+ * Verify that a call from an app with the NETWORK_SETTINGS permission can enable wifi if we
* are in softap mode.
*/
@Test
public void testSetWifiEnabledFromNetworkSettingsHolderWhenApEnabled() throws Exception {
when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_ENABLED);
when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
- when(mContext.checkCallingOrSelfPermission(
- eq(android.Manifest.permission.NETWORK_SETTINGS)))
- .thenReturn(PackageManager.PERMISSION_GRANTED);
+ when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true);
+ when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
assertTrue(mWifiServiceImpl.setWifiEnabled(SYSUI_PACKAGE_NAME, true));
verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED));
}
@@ -389,9 +438,8 @@
@Test
public void testSetWifiEnabledFromAppFailsWhenApEnabled() throws Exception {
when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_ENABLED);
- when(mContext.checkCallingOrSelfPermission(
- eq(android.Manifest.permission.NETWORK_SETTINGS)))
- .thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false);
+ when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
verify(mSettingsStore, never()).handleWifiToggled(anyBoolean());
verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED));
@@ -1524,8 +1572,10 @@
public void testAddPasspointProfileViaAddNetwork() throws Exception {
WifiConfiguration config = WifiConfigurationTestUtil.createPasspointNetwork();
config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
- when(mResources.getBoolean(com.android.internal.R.bool.config_wifi_hotspot2_enabled))
- .thenReturn(true);
+
+ PackageManager pm = mock(PackageManager.class);
+ when(pm.hasSystemFeature(PackageManager.FEATURE_WIFI_PASSPOINT)).thenReturn(true);
+ when(mContext.getPackageManager()).thenReturn(pm);
when(mWifiStateMachine.syncAddOrUpdatePasspointConfig(any(),
any(PasspointConfiguration.class), anyInt())).thenReturn(true);
@@ -1580,4 +1630,183 @@
mWifiServiceImpl.retrieveBackupData();
verify(mWifiBackupRestore, never()).retrieveBackupDataFromConfigurations(any(List.class));
}
+
+ /**
+ * Helper to test handling of async messages by wifi service when the message comes from an
+ * app without {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission.
+ */
+ private void verifyAsyncChannelMessageHandlingWithoutChangePermisson(
+ int requestMsgWhat, int expectedReplyMsgwhat) {
+ WifiAsyncChannelTester tester = verifyAsyncChannelHalfConnected();
+
+ int uidWithoutPermission = 5;
+ when(mWifiPermissionsUtil.checkChangePermission(eq(uidWithoutPermission)))
+ .thenReturn(false);
+
+ Message request = Message.obtain();
+ request.what = requestMsgWhat;
+ request.sendingUid = uidWithoutPermission;
+
+ mLooper.startAutoDispatch();
+ Message reply = tester.sendMessageSynchronously(request);
+ mLooper.stopAutoDispatch();
+
+ verify(mWifiStateMachine, never()).sendMessage(any(Message.class));
+ assertEquals(expectedReplyMsgwhat, reply.what);
+ assertEquals(WifiManager.NOT_AUTHORIZED, reply.arg1);
+ }
+
+ /**
+ * Verify that the CONNECT_NETWORK message received from an app without
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is rejected with the correct
+ * error code.
+ */
+ @Test
+ public void testConnectNetworkWithoutChangePermission() throws Exception {
+ verifyAsyncChannelMessageHandlingWithoutChangePermisson(
+ WifiManager.CONNECT_NETWORK, WifiManager.CONNECT_NETWORK_FAILED);
+ }
+
+ /**
+ * Verify that the FORGET_NETWORK message received from an app without
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is rejected with the correct
+ * error code.
+ */
+ @Test
+ public void testForgetNetworkWithoutChangePermission() throws Exception {
+ verifyAsyncChannelMessageHandlingWithoutChangePermisson(
+ WifiManager.SAVE_NETWORK, WifiManager.SAVE_NETWORK_FAILED);
+ }
+
+ /**
+ * Verify that the START_WPS message received from an app without
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is rejected with the correct
+ * error code.
+ */
+ @Test
+ public void testStartWpsWithoutChangePermission() throws Exception {
+ verifyAsyncChannelMessageHandlingWithoutChangePermisson(
+ WifiManager.START_WPS, WifiManager.WPS_FAILED);
+ }
+
+ /**
+ * Verify that the CANCEL_WPS message received from an app without
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is rejected with the correct
+ * error code.
+ */
+ @Test
+ public void testCancelWpsWithoutChangePermission() throws Exception {
+ verifyAsyncChannelMessageHandlingWithoutChangePermisson(
+ WifiManager.CANCEL_WPS, WifiManager.CANCEL_WPS_FAILED);
+ }
+
+ /**
+ * Verify that the DISABLE_NETWORK message received from an app without
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is rejected with the correct
+ * error code.
+ */
+ @Test
+ public void testDisableNetworkWithoutChangePermission() throws Exception {
+ verifyAsyncChannelMessageHandlingWithoutChangePermisson(
+ WifiManager.DISABLE_NETWORK, WifiManager.DISABLE_NETWORK_FAILED);
+ }
+
+ /**
+ * Verify that the RSSI_PKTCNT_FETCH message received from an app without
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is rejected with the correct
+ * error code.
+ */
+ @Test
+ public void testRssiPktcntFetchWithoutChangePermission() throws Exception {
+ verifyAsyncChannelMessageHandlingWithoutChangePermisson(
+ WifiManager.RSSI_PKTCNT_FETCH, WifiManager.RSSI_PKTCNT_FETCH_FAILED);
+ }
+
+ /**
+ * Helper to test handling of async messages by wifi service when the message comes from an
+ * app with {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission.
+ */
+ private void verifyAsyncChannelMessageHandlingWithChangePermisson(
+ int requestMsgWhat, Object requestMsgObj) {
+ WifiAsyncChannelTester tester = verifyAsyncChannelHalfConnected();
+
+ when(mWifiPermissionsUtil.checkChangePermission(anyInt())).thenReturn(true);
+
+ Message request = Message.obtain();
+ request.what = requestMsgWhat;
+ request.obj = requestMsgObj;
+
+ tester.sendMessage(request);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mWifiStateMachine).sendMessage(messageArgumentCaptor.capture());
+ assertEquals(requestMsgWhat, messageArgumentCaptor.getValue().what);
+ }
+
+ /**
+ * Verify that the CONNECT_NETWORK message received from an app with
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is forwarded to
+ * WifiStateMachine.
+ */
+ @Test
+ public void testConnectNetworkWithChangePermission() throws Exception {
+ verifyAsyncChannelMessageHandlingWithChangePermisson(
+ WifiManager.CONNECT_NETWORK, new WifiConfiguration());
+ }
+
+ /**
+ * Verify that the SAVE_NETWORK message received from an app with
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is forwarded to
+ * WifiStateMachine.
+ */
+ @Test
+ public void testSaveNetworkWithChangePermission() throws Exception {
+ verifyAsyncChannelMessageHandlingWithChangePermisson(
+ WifiManager.SAVE_NETWORK, new WifiConfiguration());
+ }
+
+ /**
+ * Verify that the START_WPS message received from an app with
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is forwarded to
+ * WifiStateMachine.
+ */
+ @Test
+ public void testStartWpsWithChangePermission() throws Exception {
+ verifyAsyncChannelMessageHandlingWithChangePermisson(
+ WifiManager.START_WPS, new Object());
+ }
+
+ /**
+ * Verify that the CANCEL_WPS message received from an app with
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is forwarded to
+ * WifiStateMachine.
+ */
+ @Test
+ public void testCancelWpsWithChangePermission() throws Exception {
+ verifyAsyncChannelMessageHandlingWithChangePermisson(
+ WifiManager.CANCEL_WPS, new Object());
+ }
+
+ /**
+ * Verify that the DISABLE_NETWORK message received from an app with
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is forwarded to
+ * WifiStateMachine.
+ */
+ @Test
+ public void testDisableNetworkWithChangePermission() throws Exception {
+ verifyAsyncChannelMessageHandlingWithChangePermisson(
+ WifiManager.DISABLE_NETWORK, new Object());
+ }
+
+ /**
+ * Verify that the RSSI_PKTCNT_FETCH message received from an app with
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is forwarded to
+ * WifiStateMachine.
+ */
+ @Test
+ public void testRssiPktcntFetchWithChangePermission() throws Exception {
+ verifyAsyncChannelMessageHandlingWithChangePermisson(
+ WifiManager.RSSI_PKTCNT_FETCH, new Object());
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
index 2a30b67..929a5fe 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
@@ -30,6 +30,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
@@ -41,7 +42,6 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
-import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.DhcpResults;
import android.net.LinkProperties;
@@ -80,10 +80,13 @@
import android.os.test.TestLooper;
import android.provider.Settings;
import android.security.KeyStore;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import com.android.internal.R;
@@ -130,6 +133,8 @@
(ActivityManager.isLowRamDeviceStatic()
? WifiStateMachine.NUM_LOG_RECS_VERBOSE_LOW_MEMORY
: WifiStateMachine.NUM_LOG_RECS_VERBOSE);
+ private static final int FRAMEWORK_NETWORK_ID = 7;
+ private static final int TEST_RSSI = -54;
private static final int WPS_SUPPLICANT_NETWORK_ID = 5;
private static final int WPS_FRAMEWORK_NETWORK_ID = 10;
private static final String DEFAULT_TEST_SSID = "\"GoogleGuest\"";
@@ -153,32 +158,6 @@
mWsm.enableVerboseLogging(1);
}
- private class TestIpManager extends IpManager {
- TestIpManager(Context context, String ifname, IpManager.Callback callback) {
- // Call dependency-injection superclass constructor.
- super(context, ifname, callback, mock(INetworkManagementService.class));
- }
-
- @Override
- public void startProvisioning(IpManager.ProvisioningConfiguration config) {}
-
- @Override
- public void stop() {}
-
- @Override
- public void confirmConfiguration() {}
-
- void injectDhcpSuccess(DhcpResults dhcpResults) {
- mCallback.onNewDhcpResults(dhcpResults);
- mCallback.onProvisioningSuccess(new LinkProperties());
- }
-
- void injectDhcpFailure() {
- mCallback.onNewDhcpResults(null);
- mCallback.onProvisioningFailure(new LinkProperties());
- }
- }
-
private FrameworkFacade getFrameworkFacade() throws Exception {
FrameworkFacade facade = mock(FrameworkFacade.class);
@@ -212,8 +191,8 @@
.then(new AnswerWithArguments() {
public IpManager answer(
Context context, String ifname, IpManager.Callback callback) {
- mTestIpManager = new TestIpManager(context, ifname, callback);
- return mTestIpManager;
+ mIpManagerCallback = callback;
+ return mIpManager;
}
});
@@ -227,9 +206,6 @@
Context context = mock(Context.class);
when(context.getPackageManager()).thenReturn(pkgMgr);
- MockResources resources = new com.android.server.wifi.MockResources();
- when(context.getResources()).thenReturn(resources);
-
MockContentResolver mockContentResolver = new MockContentResolver();
mockContentResolver.addProvider(Settings.AUTHORITY,
new MockContentProvider(context) {
@@ -253,9 +229,11 @@
return context;
}
- private Resources getMockResources() {
+ private MockResources getMockResources() {
MockResources resources = new MockResources();
resources.setBoolean(R.bool.config_wifi_enable_wifi_firmware_debugging, false);
+ resources.setBoolean(
+ R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit, false);
return resources;
}
@@ -290,11 +268,11 @@
Log.d(TAG, "WifiStateMachine state -" + stream.toString());
}
- private static ScanDetail getGoogleGuestScanDetail(int rssi) {
+ private static ScanDetail getGoogleGuestScanDetail(int rssi, String bssid, int freq) {
ScanResult.InformationElement ie[] = new ScanResult.InformationElement[1];
ie[0] = ScanResults.generateSsidIe(sSSID);
NetworkDetail nd = new NetworkDetail(sBSSID, ie, new ArrayList<String>(), sFreq);
- ScanDetail detail = new ScanDetail(nd, sWifiSsid, sBSSID, "", rssi, sFreq,
+ ScanDetail detail = new ScanDetail(nd, sWifiSsid, bssid, "", rssi, freq,
Long.MAX_VALUE, /* needed so that scan results aren't rejected because
there older than scan start */
ie, new ArrayList<String>());
@@ -305,16 +283,26 @@
ScanResults sr = ScanResults.create(0, 2412, 2437, 2462, 5180, 5220, 5745, 5825);
ArrayList<ScanDetail> list = sr.getScanDetailArrayList();
- int rssi = -65;
- list.add(getGoogleGuestScanDetail(rssi));
+ list.add(getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq));
return list;
}
+ private void injectDhcpSuccess(DhcpResults dhcpResults) {
+ mIpManagerCallback.onNewDhcpResults(dhcpResults);
+ mIpManagerCallback.onProvisioningSuccess(new LinkProperties());
+ }
+
+ private void injectDhcpFailure() {
+ mIpManagerCallback.onNewDhcpResults(null);
+ mIpManagerCallback.onProvisioningFailure(new LinkProperties());
+ }
+
static final String sSSID = "\"GoogleGuest\"";
static final WifiSsid sWifiSsid = WifiSsid.createFromAsciiEncoded(sSSID);
- static final String sHexSSID = sWifiSsid.getHexString().replace("0x", "").replace("22", "");
static final String sBSSID = "01:02:03:04:05:06";
+ static final String sBSSID1 = "02:01:04:03:06:05";
static final int sFreq = 2437;
+ static final int sFreq1 = 5240;
static final String WIFI_IFACE_NAME = "mockWlan";
WifiStateMachine mWsm;
@@ -324,9 +312,12 @@
AsyncChannel mWsmAsyncChannel;
TestAlarmManager mAlarmManager;
MockWifiMonitor mWifiMonitor;
- TestIpManager mTestIpManager;
TestLooper mLooper;
Context mContext;
+ MockResources mResources;
+ FrameworkFacade mFrameworkFacade;
+ IpManager.Callback mIpManagerCallback;
+ PhoneStateListener mPhoneStateListener;
final ArgumentCaptor<SoftApManager.Listener> mSoftApManagerListenerCaptor =
ArgumentCaptor.forClass(SoftApManager.Listener.class);
@@ -354,6 +345,11 @@
@Mock WifiStateTracker mWifiStateTracker;
@Mock PasspointManager mPasspointManager;
@Mock SelfRecovery mSelfRecovery;
+ @Mock IpManager mIpManager;
+ @Mock TelephonyManager mTelephonyManager;
+ @Mock WrongPasswordNotifier mWrongPasswordNotifier;
+ @Mock Clock mClock;
+ @Mock ScanDetailCache mScanDetailCache;
public WifiStateMachineTest() throws Exception {
}
@@ -394,9 +390,13 @@
when(mWifiInjector.getWifiMonitor()).thenReturn(mWifiMonitor);
when(mWifiInjector.getWifiNative()).thenReturn(mWifiNative);
when(mWifiInjector.getSelfRecovery()).thenReturn(mSelfRecovery);
+ when(mWifiInjector.makeTelephonyManager()).thenReturn(mTelephonyManager);
+ when(mWifiInjector.getClock()).thenReturn(mClock);
- when(mWifiNative.setupForClientMode()).thenReturn(mClientInterface);
- when(mWifiNative.setupForSoftApMode()).thenReturn(mApInterface);
+ when(mWifiNative.setupForClientMode())
+ .thenReturn(Pair.create(WifiNative.SETUP_SUCCESS, mClientInterface));
+ when(mWifiNative.setupForSoftApMode())
+ .thenReturn(Pair.create(WifiNative.SETUP_SUCCESS, mApInterface));
when(mApInterface.getInterfaceName()).thenReturn(WIFI_IFACE_NAME);
when(mWifiNative.getInterfaceName()).thenReturn(WIFI_IFACE_NAME);
when(mWifiNative.enableSupplicant()).thenReturn(true);
@@ -404,21 +404,21 @@
when(mWifiNative.getFrameworkNetworkId(anyInt())).thenReturn(0);
- FrameworkFacade factory = getFrameworkFacade();
+ mFrameworkFacade = getFrameworkFacade();
mContext = getContext();
- Resources resources = getMockResources();
- when(mContext.getResources()).thenReturn(resources);
+ mResources = getMockResources();
+ when(mContext.getResources()).thenReturn(mResources);
- when(factory.getIntegerSetting(mContext,
+ when(mFrameworkFacade.getIntegerSetting(mContext,
Settings.Global.WIFI_FREQUENCY_BAND,
WifiManager.WIFI_FREQUENCY_BAND_AUTO)).thenReturn(
WifiManager.WIFI_FREQUENCY_BAND_AUTO);
- when(factory.makeApConfigStore(eq(mContext), eq(mBackupManagerProxy)))
+ when(mFrameworkFacade.makeApConfigStore(eq(mContext), eq(mBackupManagerProxy)))
.thenReturn(mApConfigStore);
- when(factory.makeSupplicantStateTracker(
+ when(mFrameworkFacade.makeSupplicantStateTracker(
any(Context.class), any(WifiConfigManager.class),
any(Handler.class))).thenReturn(mSupplicantStateTracker);
@@ -431,8 +431,20 @@
when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder);
when(mClientInterface.asBinder()).thenReturn(mClientInterfaceBinder);
- mWsm = new WifiStateMachine(mContext, factory, mLooper.getLooper(),
- mUserManager, mWifiInjector, mBackupManagerProxy, mCountryCode, mWifiNative);
+ doAnswer(new AnswerWithArguments() {
+ public void answer(PhoneStateListener phoneStateListener, int events)
+ throws Exception {
+ mPhoneStateListener = phoneStateListener;
+ }
+ }).when(mTelephonyManager).listen(any(PhoneStateListener.class), anyInt());
+
+ initializeWsm();
+ }
+
+ private void initializeWsm() throws Exception {
+ mWsm = new WifiStateMachine(mContext, mFrameworkFacade, mLooper.getLooper(),
+ mUserManager, mWifiInjector, mBackupManagerProxy, mCountryCode, mWifiNative,
+ mWrongPasswordNotifier);
mWsmThread = getWsmHandlerThread(mWsm);
final AsyncChannel channel = new AsyncChannel();
@@ -459,6 +471,10 @@
/* Now channel is supposed to be connected */
mBinderToken = Binder.clearCallingIdentity();
+
+ /* Send the BOOT_COMPLETED message to setup some WSM state. */
+ mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED);
+ mLooper.dispatchAll();
}
@After
@@ -517,6 +533,7 @@
assertEquals("SoftApState", getCurrentState().getName());
+ verify(mWifiNative).setupForSoftApMode();
verify(mSoftApManager).start();
// reset expectations for mContext due to previously sent AP broadcast
@@ -796,10 +813,14 @@
mWsm.sendMessage(WifiMonitor.SUP_CONNECTION_EVENT);
mLooper.dispatchAll();
+
+ verify(mWifiNative).setupForClientMode();
+ verify(mWifiLastResortWatchdog).clearAllFailureCounts();
}
private void addNetworkAndVerifySuccess(boolean isHidden) throws Exception {
WifiConfiguration config = new WifiConfiguration();
+ config.networkId = FRAMEWORK_NETWORK_ID;
config.SSID = sSSID;
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
config.hiddenSSID = isHidden;
@@ -932,6 +953,13 @@
verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
verify(mWifiConnectivityManager).setUserConnectChoice(eq(0));
+ when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID))
+ .thenReturn(mScanDetailCache);
+
+ when(mScanDetailCache.getScanDetail(sBSSID)).thenReturn(
+ getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq));
+ when(mScanDetailCache.get(sBSSID)).thenReturn(
+ getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq).getScanResult());
mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID);
mLooper.dispatchAll();
@@ -948,9 +976,15 @@
dhcpResults.addDns("8.8.8.8");
dhcpResults.setLeaseDuration(3600);
- mTestIpManager.injectDhcpSuccess(dhcpResults);
+ injectDhcpSuccess(dhcpResults);
mLooper.dispatchAll();
+ WifiInfo wifiInfo = mWsm.getWifiInfo();
+ assertNotNull(wifiInfo);
+ assertEquals(sBSSID, wifiInfo.getBSSID());
+ assertEquals(sFreq, wifiInfo.getFrequency());
+ assertTrue(sWifiSsid.equals(wifiInfo.getWifiSsid()));
+
assertEquals("ConnectedState", getCurrentState().getName());
}
@@ -986,7 +1020,7 @@
dhcpResults.addDns("8.8.8.8");
dhcpResults.setLeaseDuration(3600);
- mTestIpManager.injectDhcpSuccess(dhcpResults);
+ injectDhcpSuccess(dhcpResults);
mLooper.dispatchAll();
assertEquals("ConnectedState", getCurrentState().getName());
@@ -1069,12 +1103,108 @@
assertEquals("ObtainingIpState", getCurrentState().getName());
- mTestIpManager.injectDhcpFailure();
+ injectDhcpFailure();
mLooper.dispatchAll();
assertEquals("DisconnectingState", getCurrentState().getName());
}
+ /**
+ * Verify that the network selection status will be updated with DISABLED_AUTHENTICATION_FAILURE
+ * when wrong password authentication failure is detected and the network had been
+ * connected previously.
+ */
+ @Test
+ public void testWrongPasswordWithPreviouslyConnected() throws Exception {
+ initializeAndAddNetworkAndVerifySuccess();
+
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ mLooper.dispatchAll();
+
+ mLooper.startAutoDispatch();
+ mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
+ mLooper.stopAutoDispatch();
+
+ verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
+
+ WifiConfiguration config = new WifiConfiguration();
+ config.getNetworkSelectionStatus().setHasEverConnected(true);
+ when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(config);
+
+ mWsm.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, 0,
+ WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD);
+ mLooper.dispatchAll();
+
+ verify(mWrongPasswordNotifier, never()).onWrongPasswordError(anyString());
+ verify(mWifiConfigManager).updateNetworkSelectionStatus(anyInt(),
+ eq(WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE));
+
+ assertEquals("DisconnectedState", getCurrentState().getName());
+ }
+
+ /**
+ * Verify that the network selection status will be updated with DISABLED_BY_WRONG_PASSWORD
+ * when wrong password authentication failure is detected and the network has never been
+ * connected.
+ */
+ @Test
+ public void testWrongPasswordWithNeverConnected() throws Exception {
+ initializeAndAddNetworkAndVerifySuccess();
+
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ mLooper.dispatchAll();
+
+ mLooper.startAutoDispatch();
+ mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
+ mLooper.stopAutoDispatch();
+
+ verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
+
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = sSSID;
+ config.getNetworkSelectionStatus().setHasEverConnected(false);
+ when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(config);
+
+ mWsm.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, 0,
+ WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD);
+ mLooper.dispatchAll();
+
+ verify(mWrongPasswordNotifier).onWrongPasswordError(eq(sSSID));
+ verify(mWifiConfigManager).updateNetworkSelectionStatus(anyInt(),
+ eq(WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD));
+
+ assertEquals("DisconnectedState", getCurrentState().getName());
+ }
+
+ /**
+ * Verify that the network selection status will be updated with DISABLED_BY_WRONG_PASSWORD
+ * when wrong password authentication failure is detected and the network is unknown.
+ */
+ @Test
+ public void testWrongPasswordWithNullNetwork() throws Exception {
+ initializeAndAddNetworkAndVerifySuccess();
+
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ mLooper.dispatchAll();
+
+ mLooper.startAutoDispatch();
+ mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
+ mLooper.stopAutoDispatch();
+
+ verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
+
+ when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(null);
+
+ mWsm.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, 0,
+ WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD);
+ mLooper.dispatchAll();
+
+ verify(mWifiConfigManager).updateNetworkSelectionStatus(anyInt(),
+ eq(WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD));
+
+ assertEquals("DisconnectedState", getCurrentState().getName());
+ }
+
@Test
public void testBadNetworkEvent() throws Exception {
initializeAndAddNetworkAndVerifySuccess();
@@ -1113,7 +1243,7 @@
public void disconnect() throws Exception {
connect();
- mWsm.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, -1, 3, "01:02:03:04:05:06");
+ mWsm.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, -1, 3, sBSSID);
mLooper.dispatchAll();
mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.DISCONNECTED));
@@ -1606,6 +1736,61 @@
}
/**
+ * Verifies that WifiInfo is updated upon SUPPLICANT_STATE_CHANGE_EVENT.
+ */
+ @Test
+ public void testWifiInfoUpdatedUponSupplicantStateChangedEvent() throws Exception {
+ // Connect to network with |sBSSID|, |sFreq|.
+ connect();
+
+ // Set the scan detail cache for roaming target.
+ when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID))
+ .thenReturn(mScanDetailCache);
+ when(mScanDetailCache.getScanDetail(sBSSID1)).thenReturn(
+ getGoogleGuestScanDetail(TEST_RSSI, sBSSID1, sFreq1));
+ when(mScanDetailCache.get(sBSSID1)).thenReturn(
+ getGoogleGuestScanDetail(TEST_RSSI, sBSSID1, sFreq1).getScanResult());
+
+ // This simulates the behavior of roaming to network with |sBSSID1|, |sFreq1|.
+ // Send a SUPPLICANT_STATE_CHANGE_EVENT, verify WifiInfo is updated.
+ mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
+ new StateChangeResult(0, sWifiSsid, sBSSID1, SupplicantState.COMPLETED));
+ mLooper.dispatchAll();
+
+ WifiInfo wifiInfo = mWsm.getWifiInfo();
+ assertEquals(sBSSID1, wifiInfo.getBSSID());
+ assertEquals(sFreq1, wifiInfo.getFrequency());
+ assertEquals(SupplicantState.COMPLETED, wifiInfo.getSupplicantState());
+ }
+
+ /**
+ * Verifies that WifiInfo is updated upon CMD_ASSOCIATED_BSSID event.
+ */
+ @Test
+ public void testWifiInfoUpdatedUponAssociatedBSSIDEvent() throws Exception {
+ // Connect to network with |sBSSID|, |sFreq|.
+ connect();
+
+ // Set the scan detail cache for roaming target.
+ when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID))
+ .thenReturn(mScanDetailCache);
+ when(mScanDetailCache.getScanDetail(sBSSID1)).thenReturn(
+ getGoogleGuestScanDetail(TEST_RSSI, sBSSID1, sFreq1));
+ when(mScanDetailCache.get(sBSSID1)).thenReturn(
+ getGoogleGuestScanDetail(TEST_RSSI, sBSSID1, sFreq1).getScanResult());
+
+ // This simulates the behavior of roaming to network with |sBSSID1|, |sFreq1|.
+ // Send a CMD_ASSOCIATED_BSSID, verify WifiInfo is updated.
+ mWsm.sendMessage(WifiStateMachine.CMD_ASSOCIATED_BSSID, 0, 0, sBSSID1);
+ mLooper.dispatchAll();
+
+ WifiInfo wifiInfo = mWsm.getWifiInfo();
+ assertEquals(sBSSID1, wifiInfo.getBSSID());
+ assertEquals(sFreq1, wifiInfo.getFrequency());
+ assertEquals(SupplicantState.COMPLETED, wifiInfo.getSupplicantState());
+ }
+
+ /**
* Verifies that WifiInfo is cleared upon exiting and entering WifiInfo, and that it is not
* updated by SUPPLICAN_STATE_CHANGE_EVENTs in ScanModeState.
* This protects WifiStateMachine from getting into a bad state where WifiInfo says wifi is
@@ -1714,4 +1899,335 @@
mLooper.stopAutoDispatch();
assertFalse(succeeded);
}
+
+ /**
+ * Test that failure to start HAL in AP mode increments the corresponding metrics.
+ */
+ @Test
+ public void testSetupForSoftApModeHalFailureIncrementsMetrics() throws Exception {
+ when(mWifiNative.setupForSoftApMode())
+ .thenReturn(Pair.create(WifiNative.SETUP_FAILURE_HAL, null));
+
+ SoftApModeConfiguration config = new SoftApModeConfiguration(
+ WifiManager.IFACE_IP_MODE_TETHERED, new WifiConfiguration());
+ mWsm.setHostApRunning(config, true);
+ mLooper.dispatchAll();
+
+ verify(mWifiNative).setupForSoftApMode();
+ verify(mWifiMetrics).incrementNumWifiOnFailureDueToHal();
+ }
+
+ /**
+ * Test that failure to start HAL in AP mode increments the corresponding metrics.
+ */
+ @Test
+ public void testSetupForSoftApModeWificondFailureIncrementsMetrics() throws Exception {
+ when(mWifiNative.setupForSoftApMode())
+ .thenReturn(Pair.create(WifiNative.SETUP_FAILURE_WIFICOND, null));
+
+ SoftApModeConfiguration config = new SoftApModeConfiguration(
+ WifiManager.IFACE_IP_MODE_TETHERED, new WifiConfiguration());
+ mWsm.setHostApRunning(config, true);
+ mLooper.dispatchAll();
+
+ verify(mWifiNative).setupForSoftApMode();
+ verify(mWifiMetrics).incrementNumWifiOnFailureDueToWificond();
+ }
+
+ /**
+ * Test that failure to start HAL in client mode increments the corresponding metrics.
+ */
+ @Test
+ public void testSetupForClientModeHalFailureIncrementsMetrics() throws Exception {
+ when(mWifiNative.setupForClientMode())
+ .thenReturn(Pair.create(WifiNative.SETUP_FAILURE_HAL, null));
+
+ mWsm.setSupplicantRunning(true);
+ mLooper.dispatchAll();
+
+ mWsm.sendMessage(WifiMonitor.SUP_CONNECTION_EVENT);
+ mLooper.dispatchAll();
+
+ verify(mWifiNative).setupForClientMode();
+ verify(mWifiMetrics).incrementNumWifiOnFailureDueToHal();
+ }
+
+ /**
+ * Test that failure to start HAL in client mode increments the corresponding metrics.
+ */
+ @Test
+ public void testSetupForClientModeWificondFailureIncrementsMetrics() throws Exception {
+ when(mWifiNative.setupForClientMode())
+ .thenReturn(Pair.create(WifiNative.SETUP_FAILURE_WIFICOND, null));
+
+ mWsm.setSupplicantRunning(true);
+ mLooper.dispatchAll();
+
+ mWsm.sendMessage(WifiMonitor.SUP_CONNECTION_EVENT);
+ mLooper.dispatchAll();
+
+ verify(mWifiNative).setupForClientMode();
+ verify(mWifiMetrics).incrementNumWifiOnFailureDueToWificond();
+ }
+
+ /**
+ * Test that we don't register the telephony call state listener on devices which do not support
+ * setting/resetting Tx power limit.
+ */
+ @Test
+ public void testVoiceCallSar_disabledTxPowerScenario_WifiOn() throws Exception {
+ loadComponentsInStaMode();
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
+ assertEquals("DisconnectedState", getCurrentState().getName());
+ assertNull(mPhoneStateListener);
+ }
+
+ /**
+ * Test that we do register the telephony call state listener on devices which do support
+ * setting/resetting Tx power limit.
+ */
+ @Test
+ public void testVoiceCallSar_enabledTxPowerScenario_WifiOn() throws Exception {
+ mResources.setBoolean(
+ R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit, true);
+ initializeWsm();
+
+ loadComponentsInStaMode();
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
+ assertEquals("DisconnectedState", getCurrentState().getName());
+ assertNotNull(mPhoneStateListener);
+ }
+
+ /**
+ * Test that we do register the telephony call state listener on devices which do support
+ * setting/resetting Tx power limit and set the tx power level if we're in state
+ * {@link TelephonyManager#CALL_STATE_OFFHOOK}.
+ */
+ @Test
+ public void testVoiceCallSar_enabledTxPowerScenarioCallStateOffHook_WhenWifiTurnedOn()
+ throws Exception {
+ mResources.setBoolean(
+ R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit, true);
+ initializeWsm();
+
+ when(mWifiNative.selectTxPowerScenario(anyInt())).thenReturn(true);
+ when(mTelephonyManager.isOffhook()).thenReturn(true);
+
+ loadComponentsInStaMode();
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
+ assertEquals("DisconnectedState", getCurrentState().getName());
+ assertNotNull(mPhoneStateListener);
+ verify(mWifiNative).selectTxPowerScenario(eq(WifiNative.TX_POWER_SCENARIO_VOICE_CALL));
+ }
+
+ /**
+ * Test that we do register the telephony call state listener on devices which do support
+ * setting/resetting Tx power limit and set the tx power level if we're in state
+ * {@link TelephonyManager#CALL_STATE_IDLE}.
+ */
+ @Test
+ public void testVoiceCallSar_enabledTxPowerScenarioCallStateIdle_WhenWifiTurnedOn()
+ throws Exception {
+ mResources.setBoolean(
+ R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit, true);
+ initializeWsm();
+
+ when(mWifiNative.selectTxPowerScenario(anyInt())).thenReturn(true);
+ when(mTelephonyManager.isIdle()).thenReturn(true);
+
+ loadComponentsInStaMode();
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
+ assertEquals("DisconnectedState", getCurrentState().getName());
+ assertNotNull(mPhoneStateListener);
+ }
+
+ /**
+ * Test that we do register the telephony call state listener on devices which do support
+ * setting/resetting Tx power limit and set the tx power level if we're in state
+ * {@link TelephonyManager#CALL_STATE_OFFHOOK}. This test checks if the
+ * {@link WifiNative#selectTxPowerScenario(int)} failure is handled correctly.
+ */
+ @Test
+ public void testVoiceCallSar_enabledTxPowerScenarioCallStateOffHook_WhenWifiTurnedOn_Fails()
+ throws Exception {
+ mResources.setBoolean(
+ R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit, true);
+ initializeWsm();
+
+ when(mWifiNative.selectTxPowerScenario(anyInt())).thenReturn(false);
+ when(mTelephonyManager.isOffhook()).thenReturn(true);
+
+ loadComponentsInStaMode();
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
+ assertEquals("DisconnectedState", getCurrentState().getName());
+ assertNotNull(mPhoneStateListener);
+ verify(mWifiNative).selectTxPowerScenario(eq(WifiNative.TX_POWER_SCENARIO_VOICE_CALL));
+ }
+
+ /**
+ * Test that we invoke the corresponding WifiNative method when
+ * {@link PhoneStateListener#onCallStateChanged(int, String)} is invoked with state
+ * {@link TelephonyManager#CALL_STATE_OFFHOOK}.
+ */
+ @Test
+ public void testVoiceCallSar_enabledTxPowerScenarioCallStateOffHook_WhenWifiOn()
+ throws Exception {
+ when(mWifiNative.selectTxPowerScenario(anyInt())).thenReturn(true);
+ testVoiceCallSar_enabledTxPowerScenario_WifiOn();
+
+ mPhoneStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK, "");
+ mLooper.dispatchAll();
+ verify(mWifiNative).selectTxPowerScenario(eq(WifiNative.TX_POWER_SCENARIO_VOICE_CALL));
+ }
+
+ /**
+ * Test that we invoke the corresponding WifiNative method when
+ * {@link PhoneStateListener#onCallStateChanged(int, String)} is invoked with state
+ * {@link TelephonyManager#CALL_STATE_IDLE}.
+ */
+ @Test
+ public void testVoiceCallSar_enabledTxPowerScenarioCallStateIdle_WhenWifiOn() throws Exception {
+ when(mWifiNative.selectTxPowerScenario(anyInt())).thenReturn(true);
+ testVoiceCallSar_enabledTxPowerScenario_WifiOn();
+
+ mPhoneStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_IDLE, "");
+ mLooper.dispatchAll();
+ verify(mWifiNative, atLeastOnce())
+ .selectTxPowerScenario(eq(WifiNative.TX_POWER_SCENARIO_NORMAL));
+ }
+
+ /**
+ * Test that we invoke the corresponding WifiNative method when
+ * {@link PhoneStateListener#onCallStateChanged(int, String)} is invoked with state
+ * {@link TelephonyManager#CALL_STATE_OFFHOOK}. This test checks if the
+ * {@link WifiNative#selectTxPowerScenario(int)} failure is handled correctly.
+ */
+ @Test
+ public void testVoiceCallSar_enabledTxPowerScenarioCallStateOffHook_WhenWifiOn_Fails()
+ throws Exception {
+ when(mWifiNative.selectTxPowerScenario(anyInt())).thenReturn(false);
+ testVoiceCallSar_enabledTxPowerScenario_WifiOn();
+
+ mPhoneStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK, "");
+ mLooper.dispatchAll();
+ verify(mWifiNative).selectTxPowerScenario(eq(WifiNative.TX_POWER_SCENARIO_VOICE_CALL));
+ }
+
+ /**
+ * Test that we don't invoke the corresponding WifiNative method when
+ * {@link PhoneStateListener#onCallStateChanged(int, String)} is invoked with state
+ * {@link TelephonyManager#CALL_STATE_IDLE} or {@link TelephonyManager#CALL_STATE_OFFHOOK} when
+ * wifi is off (state machine is not in SupplicantStarted state).
+ */
+ @Test
+ public void testVoiceCallSar_enabledTxPowerScenarioCallState_WhenWifiOff() throws Exception {
+ mResources.setBoolean(
+ R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit, true);
+ initializeWsm();
+
+ mPhoneStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK, "");
+ mLooper.dispatchAll();
+ verify(mWifiNative, never()).selectTxPowerScenario(anyInt());
+
+ mPhoneStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_IDLE, "");
+ mLooper.dispatchAll();
+ verify(mWifiNative, never()).selectTxPowerScenario(anyInt());
+ }
+
+ /**
+ * Verifies that a network disconnection event will result in WifiStateMachine invoking
+ * {@link WifiConfigManager#removeAllEphemeralOrPasspointConfiguredNetworks()} to remove
+ * any ephemeral or passpoint networks from it's internal database.
+ */
+ @Test
+ public void testDisconnectionRemovesEphemeralAndPasspointNetworks() throws Exception {
+ disconnect();
+ verify(mWifiConfigManager).removeAllEphemeralOrPasspointConfiguredNetworks();
+ }
+
+ /**
+ * Test that the helper method
+ * {@link WifiStateMachine#shouldEvaluateWhetherToSendExplicitlySelected(WifiConfiguration)}
+ * returns true when we connect to the last selected network before expiration of
+ * {@link WifiStateMachine#LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS}.
+ */
+ @Test
+ public void testShouldEvaluateWhetherToSendExplicitlySelected_SameNetworkNotExpired() {
+ long lastSelectedTimestamp = 45666743454L;
+ int lastSelectedNetworkId = 5;
+
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(
+ lastSelectedTimestamp
+ + WifiStateMachine.LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS - 1);
+ when(mWifiConfigManager.getLastSelectedTimeStamp()).thenReturn(lastSelectedTimestamp);
+ when(mWifiConfigManager.getLastSelectedNetwork()).thenReturn(lastSelectedNetworkId);
+
+ WifiConfiguration currentConfig = new WifiConfiguration();
+ currentConfig.networkId = lastSelectedNetworkId;
+ assertTrue(mWsm.shouldEvaluateWhetherToSendExplicitlySelected(currentConfig));
+ }
+
+ /**
+ * Test that the helper method
+ * {@link WifiStateMachine#shouldEvaluateWhetherToSendExplicitlySelected(WifiConfiguration)}
+ * returns false when we connect to the last selected network after expiration of
+ * {@link WifiStateMachine#LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS}.
+ */
+ @Test
+ public void testShouldEvaluateWhetherToSendExplicitlySelected_SameNetworkExpired() {
+ long lastSelectedTimestamp = 45666743454L;
+ int lastSelectedNetworkId = 5;
+
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(
+ lastSelectedTimestamp
+ + WifiStateMachine.LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS + 1);
+ when(mWifiConfigManager.getLastSelectedTimeStamp()).thenReturn(lastSelectedTimestamp);
+ when(mWifiConfigManager.getLastSelectedNetwork()).thenReturn(lastSelectedNetworkId);
+
+ WifiConfiguration currentConfig = new WifiConfiguration();
+ currentConfig.networkId = lastSelectedNetworkId;
+ assertFalse(mWsm.shouldEvaluateWhetherToSendExplicitlySelected(currentConfig));
+ }
+
+ /**
+ * Test that the helper method
+ * {@link WifiStateMachine#shouldEvaluateWhetherToSendExplicitlySelected(WifiConfiguration)}
+ * returns false when we connect to a different network to the last selected network.
+ */
+ @Test
+ public void testShouldEvaluateWhetherToSendExplicitlySelected_DifferentNetwork() {
+ long lastSelectedTimestamp = 45666743454L;
+ int lastSelectedNetworkId = 5;
+
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(
+ lastSelectedTimestamp
+ + WifiStateMachine.LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS - 1);
+ when(mWifiConfigManager.getLastSelectedTimeStamp()).thenReturn(lastSelectedTimestamp);
+ when(mWifiConfigManager.getLastSelectedNetwork()).thenReturn(lastSelectedNetworkId);
+
+ WifiConfiguration currentConfig = new WifiConfiguration();
+ currentConfig.networkId = lastSelectedNetworkId - 1;
+ assertFalse(mWsm.shouldEvaluateWhetherToSendExplicitlySelected(currentConfig));
+ }
+
+ /**
+ * Test that {@link WifiStateMachine#syncRequestConnectionInfo()} always returns a copy of
+ * WifiInfo.
+ */
+ @Test
+ public void testSyncRequestConnectionInfoDoesNotReturnLocalReference() {
+ WifiInfo wifiInfo = mWsm.getWifiInfo();
+ wifiInfo.setBSSID(sBSSID);
+ wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(sSSID));
+
+ WifiInfo syncWifiInfo = mWsm.syncRequestConnectionInfo();
+ assertEquals(wifiInfo.getSSID(), syncWifiInfo.getSSID());
+ assertEquals(wifiInfo.getBSSID(), syncWifiInfo.getBSSID());
+ assertFalse(wifiInfo == syncWifiInfo);
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
index 34ddf23..84de9d2 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
@@ -24,6 +24,7 @@
import android.hardware.wifi.V1_0.IWifiRttControllerEventCallback;
import android.hardware.wifi.V1_0.IWifiStaIface;
import android.hardware.wifi.V1_0.IWifiStaIfaceEventCallback;
+import android.hardware.wifi.V1_0.IfaceType;
import android.hardware.wifi.V1_0.RttCapabilities;
import android.hardware.wifi.V1_0.RttConfig;
import android.hardware.wifi.V1_0.StaApfPacketFilterCapabilities;
@@ -55,6 +56,7 @@
import android.net.wifi.WifiScanner;
import android.net.wifi.WifiSsid;
import android.net.wifi.WifiWakeReasonAndCounts;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.test.TestLooper;
import android.util.Pair;
@@ -75,8 +77,10 @@
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.Random;
+import java.util.Set;
/**
* Unit tests for {@link com.android.server.wifi.WifiVendorHal}.
@@ -98,6 +102,8 @@
@Mock
private IWifiChip mIWifiChip;
@Mock
+ private android.hardware.wifi.V1_1.IWifiChip mIWifiChipV11;
+ @Mock
private IWifiStaIface mIWifiStaIface;
@Mock
private IWifiRttController mIWifiRttController;
@@ -107,6 +113,21 @@
private WifiNative.VendorHalDeathEventHandler mVendorHalDeathHandler;
/**
+ * Spy used to return the V1_1 IWifiChip mock object to simulate the 1.1 HAL running on the
+ * device.
+ */
+ private class WifiVendorHalSpyV1_1 extends WifiVendorHal {
+ WifiVendorHalSpyV1_1(HalDeviceManager halDeviceManager, Looper looper) {
+ super(halDeviceManager, looper);
+ }
+
+ @Override
+ protected android.hardware.wifi.V1_1.IWifiChip getWifiChipForV1_1Mockable() {
+ return mIWifiChipV11;
+ }
+ }
+
+ /**
* Identity function to supply a type to its argument, which is a lambda
*/
static Answer<WifiStatus> answerWifiStatus(Answer<WifiStatus> statusLambda) {
@@ -560,7 +581,7 @@
* driven we don't have to work hard to exercise all of it.
*/
@Test
- public void testFeatureMaskTranslation() {
+ public void testStaIfaceFeatureMaskTranslation() {
int caps = (
IWifiStaIface.StaIfaceCapabilityMask.BACKGROUND_SCAN
| IWifiStaIface.StaIfaceCapabilityMask.LINK_LAYER_STATS
@@ -572,6 +593,70 @@
}
/**
+ * Test translation to WifiManager.WIFI_FEATURE_*
+ *
+ * Just do a spot-check with a few feature bits here; since the code is table-
+ * driven we don't have to work hard to exercise all of it.
+ */
+ @Test
+ public void testChipFeatureMaskTranslation() {
+ int caps = (
+ android.hardware.wifi.V1_1.IWifiChip.ChipCapabilityMask.SET_TX_POWER_LIMIT
+ | android.hardware.wifi.V1_1.IWifiChip.ChipCapabilityMask.D2D_RTT
+ | android.hardware.wifi.V1_1.IWifiChip.ChipCapabilityMask.D2AP_RTT
+ );
+ int expected = (
+ WifiManager.WIFI_FEATURE_TX_POWER_LIMIT
+ | WifiManager.WIFI_FEATURE_D2D_RTT
+ | WifiManager.WIFI_FEATURE_D2AP_RTT
+ );
+ assertEquals(expected, mWifiVendorHal.wifiFeatureMaskFromChipCapabilities(caps));
+ }
+
+ /**
+ * Test get supported features. Tests whether we coalesce information from different sources
+ * (IWifiStaIface, IWifiChip and HalDeviceManager) into the bitmask of supported features
+ * correctly.
+ */
+ @Test
+ public void testGetSupportedFeatures() throws Exception {
+ assertTrue(mWifiVendorHal.startVendorHal(true));
+
+ int staIfaceHidlCaps = (
+ IWifiStaIface.StaIfaceCapabilityMask.BACKGROUND_SCAN
+ | IWifiStaIface.StaIfaceCapabilityMask.LINK_LAYER_STATS
+ );
+ int chipHidlCaps =
+ android.hardware.wifi.V1_1.IWifiChip.ChipCapabilityMask.SET_TX_POWER_LIMIT;
+ Set<Integer> halDeviceManagerSupportedIfaces = new HashSet<Integer>() {{
+ add(IfaceType.STA);
+ add(IfaceType.P2P);
+ }};
+ int expectedFeatureSet = (
+ WifiManager.WIFI_FEATURE_SCANNER
+ | WifiManager.WIFI_FEATURE_LINK_LAYER_STATS
+ | WifiManager.WIFI_FEATURE_TX_POWER_LIMIT
+ | WifiManager.WIFI_FEATURE_INFRA
+ | WifiManager.WIFI_FEATURE_P2P
+ );
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(IWifiStaIface.getCapabilitiesCallback cb) throws RemoteException {
+ cb.onValues(mWifiStatusSuccess, staIfaceHidlCaps);
+ }
+ }).when(mIWifiStaIface).getCapabilities(any(IWifiStaIface.getCapabilitiesCallback.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(IWifiChip.getCapabilitiesCallback cb) throws RemoteException {
+ cb.onValues(mWifiStatusSuccess, chipHidlCaps);
+ }
+ }).when(mIWifiChip).getCapabilities(any(IWifiChip.getCapabilitiesCallback.class));
+ when(mHalDeviceManager.getSupportedIfaceTypes())
+ .thenReturn(halDeviceManagerSupportedIfaces);
+
+ assertEquals(expectedFeatureSet, mWifiVendorHal.getSupportedFeatureSet());
+ }
+
+ /**
* Test enablement of link layer stats after startup
*
* Request link layer stats before HAL start
@@ -1758,6 +1843,71 @@
verify(mVendorHalDeathHandler).onDeath();
}
+ /**
+ * Test the new selectTxPowerScenario HIDL method invocation. This should return failure if the
+ * HAL service is exposing the 1.0 interface.
+ */
+ @Test
+ public void testSelectTxPowerScenario() throws RemoteException {
+ assertTrue(mWifiVendorHal.startVendorHal(true));
+ // Should fail because we exposed the 1.0 IWifiChip.
+ assertFalse(
+ mWifiVendorHal.selectTxPowerScenario(WifiNative.TX_POWER_SCENARIO_VOICE_CALL));
+ verify(mIWifiChipV11, never()).selectTxPowerScenario(anyInt());
+ mWifiVendorHal.stopVendorHal();
+
+ // Now expose the 1.1 IWifiChip.
+ mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mLooper.getLooper());
+ when(mIWifiChipV11.selectTxPowerScenario(anyInt())).thenReturn(mWifiStatusSuccess);
+
+ assertTrue(mWifiVendorHal.startVendorHal(true));
+ assertTrue(
+ mWifiVendorHal.selectTxPowerScenario(WifiNative.TX_POWER_SCENARIO_VOICE_CALL));
+ verify(mIWifiChipV11).selectTxPowerScenario(
+ eq(android.hardware.wifi.V1_1.IWifiChip.TxPowerScenario.VOICE_CALL));
+ verify(mIWifiChipV11, never()).resetTxPowerScenario();
+ mWifiVendorHal.stopVendorHal();
+ }
+
+ /**
+ * Test the new resetTxPowerScenario HIDL method invocation. This should return failure if the
+ * HAL service is exposing the 1.0 interface.
+ */
+ @Test
+ public void testResetTxPowerScenario() throws RemoteException {
+ assertTrue(mWifiVendorHal.startVendorHal(true));
+ // Should fail because we exposed the 1.0 IWifiChip.
+ assertFalse(mWifiVendorHal.selectTxPowerScenario(WifiNative.TX_POWER_SCENARIO_NORMAL));
+ verify(mIWifiChipV11, never()).resetTxPowerScenario();
+ mWifiVendorHal.stopVendorHal();
+
+ // Now expose the 1.1 IWifiChip.
+ mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mLooper.getLooper());
+ when(mIWifiChipV11.resetTxPowerScenario()).thenReturn(mWifiStatusSuccess);
+
+ assertTrue(mWifiVendorHal.startVendorHal(true));
+ assertTrue(mWifiVendorHal.selectTxPowerScenario(WifiNative.TX_POWER_SCENARIO_NORMAL));
+ verify(mIWifiChipV11).resetTxPowerScenario();
+ verify(mIWifiChipV11, never()).selectTxPowerScenario(anyInt());
+ mWifiVendorHal.stopVendorHal();
+ }
+
+ /**
+ * Test the new selectTxPowerScenario HIDL method invocation with a bad scenario index.
+ */
+ @Test
+ public void testInvalidSelectTxPowerScenario() throws RemoteException {
+ // Expose the 1.1 IWifiChip.
+ mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mLooper.getLooper());
+ when(mIWifiChipV11.selectTxPowerScenario(anyInt())).thenReturn(mWifiStatusSuccess);
+
+ assertTrue(mWifiVendorHal.startVendorHal(true));
+ assertFalse(mWifiVendorHal.selectTxPowerScenario(-6));
+ verify(mIWifiChipV11, never()).selectTxPowerScenario(anyInt());
+ verify(mIWifiChipV11, never()).resetTxPowerScenario();
+ mWifiVendorHal.stopVendorHal();
+ }
+
private void startBgScan(WifiNative.ScanEventHandler eventHandler) throws Exception {
when(mIWifiStaIface.startBackgroundScan(
anyInt(), any(StaBackgroundScanParameters.class))).thenReturn(mWifiStatusSuccess);
diff --git a/tests/wifitests/src/com/android/server/wifi/WrongPasswordNotifierTest.java b/tests/wifitests/src/com/android/server/wifi/WrongPasswordNotifierTest.java
new file mode 100644
index 0000000..74bfdeb
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WrongPasswordNotifierTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 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 static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.provider.Settings;
+
+import com.android.internal.notification.SystemNotificationChannels;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.WrongPasswordNotifier}.
+ */
+public class WrongPasswordNotifierTest {
+ private static final String TEST_SSID = "Test SSID";
+
+ @Mock Context mContext;
+ @Mock Resources mResources;
+ @Mock NotificationManager mNotificationManager;
+ @Mock FrameworkFacade mFrameworkFacade;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Notification.Builder mNotificationBuilder;
+ WrongPasswordNotifier mWrongPassNotifier;
+
+ /**
+ * Sets up for unit test
+ */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getSystemService(Context.NOTIFICATION_SERVICE))
+ .thenReturn(mNotificationManager);
+ when(mContext.getResources()).thenReturn(mResources);
+ mWrongPassNotifier =
+ new WrongPasswordNotifier(mContext, mFrameworkFacade);
+ }
+
+ /**
+ * Verify that a wrong password notification will be generated/pushed when a wrong password
+ * error is detected for the current connection.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void onWrongPasswordError() throws Exception {
+ when(mFrameworkFacade.makeNotificationBuilder(any(),
+ eq(SystemNotificationChannels.NETWORK_ALERTS))).thenReturn(mNotificationBuilder);
+ mWrongPassNotifier.onWrongPasswordError(TEST_SSID);
+ verify(mNotificationManager).notify(eq(WrongPasswordNotifier.NOTIFICATION_ID), any());
+ ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+ verify(mFrameworkFacade).getActivity(
+ any(Context.class), anyInt(), intent.capture(), anyInt());
+ assertEquals(Settings.ACTION_WIFI_SETTINGS, intent.getValue().getAction());
+ assertEquals(TEST_SSID, intent.getValue().getStringExtra("wifi_start_connect_ssid"));
+ }
+
+ /**
+ * Verify that we will attempt to dismiss the wrong password notification when starting a new
+ * connection attempt with the previous connection resulting in a wrong password error.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void onNewConnectionAttemptWithPreviousWrongPasswordError() throws Exception {
+ onWrongPasswordError();
+ reset(mNotificationManager);
+
+ mWrongPassNotifier.onNewConnectionAttempt();
+ verify(mNotificationManager).cancel(any(), eq(WrongPasswordNotifier.NOTIFICATION_ID));
+ }
+
+ /**
+ * Verify that we don't attempt to dismiss the wrong password notification when starting a new
+ * connection attempt with the previous connection not resulting in a wrong password error.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void onNewConnectionAttemptWithoutPreviousWrongPasswordError() throws Exception {
+ mWrongPassNotifier.onNewConnectionAttempt();
+ verify(mNotificationManager, never()).cancel(any(), anyInt());
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
index a2e7c77..8063004 100644
--- a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
@@ -16,14 +16,17 @@
package com.android.server.wifi.aware;
-import static android.hardware.wifi.V1_0.NanDataPathChannelCfg.REQUEST_CHANNEL_SETUP;
+import static android.hardware.wifi.V1_0.NanDataPathChannelCfg.CHANNEL_NOT_REQUESTED;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyByte;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyShort;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
@@ -31,8 +34,11 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.Manifest;
import android.app.test.TestAlarmManager;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.wifi.V1_0.NanStatusType;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import android.net.NetworkFactory;
@@ -55,14 +61,16 @@
import android.net.wifi.aware.WifiAwareSession;
import android.os.Handler;
import android.os.INetworkManagementService;
+import android.os.IPowerManager;
import android.os.Message;
import android.os.Messenger;
+import android.os.PowerManager;
import android.os.Process;
import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Pair;
import com.android.internal.util.AsyncChannel;
+import com.android.server.wifi.util.WifiPermissionsWrapper;
import libcore.util.HexEncoding;
@@ -88,6 +96,7 @@
private TestLooper mMockLooper;
private Handler mMockLooperHandler;
private WifiAwareStateManager mDut;
+ @Mock private WifiAwareNativeManager mMockNativeManager;
@Mock private WifiAwareNativeApi mMockNative;
@Mock private Context mMockContext;
@Mock private IWifiAwareManager mMockAwareService;
@@ -96,7 +105,10 @@
@Mock private WifiAwareDataPathStateManager.NetworkInterfaceWrapper mMockNetworkInterface;
@Mock private IWifiAwareEventCallback mMockCallback;
@Mock IWifiAwareDiscoverySessionCallback mMockSessionCallback;
+ @Mock private WifiAwareMetrics mAwareMetricsMock;
+ @Mock private WifiPermissionsWrapper mPermissionsWrapperMock;
TestAlarmManager mAlarmManager;
+ private PowerManager mMockPowerManager;
@Rule
public ErrorCollector collector = new ErrorCollector();
@@ -112,39 +124,54 @@
when(mMockContext.getSystemService(Context.ALARM_SERVICE))
.thenReturn(mAlarmManager.getAlarmManager());
- when(mMockContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mMockCm);
-
mMockLooper = new TestLooper();
mMockLooperHandler = new Handler(mMockLooper.getLooper());
+ IPowerManager powerManagerService = mock(IPowerManager.class);
+ mMockPowerManager = new PowerManager(mMockContext, powerManagerService,
+ new Handler(mMockLooper.getLooper()));
+
+ when(mMockContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mMockCm);
+ when(mMockContext.getSystemServiceName(PowerManager.class)).thenReturn(
+ Context.POWER_SERVICE);
+ when(mMockContext.getSystemService(PowerManager.class)).thenReturn(mMockPowerManager);
+
mDut = new WifiAwareStateManager();
- mDut.setNative(mMockNative);
- mDut.start(mMockContext, mMockLooper.getLooper());
+ mDut.setNative(mMockNativeManager, mMockNative);
+ mDut.start(mMockContext, mMockLooper.getLooper(), mAwareMetricsMock,
+ mPermissionsWrapperMock);
mDut.startLate();
+ mMockLooper.dispatchAll();
when(mMockNative.getCapabilities(anyShort())).thenReturn(true);
when(mMockNative.enableAndConfigure(anyShort(), any(), anyBoolean(),
- anyBoolean())).thenReturn(true);
+ anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(true);
when(mMockNative.disable(anyShort())).thenReturn(true);
- when(mMockNative.publish(anyShort(), anyInt(), any())).thenReturn(true);
- when(mMockNative.subscribe(anyShort(), anyInt(), any()))
+ when(mMockNative.publish(anyShort(), anyByte(), any())).thenReturn(true);
+ when(mMockNative.subscribe(anyShort(), anyByte(), any()))
.thenReturn(true);
- when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(),
+ when(mMockNative.sendMessage(anyShort(), anyByte(), anyInt(), any(),
any(), anyInt())).thenReturn(true);
- when(mMockNative.stopPublish(anyShort(), anyInt())).thenReturn(true);
- when(mMockNative.stopSubscribe(anyShort(), anyInt())).thenReturn(true);
+ when(mMockNative.stopPublish(anyShort(), anyByte())).thenReturn(true);
+ when(mMockNative.stopSubscribe(anyShort(), anyByte())).thenReturn(true);
when(mMockNative.createAwareNetworkInterface(anyShort(), any())).thenReturn(true);
when(mMockNative.deleteAwareNetworkInterface(anyShort(), any())).thenReturn(true);
when(mMockNative.initiateDataPath(anyShort(), anyInt(), anyInt(), anyInt(),
- any(), any(), any(), any(),
+ any(), any(), any(), any(), anyBoolean(),
any())).thenReturn(true);
when(mMockNative.respondToDataPathRequest(anyShort(), anyBoolean(), anyInt(), any(),
- any(), any(), any())).thenReturn(true);
+ any(), any(), anyBoolean(), any())).thenReturn(true);
when(mMockNative.endDataPath(anyShort(), anyInt())).thenReturn(true);
when(mMockNetworkInterface.configureAgentProperties(any(), any(), anyInt(), any(), any(),
any())).thenReturn(true);
+ when(mMockPowerManager.isDeviceIdleMode()).thenReturn(false);
+ when(mMockPowerManager.isInteractive()).thenReturn(true);
+
+ when(mPermissionsWrapperMock.getUidPermission(eq(Manifest.permission.CONNECTIVITY_INTERNAL),
+ anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
+
installDataPathStateManagerMocks();
}
@@ -228,6 +255,96 @@
verifyNoMoreInteractions(mMockNative);
}
+ /**
+ * Validate that trying to specify a PMK without permission results in failure.
+ */
+ @Test
+ public void testDataPathPmkWithoutPermission() throws Exception {
+ final int clientId = 123;
+ final byte pubSubId = 55;
+ final byte[] pmk = "01234567890123456789012345678901".getBytes();
+ final int requestorId = 1341234;
+ final byte[] peerDiscoveryMac = HexEncoding.decode("000102030405".toCharArray(), false);
+
+ InOrder inOrder = inOrder(mMockNative, mMockCm, mMockCallback, mMockSessionCallback);
+ InOrder inOrderM = inOrder(mAwareMetricsMock);
+
+ when(mPermissionsWrapperMock.getUidPermission(eq(Manifest.permission.CONNECTIVITY_INTERNAL),
+ anyInt())).thenReturn(PackageManager.PERMISSION_DENIED);
+
+ // (0) initialize
+ DataPathEndPointInfo res = initDataPathEndPoint(clientId, pubSubId, requestorId,
+ peerDiscoveryMac, inOrder, inOrderM, false);
+
+ // (1) request network
+ NetworkRequest nr = getSessionNetworkRequest(clientId, res.mSessionId, res.mPeerHandle, pmk,
+ null, false);
+
+ Message reqNetworkMsg = Message.obtain();
+ reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK;
+ reqNetworkMsg.obj = nr;
+ reqNetworkMsg.arg1 = 0;
+ res.mMessenger.send(reqNetworkMsg);
+ mMockLooper.dispatchAll();
+
+ // failure: no interactions with connectivity manager or native manager
+ verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock);
+ }
+
+ /**
+ * Validate that if the data-interfaces are deleted while a data-path is being created, the
+ * process will terminate.
+ */
+ @Test
+ public void testDestroyNdiDuringNdpSetupResponder() throws Exception {
+ final int clientId = 123;
+ final byte pubSubId = 55;
+ final int requestorId = 1341234;
+ final byte[] peerDiscoveryMac = HexEncoding.decode("000102030405".toCharArray(), false);
+ final int ndpId = 3;
+
+ InOrder inOrder = inOrder(mMockNative, mMockCm, mMockCallback, mMockSessionCallback);
+ InOrder inOrderM = inOrder(mAwareMetricsMock);
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+
+ // (0) initialize
+ DataPathEndPointInfo res = initDataPathEndPoint(clientId, pubSubId, requestorId,
+ peerDiscoveryMac, inOrder, inOrderM, true);
+
+ // (1) request network
+ NetworkRequest nr = getSessionNetworkRequest(clientId, res.mSessionId, res.mPeerHandle,
+ null, null, true);
+
+ Message reqNetworkMsg = Message.obtain();
+ reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK;
+ reqNetworkMsg.obj = nr;
+ reqNetworkMsg.arg1 = 0;
+ res.mMessenger.send(reqNetworkMsg);
+ mMockLooper.dispatchAll();
+
+ // (2) delete interface(s)
+ mDut.deleteAllDataPathInterfaces();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).deleteAwareNetworkInterface(transactionId.capture(),
+ anyString());
+ mDut.onDeleteDataPathInterfaceResponse(transactionId.getValue(), true, 0);
+ mMockLooper.dispatchAll();
+
+ // (3) have responder receive request
+ mDut.onDataPathRequestNotification(pubSubId, peerDiscoveryMac, ndpId);
+ mMockLooper.dispatchAll();
+
+ // (4) verify that responder aborts (i.e. refuses request)
+ inOrder.verify(mMockNative).respondToDataPathRequest(transactionId.capture(), eq(false),
+ eq(ndpId), eq(""), eq(null), eq(null), eq(false), any());
+ mDut.onRespondToDataPathSetupRequestResponse(transactionId.getValue(), true, 0);
+ mMockLooper.dispatchAll();
+
+ // failure if there's further activity
+ verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock);
+ }
+
/*
* Initiator tests
*/
@@ -238,7 +355,7 @@
*/
@Test
public void testDataPathInitiatorMacTokenSuccess() throws Exception {
- testDataPathInitiatorUtility(false, true, true, true, false);
+ testDataPathInitiatorUtility(false, true, true, false, true, false);
}
/**
@@ -247,7 +364,7 @@
*/
@Test(expected = IllegalArgumentException.class)
public void testDataPathInitiatorNoMacFail() throws Exception {
- testDataPathInitiatorUtility(false, false, true, true, false);
+ testDataPathInitiatorUtility(false, false, false, true, true, false);
}
/**
@@ -256,7 +373,7 @@
*/
@Test
public void testDataPathInitiatorDirectMacTokenSuccess() throws Exception {
- testDataPathInitiatorUtility(true, true, true, true, false);
+ testDataPathInitiatorUtility(true, true, true, false, true, false);
}
/**
@@ -265,7 +382,7 @@
*/
@Test(expected = IllegalArgumentException.class)
public void testDataPathInitiatorDirectNoMacTokenFail() throws Exception {
- testDataPathInitiatorUtility(true, false, true, true, false);
+ testDataPathInitiatorUtility(true, false, false, true, true, false);
}
/**
@@ -274,7 +391,7 @@
*/
@Test(expected = IllegalArgumentException.class)
public void testDataPathInitiatorDirectNoMacNoTokenFail() throws Exception {
- testDataPathInitiatorUtility(true, false, false, true, false);
+ testDataPathInitiatorUtility(true, false, false, false, true, false);
}
/**
@@ -283,7 +400,7 @@
*/
@Test
public void testDataPathInitiatorNoConfirmationTimeoutFail() throws Exception {
- testDataPathInitiatorUtility(false, true, true, false, false);
+ testDataPathInitiatorUtility(false, true, true, false, false, false);
}
/**
@@ -292,7 +409,7 @@
*/
@Test
public void testDataPathInitiatorNoConfirmationHalFail() throws Exception {
- testDataPathInitiatorUtility(false, true, true, true, true);
+ testDataPathInitiatorUtility(false, true, false, true, true, true);
}
/**
@@ -330,7 +447,7 @@
*/
@Test
public void testDataPathResonderMacTokenSuccess() throws Exception {
- testDataPathResponderUtility(false, true, true, true);
+ testDataPathResponderUtility(false, true, true, false, true);
}
/**
@@ -339,7 +456,7 @@
*/
@Test
public void testDataPathResonderMacNoTokenSuccess() throws Exception {
- testDataPathResponderUtility(false, true, false, true);
+ testDataPathResponderUtility(false, true, false, false, true);
}
/**
@@ -348,7 +465,7 @@
*/
@Test
public void testDataPathResonderMacTokenNoPeerIdSuccess() throws Exception {
- testDataPathResponderUtility(false, false, true, true);
+ testDataPathResponderUtility(false, false, false, true, true);
}
/**
@@ -357,7 +474,7 @@
*/
@Test
public void testDataPathResonderMacTokenNoPeerIdNoTokenSuccess() throws Exception {
- testDataPathResponderUtility(false, false, false, true);
+ testDataPathResponderUtility(false, false, false, false, true);
}
/**
@@ -366,7 +483,7 @@
*/
@Test
public void testDataPathResonderDirectMacTokenSuccess() throws Exception {
- testDataPathResponderUtility(true, true, true, true);
+ testDataPathResponderUtility(true, true, true, false, true);
}
/**
@@ -375,7 +492,7 @@
*/
@Test
public void testDataPathResonderDirectMacNoTokenSuccess() throws Exception {
- testDataPathResponderUtility(true, true, false, true);
+ testDataPathResponderUtility(true, true, false, false, true);
}
/**
@@ -384,7 +501,7 @@
*/
@Test
public void testDataPathResonderDirectNoMacTokenSuccess() throws Exception {
- testDataPathResponderUtility(true, false, true, true);
+ testDataPathResponderUtility(true, false, false, true, true);
}
/**
@@ -393,7 +510,7 @@
*/
@Test
public void testDataPathResonderDirectNoMacNoTokenSuccess() throws Exception {
- testDataPathResponderUtility(true, false, false, true);
+ testDataPathResponderUtility(true, false, false, false, true);
}
/**
@@ -402,7 +519,7 @@
*/
@Test
public void testDataPathResponderNoConfirmationTimeoutFail() throws Exception {
- testDataPathResponderUtility(false, true, true, false);
+ testDataPathResponderUtility(false, true, true, false, false);
}
/**
@@ -436,21 +553,22 @@
private void testDataPathInitiatorResponderMismatchUtility(boolean doPublish) throws Exception {
final int clientId = 123;
- final int pubSubId = 11234;
+ final byte pubSubId = 55;
final int ndpId = 2;
- final byte[] pmk = "some bytes".getBytes();
- final PeerHandle peerHandle = new PeerHandle(1341234);
+ final byte[] pmk = "01234567890123456789012345678901".getBytes();
+ final int requestorId = 1341234;
final byte[] peerDiscoveryMac = HexEncoding.decode("000102030405".toCharArray(), false);
InOrder inOrder = inOrder(mMockNative, mMockCm, mMockCallback, mMockSessionCallback);
+ InOrder inOrderM = inOrder(mAwareMetricsMock);
// (0) initialize
- Pair<Integer, Messenger> res = initDataPathEndPoint(clientId, pubSubId, peerHandle,
- peerDiscoveryMac, inOrder, doPublish);
+ DataPathEndPointInfo res = initDataPathEndPoint(clientId, pubSubId, requestorId,
+ peerDiscoveryMac, inOrder, inOrderM, doPublish);
// (1) request network
- NetworkRequest nr = getSessionNetworkRequest(clientId, res.first, peerHandle, pmk,
- doPublish);
+ NetworkRequest nr = getSessionNetworkRequest(clientId, res.mSessionId, res.mPeerHandle, pmk,
+ null, doPublish);
// corrupt the network specifier: reverse the role (so it's mis-matched)
WifiAwareNetworkSpecifier ns =
@@ -471,7 +589,7 @@
reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK;
reqNetworkMsg.obj = nr;
reqNetworkMsg.arg1 = 0;
- res.second.send(reqNetworkMsg);
+ res.mMessenger.send(reqNetworkMsg);
mMockLooper.dispatchAll();
// consequences of failure:
@@ -482,30 +600,31 @@
mDut.onDataPathRequestNotification(pubSubId, peerDiscoveryMac, ndpId);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).respondToDataPathRequest(anyShort(), eq(false),
- eq(ndpId), eq(""), eq(null), eq(null), any());
+ eq(ndpId), eq(""), eq(null), eq(null), anyBoolean(), any());
}
- verifyNoMoreInteractions(mMockNative, mMockCm);
+ verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock);
}
private void testDataPathInitiatorResponderInvalidUidUtility(boolean doPublish,
boolean isUidSet) throws Exception {
final int clientId = 123;
- final int pubSubId = 11234;
+ final byte pubSubId = 56;
final int ndpId = 2;
- final byte[] pmk = "some bytes".getBytes();
- final PeerHandle peerHandle = new PeerHandle(1341234);
+ final byte[] pmk = "01234567890123456789012345678901".getBytes();
+ final int requestorId = 1341234;
final byte[] peerDiscoveryMac = HexEncoding.decode("000102030405".toCharArray(), false);
InOrder inOrder = inOrder(mMockNative, mMockCm, mMockCallback, mMockSessionCallback);
+ InOrder inOrderM = inOrder(mAwareMetricsMock);
// (0) initialize
- Pair<Integer, Messenger> res = initDataPathEndPoint(clientId, pubSubId, peerHandle,
- peerDiscoveryMac, inOrder, doPublish);
+ DataPathEndPointInfo res = initDataPathEndPoint(clientId, pubSubId, requestorId,
+ peerDiscoveryMac, inOrder, inOrderM, doPublish);
// (1) create network request
- NetworkRequest nr = getSessionNetworkRequest(clientId, res.first, peerHandle, pmk,
- doPublish);
+ NetworkRequest nr = getSessionNetworkRequest(clientId, res.mSessionId, res.mPeerHandle, pmk,
+ null, doPublish);
// (2) corrupt request's UID
WifiAwareNetworkSpecifier ns =
@@ -527,7 +646,7 @@
reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK;
reqNetworkMsg.obj = nr;
reqNetworkMsg.arg1 = 0;
- res.second.send(reqNetworkMsg);
+ res.mMessenger.send(reqNetworkMsg);
mMockLooper.dispatchAll();
// consequences of failure:
@@ -538,20 +657,21 @@
mDut.onDataPathRequestNotification(pubSubId, peerDiscoveryMac, ndpId);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).respondToDataPathRequest(anyShort(), eq(false),
- eq(ndpId), eq(""), eq(null), eq(null), any());
+ eq(ndpId), eq(""), eq(null), eq(null), anyBoolean(), any());
}
- verifyNoMoreInteractions(mMockNative, mMockCm);
+ verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock);
}
private void testDataPathInitiatorUtility(boolean useDirect, boolean provideMac,
- boolean providePmk, boolean getConfirmation, boolean immediateHalFailure)
- throws Exception {
+ boolean providePmk, boolean providePassphrase, boolean getConfirmation,
+ boolean immediateHalFailure) throws Exception {
final int clientId = 123;
- final int pubSubId = 11234;
- final PeerHandle peerHandle = new PeerHandle(1341234);
+ final byte pubSubId = 58;
+ final int requestorId = 1341234;
final int ndpId = 2;
- final byte[] pmk = "some bytes".getBytes();
+ final byte[] pmk = "01234567890123456789012345678901".getBytes();
+ final String passphrase = "some passphrase";
final String peerToken = "let's go!";
final byte[] peerDiscoveryMac = HexEncoding.decode("000102030405".toCharArray(), false);
final byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false);
@@ -559,41 +679,53 @@
ArgumentCaptor<Messenger> messengerCaptor = ArgumentCaptor.forClass(Messenger.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mMockNative, mMockCm, mMockCallback, mMockSessionCallback);
+ InOrder inOrderM = inOrder(mAwareMetricsMock);
+
+ if (!providePmk) {
+ when(mPermissionsWrapperMock.getUidPermission(
+ eq(Manifest.permission.CONNECTIVITY_INTERNAL), anyInt())).thenReturn(
+ PackageManager.PERMISSION_DENIED);
+ }
if (immediateHalFailure) {
when(mMockNative.initiateDataPath(anyShort(), anyInt(), anyInt(), anyInt(), any(),
- any(), any(), any(), any())).thenReturn(false);
+ any(), any(), any(), anyBoolean(), any())).thenReturn(false);
}
// (0) initialize
- Pair<Integer, Messenger> res = initDataPathEndPoint(clientId, pubSubId, peerHandle,
- peerDiscoveryMac, inOrder, false);
+ DataPathEndPointInfo res = initDataPathEndPoint(clientId, pubSubId, requestorId,
+ peerDiscoveryMac, inOrder, inOrderM, false);
// (1) request network
NetworkRequest nr;
if (useDirect) {
nr = getDirectNetworkRequest(clientId,
WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR,
- provideMac ? peerDiscoveryMac : null, providePmk ? pmk : null);
+ provideMac ? peerDiscoveryMac : null, providePmk ? pmk : null,
+ providePassphrase ? passphrase : null);
} else {
- nr = getSessionNetworkRequest(clientId, res.first, provideMac ? peerHandle : null,
- providePmk ? pmk : null, false);
+ nr = getSessionNetworkRequest(clientId, res.mSessionId,
+ provideMac ? res.mPeerHandle : null, providePmk ? pmk : null,
+ providePassphrase ? passphrase : null, false);
}
Message reqNetworkMsg = Message.obtain();
reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK;
reqNetworkMsg.obj = nr;
reqNetworkMsg.arg1 = 0;
- res.second.send(reqNetworkMsg);
+ res.mMessenger.send(reqNetworkMsg);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).initiateDataPath(transactionId.capture(),
- eq(useDirect ? 0 : peerHandle.peerId),
- eq(REQUEST_CHANNEL_SETUP), eq(2437), eq(peerDiscoveryMac),
- eq(sAwareInterfacePrefix + "0"), eq(pmk), eq(null), any());
+ eq(useDirect ? 0 : requestorId),
+ eq(CHANNEL_NOT_REQUESTED), anyInt(), eq(peerDiscoveryMac),
+ eq(sAwareInterfacePrefix + "0"), eq(providePmk ? pmk : null),
+ eq(providePassphrase ? passphrase : null), eq(useDirect), any());
if (immediateHalFailure) {
// short-circuit the rest of this test
- verifyNoMoreInteractions(mMockNative, mMockCm);
+ inOrderM.verify(mAwareMetricsMock).recordNdpStatus(eq(NanStatusType.INTERNAL_FAILURE),
+ eq(useDirect), anyLong());
+ verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock);
return;
}
@@ -607,6 +739,9 @@
mMockLooper.dispatchAll();
inOrder.verify(mMockCm).registerNetworkAgent(messengerCaptor.capture(), any(), any(),
any(), anyInt(), any());
+ inOrderM.verify(mAwareMetricsMock).recordNdpStatus(eq(NanStatusType.SUCCESS),
+ eq(useDirect), anyLong());
+ inOrderM.verify(mAwareMetricsMock).recordNdpCreation(anyInt(), any());
} else {
assertTrue(mAlarmManager.dispatch(
WifiAwareStateManager.HAL_DATA_PATH_CONFIRM_TIMEOUT_TAG));
@@ -614,6 +749,8 @@
inOrder.verify(mMockNative).endDataPath(transactionId.capture(), eq(ndpId));
mDut.onEndDataPathResponse(transactionId.getValue(), true, 0);
mMockLooper.dispatchAll();
+ inOrderM.verify(mAwareMetricsMock).recordNdpStatus(eq(NanStatusType.INTERNAL_FAILURE),
+ eq(useDirect), anyLong());
}
// (3) end data-path (unless didn't get confirmation)
@@ -621,7 +758,7 @@
Message endNetworkReqMsg = Message.obtain();
endNetworkReqMsg.what = NetworkFactory.CMD_CANCEL_REQUEST;
endNetworkReqMsg.obj = nr;
- res.second.send(endNetworkReqMsg);
+ res.mMessenger.send(endNetworkReqMsg);
Message endNetworkUsageMsg = Message.obtain();
endNetworkUsageMsg.what = AsyncChannel.CMD_CHANNEL_DISCONNECTED;
@@ -630,19 +767,23 @@
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).endDataPath(transactionId.capture(), eq(ndpId));
mDut.onEndDataPathResponse(transactionId.getValue(), true, 0);
+ mDut.onDataPathEndNotification(ndpId);
mMockLooper.dispatchAll();
+ inOrderM.verify(mAwareMetricsMock).recordNdpSessionDuration(anyLong());
}
- verifyNoMoreInteractions(mMockNative, mMockCm);
+ verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock);
}
private void testDataPathResponderUtility(boolean useDirect, boolean provideMac,
- boolean providePmk, boolean getConfirmation) throws Exception {
+ boolean providePmk, boolean providePassphrase, boolean getConfirmation)
+ throws Exception {
final int clientId = 123;
- final int pubSubId = 11234;
- final PeerHandle peerHandle = new PeerHandle(1341234);
+ final byte pubSubId = 60;
+ final int requestorId = 1341234;
final int ndpId = 2;
- final byte[] pmk = "some bytes".getBytes();
+ final byte[] pmk = "01234567890123456789012345678901".getBytes();
+ final String passphrase = "some passphrase";
final String peerToken = "let's go!";
final byte[] peerDiscoveryMac = HexEncoding.decode("000102030405".toCharArray(), false);
final byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false);
@@ -650,35 +791,44 @@
ArgumentCaptor<Messenger> messengerCaptor = ArgumentCaptor.forClass(Messenger.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mMockNative, mMockCm, mMockCallback, mMockSessionCallback);
+ InOrder inOrderM = inOrder(mAwareMetricsMock);
+
+ if (providePmk) {
+ when(mPermissionsWrapperMock.getUidPermission(
+ eq(Manifest.permission.CONNECTIVITY_INTERNAL), anyInt())).thenReturn(
+ PackageManager.PERMISSION_GRANTED);
+ }
// (0) initialize
- Pair<Integer, Messenger> res = initDataPathEndPoint(clientId, pubSubId, peerHandle,
- peerDiscoveryMac, inOrder, true);
+ DataPathEndPointInfo res = initDataPathEndPoint(clientId, pubSubId, requestorId,
+ peerDiscoveryMac, inOrder, inOrderM, true);
// (1) request network
NetworkRequest nr;
if (useDirect) {
nr = getDirectNetworkRequest(clientId,
WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER,
- provideMac ? peerDiscoveryMac : null, providePmk ? pmk : null);
+ provideMac ? peerDiscoveryMac : null, providePmk ? pmk : null,
+ providePassphrase ? passphrase : null);
} else {
- nr = getSessionNetworkRequest(clientId, res.first, provideMac ? peerHandle : null,
- providePmk ? pmk : null, true);
+ nr = getSessionNetworkRequest(clientId, res.mSessionId,
+ provideMac ? res.mPeerHandle : null, providePmk ? pmk : null,
+ providePassphrase ? passphrase : null, true);
}
Message reqNetworkMsg = Message.obtain();
reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK;
reqNetworkMsg.obj = nr;
reqNetworkMsg.arg1 = 0;
- res.second.send(reqNetworkMsg);
+ res.mMessenger.send(reqNetworkMsg);
mMockLooper.dispatchAll();
// (2) get request & respond
mDut.onDataPathRequestNotification(pubSubId, peerDiscoveryMac, ndpId);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).respondToDataPathRequest(transactionId.capture(), eq(true),
- eq(ndpId), eq(sAwareInterfacePrefix + "0"), eq(providePmk ? pmk : null), eq(null),
- any());
+ eq(ndpId), eq(sAwareInterfacePrefix + "0"), eq(providePmk ? pmk : null),
+ eq(providePassphrase ? passphrase : null), eq(useDirect), any());
mDut.onRespondToDataPathSetupRequestResponse(transactionId.getValue(), true, 0);
mMockLooper.dispatchAll();
@@ -689,6 +839,9 @@
mMockLooper.dispatchAll();
inOrder.verify(mMockCm).registerNetworkAgent(messengerCaptor.capture(), any(), any(),
any(), anyInt(), any());
+ inOrderM.verify(mAwareMetricsMock).recordNdpStatus(eq(NanStatusType.SUCCESS),
+ eq(useDirect), anyLong());
+ inOrderM.verify(mAwareMetricsMock).recordNdpCreation(anyInt(), any());
} else {
assertTrue(mAlarmManager.dispatch(
WifiAwareStateManager.HAL_DATA_PATH_CONFIRM_TIMEOUT_TAG));
@@ -696,6 +849,8 @@
inOrder.verify(mMockNative).endDataPath(transactionId.capture(), eq(ndpId));
mDut.onEndDataPathResponse(transactionId.getValue(), true, 0);
mMockLooper.dispatchAll();
+ inOrderM.verify(mAwareMetricsMock).recordNdpStatus(eq(NanStatusType.INTERNAL_FAILURE),
+ eq(useDirect), anyLong());
}
// (4) end data-path (unless didn't get confirmation)
@@ -703,7 +858,7 @@
Message endNetworkMsg = Message.obtain();
endNetworkMsg.what = NetworkFactory.CMD_CANCEL_REQUEST;
endNetworkMsg.obj = nr;
- res.second.send(endNetworkMsg);
+ res.mMessenger.send(endNetworkMsg);
Message endNetworkUsageMsg = Message.obtain();
endNetworkUsageMsg.what = AsyncChannel.CMD_CHANNEL_DISCONNECTED;
@@ -712,10 +867,12 @@
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).endDataPath(transactionId.capture(), eq(ndpId));
mDut.onEndDataPathResponse(transactionId.getValue(), true, 0);
+ mDut.onDataPathEndNotification(ndpId);
mMockLooper.dispatchAll();
+ inOrderM.verify(mAwareMetricsMock).recordNdpSessionDuration(anyLong());
}
- verifyNoMoreInteractions(mMockNative, mMockCm);
+ verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock);
}
private void installDataPathStateManagerMocks() throws Exception {
@@ -733,7 +890,7 @@
}
private NetworkRequest getSessionNetworkRequest(int clientId, int sessionId,
- PeerHandle peerHandle, byte[] pmk, boolean doPublish)
+ PeerHandle peerHandle, byte[] pmk, String passphrase, boolean doPublish)
throws Exception {
final WifiAwareManager mgr = new WifiAwareManager(mMockContext, mMockAwareService);
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
@@ -781,10 +938,13 @@
}
NetworkSpecifier ns;
- if (pmk == null) {
+ if (pmk == null && passphrase == null) {
ns = discoverySession.getValue().createNetworkSpecifierOpen(peerHandle);
- } else {
+ } else if (passphrase == null) {
ns = discoverySession.getValue().createNetworkSpecifierPmk(peerHandle, pmk);
+ } else {
+ ns = discoverySession.getValue().createNetworkSpecifierPassphrase(peerHandle,
+ passphrase);
}
NetworkCapabilities nc = new NetworkCapabilities();
@@ -801,7 +961,7 @@
}
private NetworkRequest getDirectNetworkRequest(int clientId, int role, byte[] peer,
- byte[] pmk) throws Exception {
+ byte[] pmk, String passphrase) throws Exception {
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
final WifiAwareManager mgr = new WifiAwareManager(mMockContext, mMockAwareService);
@@ -820,10 +980,12 @@
verify(mockCallback).onAttached(sessionCaptor.capture());
NetworkSpecifier ns;
- if (pmk == null) {
+ if (pmk == null && passphrase == null) {
ns = sessionCaptor.getValue().createNetworkSpecifierOpen(role, peer);
- } else {
+ } else if (passphrase == null) {
ns = sessionCaptor.getValue().createNetworkSpecifierPmk(role, peer, pmk);
+ } else {
+ ns = sessionCaptor.getValue().createNetworkSpecifierPassphrase(role, peer, passphrase);
}
NetworkCapabilities nc = new NetworkCapabilities();
nc.clearAll();
@@ -838,8 +1000,8 @@
return new NetworkRequest(nc, 0, 0, NetworkRequest.Type.REQUEST);
}
- private Pair<Integer, Messenger> initDataPathEndPoint(int clientId, int pubSubId,
- PeerHandle peerHandle, byte[] peerDiscoveryMac, InOrder inOrder,
+ private DataPathEndPointInfo initDataPathEndPoint(int clientId, byte pubSubId,
+ int requestorId, byte[] peerDiscoveryMac, InOrder inOrder, InOrder inOrderM,
boolean doPublish)
throws Exception {
final int pid = 2000;
@@ -854,6 +1016,7 @@
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Messenger> messengerCaptor = ArgumentCaptor.forClass(Messenger.class);
ArgumentCaptor<String> strCaptor = ArgumentCaptor.forClass(String.class);
@@ -869,9 +1032,23 @@
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), capabilities);
mMockLooper.dispatchAll();
- // (2) enable usage (creates interfaces)
+ // (2) enable usage
mDut.enableUsage();
mMockLooper.dispatchAll();
+ inOrderM.verify(mAwareMetricsMock).recordEnableUsage();
+
+ // (3) create client & session & rx message
+ mDut.connect(clientId, Process.myUid(), pid, callingPackage, mMockCallback, configRequest,
+ false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(false), eq(true), eq(true), eq(false));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockCallback).onConnectSuccess(clientId);
+ inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(Process.myUid()), eq(false),
+ any());
+
inOrder.verify(mMockNative).createAwareNetworkInterface(transactionId.capture(),
strCaptor.capture());
collector.checkThat("interface created -- 0", sAwareInterfacePrefix + 0,
@@ -879,15 +1056,6 @@
mDut.onCreateDataPathInterfaceResponse(transactionId.getValue(), true, 0);
mMockLooper.dispatchAll();
- // (3) create client & session & rx message
- mDut.connect(clientId, Process.myUid(), pid, callingPackage, mMockCallback, configRequest,
- false);
- mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
- eq(configRequest), eq(false), eq(true));
- mDut.onConfigSuccessResponse(transactionId.getValue());
- mMockLooper.dispatchAll();
- inOrder.verify(mMockCallback).onConnectSuccess(clientId);
if (doPublish) {
mDut.publish(clientId, publishConfig, mMockSessionCallback);
} else {
@@ -895,20 +1063,39 @@
}
mMockLooper.dispatchAll();
if (doPublish) {
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq((byte) 0),
+ eq(publishConfig));
} else {
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0),
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
eq(subscribeConfig));
}
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), doPublish, pubSubId);
mMockLooper.dispatchAll();
inOrder.verify(mMockSessionCallback).onSessionStarted(sessionId.capture());
- mDut.onMessageReceivedNotification(pubSubId, peerHandle.peerId, peerDiscoveryMac,
+ inOrderM.verify(mAwareMetricsMock).recordDiscoverySession(eq(Process.myUid()),
+ eq(doPublish), any());
+ inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(Process.myUid(),
+ NanStatusType.SUCCESS, doPublish);
+
+ mDut.onMessageReceivedNotification(pubSubId, requestorId, peerDiscoveryMac,
someMsg.getBytes());
mMockLooper.dispatchAll();
- inOrder.verify(mMockSessionCallback).onMessageReceived(peerHandle.peerId,
- someMsg.getBytes());
+ inOrder.verify(mMockSessionCallback).onMessageReceived(peerIdCaptor.capture(),
+ eq(someMsg.getBytes()));
- return new Pair<>(sessionId.getValue(), messengerCaptor.getValue());
+ return new DataPathEndPointInfo(sessionId.getValue(), peerIdCaptor.getValue(),
+ messengerCaptor.getValue());
+ }
+
+ private static class DataPathEndPointInfo {
+ int mSessionId;
+ PeerHandle mPeerHandle;
+ Messenger mMessenger;
+
+ DataPathEndPointInfo(int sessionId, int peerId, Messenger messenger) {
+ mSessionId = sessionId;
+ mPeerHandle = new PeerHandle(peerId);
+ mMessenger = messenger;
+ }
}
}
diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareMetricsTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareMetricsTest.java
new file mode 100644
index 0000000..8cd1e10
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareMetricsTest.java
@@ -0,0 +1,672 @@
+/*
+ * Copyright (C) 2017 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.aware;
+
+import static com.android.server.wifi.aware.WifiAwareMetrics.addNanHalStatusToHistogram;
+import static com.android.server.wifi.aware.WifiAwareMetrics.histogramToProtoArray;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.mockito.Mockito.when;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.hardware.wifi.V1_0.NanStatusType;
+import android.net.wifi.aware.WifiAwareNetworkSpecifier;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import com.android.server.wifi.Clock;
+import com.android.server.wifi.nano.WifiMetricsProto;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ErrorCollector;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Unit test harness for WifiAwareMetrics
+ */
+public class WifiAwareMetricsTest {
+ @Mock Clock mClock;
+ @Mock private Context mMockContext;
+ @Mock private AppOpsManager mMockAppOpsManager;
+ @Rule public ErrorCollector collector = new ErrorCollector();
+
+ private WifiAwareMetrics mDut;
+
+ // Histogram definition: start[i] = b + p * m^i with s sub-buckets, i=0,...,n-1
+
+ /**
+ * Histogram of following buckets, start[i] = 0 + 1 * 10^i with 9 sub-buckets, i=0,...,5
+ * 1 - 10: 9 sub-buckets each of width 1
+ * 10 - 100: 10
+ * 100 - 10e3: 10^2
+ * 10e3 - 10e4: 10^3
+ * 10e4 - 10e5: 10^4
+ * 10e5 - 10e6: 10^5
+ */
+ private static final WifiAwareMetrics.HistParms HIST1 = new WifiAwareMetrics.HistParms(0, 1,
+ 10, 9, 6);
+
+ /**
+ * Histogram of following buckets, start[i] = -20 + 2 * 5^i with 40 sub-buckets, i=0,...,2
+ * -18 - -10: 40 sub-bucket each of width 0.2
+ * -10 - 30: 1
+ * 30 - 230: 5
+ */
+ private static final WifiAwareMetrics.HistParms HIST2 = new WifiAwareMetrics.HistParms(-20, 2,
+ 5, 40, 3);
+
+ /**
+ * Pre-test configuration. Initialize and install mocks.
+ */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mMockAppOpsManager);
+ setTime(0);
+
+ mDut = new WifiAwareMetrics(mClock);
+ }
+
+ /**
+ * Validates that recordEnableUsage() and recordDisableUsage() record valid metrics.
+ */
+ @Test
+ public void testEnableDisableUsageMetrics() {
+ WifiMetricsProto.WifiAwareLog log;
+
+ // create 2 records
+ setTime(5);
+ mDut.recordEnableUsage();
+ setTime(10);
+ mDut.recordDisableUsage();
+ setTime(11);
+ mDut.recordEnableUsage();
+ setTime(12);
+ mDut.recordDisableUsage();
+
+ setTime(14);
+ log = mDut.consolidateProto();
+ collector.checkThat(countAllHistogramSamples(log.histogramAwareAvailableDurationMs),
+ equalTo(2));
+ validateProtoHistBucket("Duration[0] #1", log.histogramAwareAvailableDurationMs[0], 1, 2,
+ 1);
+ validateProtoHistBucket("Duration[1] #1", log.histogramAwareAvailableDurationMs[1], 5, 6,
+ 1);
+ collector.checkThat(log.availableTimeMs, equalTo(6L));
+
+ // create another partial record
+ setTime(15);
+ mDut.recordEnableUsage();
+
+ setTime(17);
+ log = mDut.consolidateProto();
+ collector.checkThat(countAllHistogramSamples(log.histogramAwareAvailableDurationMs),
+ equalTo(2));
+ validateProtoHistBucket("Duration[0] #2", log.histogramAwareAvailableDurationMs[0], 1, 2,
+ 1);
+ validateProtoHistBucket("Duration[1] #2", log.histogramAwareAvailableDurationMs[1], 5, 6,
+ 1);
+ collector.checkThat(log.availableTimeMs, equalTo(8L)); // the partial record of 2ms
+
+
+ // clear and continue that partial record (verify completed)
+ mDut.clear();
+ setTime(23);
+ mDut.recordDisableUsage();
+
+ log = mDut.consolidateProto();
+ collector.checkThat(countAllHistogramSamples(log.histogramAwareAvailableDurationMs),
+ equalTo(1));
+ validateProtoHistBucket("Duration[0] #3", log.histogramAwareAvailableDurationMs[0], 8, 9,
+ 1);
+ collector.checkThat(log.availableTimeMs, equalTo(6L)); // the remnant record of 6ms
+
+ // clear and verify empty records
+ mDut.clear();
+ log = mDut.consolidateProto();
+ collector.checkThat(countAllHistogramSamples(log.histogramAwareAvailableDurationMs),
+ equalTo(0));
+ }
+
+ /**
+ * Validates that recordEnableAware() and recordDisableAware() record valid metrics.
+ */
+ @Test
+ public void testEnableDisableAwareMetrics() {
+ WifiMetricsProto.WifiAwareLog log;
+
+ // create 2 records
+ setTime(5);
+ mDut.recordEnableAware();
+ setTime(10);
+ mDut.recordDisableAware();
+ setTime(11);
+ mDut.recordEnableAware();
+ setTime(12);
+ mDut.recordDisableAware();
+
+ setTime(14);
+ log = mDut.consolidateProto();
+ collector.checkThat(countAllHistogramSamples(log.histogramAwareEnabledDurationMs),
+ equalTo(2));
+ validateProtoHistBucket("Duration[0] #1", log.histogramAwareEnabledDurationMs[0], 1, 2,
+ 1);
+ validateProtoHistBucket("Duration[1] #1", log.histogramAwareEnabledDurationMs[1], 5, 6,
+ 1);
+ collector.checkThat(log.enabledTimeMs, equalTo(6L));
+
+ // create another partial record
+ setTime(15);
+ mDut.recordEnableAware();
+
+ setTime(17);
+ log = mDut.consolidateProto();
+ collector.checkThat(countAllHistogramSamples(log.histogramAwareEnabledDurationMs),
+ equalTo(2));
+ validateProtoHistBucket("Duration[0] #2", log.histogramAwareEnabledDurationMs[0], 1, 2,
+ 1);
+ validateProtoHistBucket("Duration[1] #2", log.histogramAwareEnabledDurationMs[1], 5, 6,
+ 1);
+ collector.checkThat(log.enabledTimeMs, equalTo(8L)); // the partial record of 2ms
+
+
+ // clear and continue that partial record (verify completed)
+ mDut.clear();
+ setTime(23);
+ mDut.recordDisableAware();
+
+ log = mDut.consolidateProto();
+ collector.checkThat(countAllHistogramSamples(log.histogramAwareEnabledDurationMs),
+ equalTo(1));
+ validateProtoHistBucket("Duration[0] #3", log.histogramAwareEnabledDurationMs[0], 8, 9,
+ 1);
+ collector.checkThat(log.enabledTimeMs, equalTo(6L)); // the remnant record of 6ms
+
+ // clear and verify empty records
+ mDut.clear();
+ log = mDut.consolidateProto();
+ collector.checkThat(countAllHistogramSamples(log.histogramAwareEnabledDurationMs),
+ equalTo(0));
+ }
+
+ @Test
+ public void testAttachSessionMetrics() {
+ final int uid1 = 1005;
+ final int uid2 = 1006;
+ final SparseArray<WifiAwareClientState> clients = new SparseArray<>();
+ WifiMetricsProto.WifiAwareLog log;
+
+ setTime(5);
+
+ // uid1: session 1
+ clients.put(10,
+ new WifiAwareClientState(mMockContext, 10, uid1, 0, null, null, null, false,
+ mClock.getElapsedSinceBootMillis()));
+ mDut.recordAttachSession(uid1, false, clients);
+
+ // uid1: session 2
+ clients.put(11,
+ new WifiAwareClientState(mMockContext, 11, uid1, 0, null, null, null, false,
+ mClock.getElapsedSinceBootMillis()));
+ mDut.recordAttachSession(uid1, false, clients);
+
+ // uid2: session 1
+ clients.put(12,
+ new WifiAwareClientState(mMockContext, 12, uid2, 0, null, null, null, false,
+ mClock.getElapsedSinceBootMillis()));
+ mDut.recordAttachSession(uid2, false, clients);
+
+ // uid2: session 2
+ clients.put(13,
+ new WifiAwareClientState(mMockContext, 13, uid2, 0, null, null, null, true,
+ mClock.getElapsedSinceBootMillis()));
+ mDut.recordAttachSession(uid2, true, clients);
+
+ // uid2: delete session 1
+ setTime(10);
+ mDut.recordAttachSessionDuration(clients.get(12).getCreationTime());
+ clients.delete(12);
+
+ // uid2: delete session 2
+ setTime(15);
+ mDut.recordAttachSessionDuration(clients.get(13).getCreationTime());
+ clients.delete(13);
+
+ // uid2: session 3
+ clients.put(14,
+ new WifiAwareClientState(mMockContext, 14, uid2, 0, null, null, null, false,
+ mClock.getElapsedSinceBootMillis()));
+ mDut.recordAttachSession(uid2, false, clients);
+
+ // a few failures
+ mDut.recordAttachStatus(NanStatusType.INTERNAL_FAILURE);
+ mDut.recordAttachStatus(NanStatusType.INTERNAL_FAILURE);
+ mDut.recordAttachStatus(-5); // invalid
+
+ // verify
+ log = mDut.consolidateProto();
+
+ collector.checkThat("numApps", log.numApps, equalTo(2));
+ collector.checkThat("numAppsUsingIdentityCallback", log.numAppsUsingIdentityCallback,
+ equalTo(1));
+ collector.checkThat("maxConcurrentAttachSessionsInApp",
+ log.maxConcurrentAttachSessionsInApp, equalTo(2));
+ collector.checkThat("histogramAttachSessionStatus.length",
+ log.histogramAttachSessionStatus.length, equalTo(3)); // 3 buckets
+ collector.checkThat("histogramAttachDurationMs.length",
+ log.histogramAttachDurationMs.length, equalTo(2));
+ validateProtoHistBucket("Duration[0]", log.histogramAttachDurationMs[0], 5, 6, 1);
+ validateProtoHistBucket("Duration[1]", log.histogramAttachDurationMs[1], 10, 20, 1);
+ }
+
+ @Test
+ public void testDiscoverySessionMetrics() {
+ final int uid1 = 1005;
+ final int uid2 = 1006;
+ final SparseArray<WifiAwareClientState> clients = new SparseArray<>();
+ WifiMetricsProto.WifiAwareLog log;
+
+ setTime(5);
+ WifiAwareClientState client1 = new WifiAwareClientState(mMockContext, 10, uid1, 0, null,
+ null, null, false, 0);
+ WifiAwareClientState client2 = new WifiAwareClientState(mMockContext, 11, uid2, 0, null,
+ null, null, false, 0);
+ clients.put(10, client1);
+ clients.put(11, client2);
+
+ // uid1: publish session 1
+ client1.addSession(new WifiAwareDiscoverySessionState(null, 100, (byte) 0, null, true,
+ mClock.getElapsedSinceBootMillis()));
+ mDut.recordDiscoverySession(uid1, true, clients);
+ mDut.recordDiscoveryStatus(uid1, NanStatusType.SUCCESS, true);
+
+ // uid1: publish session 2
+ client1.addSession(new WifiAwareDiscoverySessionState(null, 101, (byte) 0, null, true,
+ mClock.getElapsedSinceBootMillis()));
+ mDut.recordDiscoverySession(uid1, true, clients);
+ mDut.recordDiscoveryStatus(uid1, NanStatusType.SUCCESS, true);
+
+ // uid2: subscribe session 1
+ client2.addSession(new WifiAwareDiscoverySessionState(null, 102, (byte) 0, null, false,
+ mClock.getElapsedSinceBootMillis()));
+ mDut.recordDiscoverySession(uid2, false, clients);
+ mDut.recordDiscoveryStatus(uid2, NanStatusType.SUCCESS, false);
+
+ // uid2: publish session 2
+ client2.addSession(new WifiAwareDiscoverySessionState(null, 103, (byte) 0, null, true,
+ mClock.getElapsedSinceBootMillis()));
+ mDut.recordDiscoverySession(uid2, false, clients);
+ mDut.recordDiscoveryStatus(uid2, NanStatusType.SUCCESS, false);
+
+ // uid1: delete session 1
+ setTime(10);
+ mDut.recordDiscoverySessionDuration(client1.getSession(100).getCreationTime(),
+ client1.getSession(100).isPublishSession());
+ client1.removeSession(100);
+
+ // uid2: delete session 1
+ setTime(15);
+ mDut.recordDiscoverySessionDuration(client2.getSession(102).getCreationTime(),
+ client2.getSession(102).isPublishSession());
+ client2.removeSession(102);
+
+ // uid2: subscribe session 3
+ mDut.recordDiscoverySession(uid2, false, clients);
+ client2.addSession(new WifiAwareDiscoverySessionState(null, 104, (byte) 0, null, false,
+ mClock.getElapsedSinceBootMillis()));
+
+ // a few failures
+ mDut.recordDiscoveryStatus(uid1, NanStatusType.INTERNAL_FAILURE, true);
+ mDut.recordDiscoveryStatus(uid2, NanStatusType.INTERNAL_FAILURE, false);
+ mDut.recordDiscoveryStatus(uid2, NanStatusType.NO_RESOURCES_AVAILABLE, false);
+ mDut.recordAttachStatus(-5); // invalid
+
+ // verify
+ log = mDut.consolidateProto();
+
+ collector.checkThat("maxConcurrentPublishInApp", log.maxConcurrentPublishInApp, equalTo(2));
+ collector.checkThat("maxConcurrentSubscribeInApp", log.maxConcurrentSubscribeInApp,
+ equalTo(1));
+ collector.checkThat("maxConcurrentDiscoverySessionsInApp",
+ log.maxConcurrentDiscoverySessionsInApp, equalTo(2));
+ collector.checkThat("maxConcurrentPublishInSystem", log.maxConcurrentPublishInSystem,
+ equalTo(3));
+ collector.checkThat("maxConcurrentSubscribeInSystem", log.maxConcurrentSubscribeInSystem,
+ equalTo(1));
+ collector.checkThat("maxConcurrentDiscoverySessionsInSystem",
+ log.maxConcurrentDiscoverySessionsInSystem, equalTo(4));
+ collector.checkThat("histogramPublishStatus.length",
+ log.histogramPublishStatus.length, equalTo(2)); // 2 buckets
+ collector.checkThat("histogramSubscribeStatus.length",
+ log.histogramSubscribeStatus.length, equalTo(3)); // 3 buckets
+ collector.checkThat("numAppsWithDiscoverySessionFailureOutOfResources",
+ log.numAppsWithDiscoverySessionFailureOutOfResources, equalTo(1));
+ validateProtoHistBucket("Publish Duration[0]", log.histogramPublishSessionDurationMs[0], 5,
+ 6, 1);
+ validateProtoHistBucket("Subscribe Duration[0]", log.histogramSubscribeSessionDurationMs[0],
+ 10, 20, 1);
+ }
+
+ /**
+ * Validate the data-path (NDP & NDI) metrics.
+ */
+ @Test
+ public void testDataPathMetrics() {
+ final int uid1 = 1005;
+ final int uid2 = 1006;
+ final String ndi0 = "aware_data0";
+ final String ndi1 = "aware_data1";
+ Map<WifiAwareNetworkSpecifier, WifiAwareDataPathStateManager.AwareNetworkRequestInformation>
+ networkRequestCache = new HashMap<>();
+ WifiMetricsProto.WifiAwareLog log;
+
+ setTime(5);
+
+ // uid1: ndp (non-secure) on ndi0
+ addNetworkInfoToCache(networkRequestCache, 10, uid1, ndi0, null);
+ mDut.recordNdpCreation(uid1, networkRequestCache);
+ setTime(7); // 2ms creation time
+ mDut.recordNdpStatus(NanStatusType.SUCCESS, false, 5);
+
+ // uid2: ndp (non-secure) on ndi0
+ WifiAwareNetworkSpecifier ns = addNetworkInfoToCache(networkRequestCache, 11, uid2, ndi0,
+ null);
+ mDut.recordNdpCreation(uid2, networkRequestCache);
+ setTime(10); // 3 ms creation time
+ mDut.recordNdpStatus(NanStatusType.SUCCESS, false, 7);
+
+ // uid2: ndp (secure) on ndi1 (OOB)
+ addNetworkInfoToCache(networkRequestCache, 12, uid2, ndi1, "passphrase of some kind");
+ mDut.recordNdpCreation(uid2, networkRequestCache);
+ setTime(25); // 15 ms creation time
+ mDut.recordNdpStatus(NanStatusType.SUCCESS, true, 10);
+
+ // uid2: ndp (secure) on ndi0 (OOB)
+ addNetworkInfoToCache(networkRequestCache, 13, uid2, ndi0, "super secret password");
+ mDut.recordNdpCreation(uid2, networkRequestCache);
+ setTime(36); // 11 ms creation time
+ mDut.recordNdpStatus(NanStatusType.SUCCESS, true, 25);
+
+ // uid2: delete the first NDP
+ networkRequestCache.remove(ns);
+
+ // uid2: ndp (non-secure) on ndi0
+ addNetworkInfoToCache(networkRequestCache, 14, uid2, ndi0, null);
+ mDut.recordNdpCreation(uid2, networkRequestCache);
+ setTime(37); // 1 ms creation time!
+ mDut.recordNdpStatus(NanStatusType.SUCCESS, false, 36);
+
+ // a few error codes
+ mDut.recordNdpStatus(NanStatusType.INTERNAL_FAILURE, false, 0);
+ mDut.recordNdpStatus(NanStatusType.INTERNAL_FAILURE, false, 0);
+ mDut.recordNdpStatus(NanStatusType.NO_RESOURCES_AVAILABLE, false, 0);
+
+ // and some durations
+ setTime(150);
+ mDut.recordNdpSessionDuration(7); // 143ms
+ mDut.recordNdpSessionDuration(10); // 140ms
+ mDut.recordNdpSessionDuration(25); // 125ms
+ mDut.recordNdpSessionDuration(140); // 10ms
+
+ //verify
+ log = mDut.consolidateProto();
+
+ collector.checkThat("maxConcurrentNdiInApp", log.maxConcurrentNdiInApp, equalTo(2));
+ collector.checkThat("maxConcurrentNdiInSystem", log.maxConcurrentNdiInSystem, equalTo(2));
+ collector.checkThat("maxConcurrentNdpInApp", log.maxConcurrentNdpInApp, equalTo(3));
+ collector.checkThat("maxConcurrentNdpInSystem", log.maxConcurrentNdpInSystem, equalTo(4));
+ collector.checkThat("maxConcurrentSecureNdpInApp", log.maxConcurrentSecureNdpInApp,
+ equalTo(2));
+ collector.checkThat("maxConcurrentSecureNdpInSystem", log.maxConcurrentSecureNdpInSystem,
+ equalTo(2));
+ collector.checkThat("maxConcurrentNdpPerNdi", log.maxConcurrentNdpPerNdi, equalTo(3));
+ collector.checkThat("histogramRequestNdpStatus.length",
+ log.histogramRequestNdpStatus.length, equalTo(3));
+ collector.checkThat("histogramRequestNdpOobStatus.length",
+ log.histogramRequestNdpOobStatus.length, equalTo(1));
+
+ collector.checkThat("ndpCreationTimeMsMin", log.ndpCreationTimeMsMin, equalTo(1L));
+ collector.checkThat("ndpCreationTimeMsMax", log.ndpCreationTimeMsMax, equalTo(15L));
+ collector.checkThat("ndpCreationTimeMsSum", log.ndpCreationTimeMsSum, equalTo(32L));
+ collector.checkThat("ndpCreationTimeMsSumOfSq", log.ndpCreationTimeMsSumOfSq,
+ equalTo(360L));
+ collector.checkThat("ndpCreationTimeMsNumSamples", log.ndpCreationTimeMsNumSamples,
+ equalTo(5L));
+ validateProtoHistBucket("Creation[0]", log.histogramNdpCreationTimeMs[0], 1, 2, 1);
+ validateProtoHistBucket("Creation[1]", log.histogramNdpCreationTimeMs[1], 2, 3, 1);
+ validateProtoHistBucket("Creation[2]", log.histogramNdpCreationTimeMs[2], 3, 4, 1);
+ validateProtoHistBucket("Creation[3]", log.histogramNdpCreationTimeMs[3], 10, 20, 2);
+
+ validateProtoHistBucket("Duration[0]", log.histogramNdpSessionDurationMs[0], 10, 20, 1);
+ validateProtoHistBucket("Duration[1]", log.histogramNdpSessionDurationMs[1], 100, 200, 3);
+ }
+
+ /**
+ * Validate that the histogram configuration is initialized correctly: bucket starting points
+ * and sub-bucket widths.
+ */
+ @Test
+ public void testHistParamInit() {
+ collector.checkThat("HIST1.mLog", HIST1.mLog, equalTo(Math.log(10)));
+ collector.checkThat("HIST1.bb[0]", HIST1.bb[0], equalTo(1.0));
+ collector.checkThat("HIST1.bb[1]", HIST1.bb[1], equalTo(10.0));
+ collector.checkThat("HIST1.bb[2]", HIST1.bb[2], equalTo(100.0));
+ collector.checkThat("HIST1.bb[3]", HIST1.bb[3], equalTo(1000.0));
+ collector.checkThat("HIST1.bb[4]", HIST1.bb[4], equalTo(10000.0));
+ collector.checkThat("HIST1.bb[5]", HIST1.bb[5], equalTo(100000.0));
+ collector.checkThat("HIST1.sbw[0]", HIST1.sbw[0], equalTo(1.0));
+ collector.checkThat("HIST1.sbw[1]", HIST1.sbw[1], equalTo(10.0));
+ collector.checkThat("HIST1.sbw[2]", HIST1.sbw[2], equalTo(100.0));
+ collector.checkThat("HIST1.sbw[3]", HIST1.sbw[3], equalTo(1000.0));
+ collector.checkThat("HIST1.sbw[4]", HIST1.sbw[4], equalTo(10000.0));
+ collector.checkThat("HIST1.sbw[5]", HIST1.sbw[5], equalTo(100000.0));
+
+ collector.checkThat("HIST2.mLog", HIST1.mLog, equalTo(Math.log(10)));
+ collector.checkThat("HIST2.bb[0]", HIST2.bb[0], equalTo(-18.0));
+ collector.checkThat("HIST2.bb[1]", HIST2.bb[1], equalTo(-10.0));
+ collector.checkThat("HIST2.bb[2]", HIST2.bb[2], equalTo(30.0));
+ collector.checkThat("HIST2.sbw[0]", HIST2.sbw[0], equalTo(0.2));
+ collector.checkThat("HIST2.sbw[1]", HIST2.sbw[1], equalTo(1.0));
+ collector.checkThat("HIST2.sbw[2]", HIST2.sbw[2], equalTo(5.0));
+ }
+
+ /**
+ * Validate that a set of values are bucketed correctly into the histogram, and that they are
+ * converted to a primitive proto-buffer array correctly.
+ */
+ @Test
+ public void testHistBucketing() {
+ SparseIntArray hist1 = new SparseIntArray();
+ SparseIntArray hist2 = new SparseIntArray();
+
+ bucketValueAndVerify("HIST1: x=", -5, hist1, HIST1, 0, 1);
+ bucketValueAndVerify("HIST1: x=", 0, hist1, HIST1, 0, 2);
+ bucketValueAndVerify("HIST1: x=", 1, hist1, HIST1, 0, 3);
+ bucketValueAndVerify("HIST1: x=", 9, hist1, HIST1, 8, 1);
+ bucketValueAndVerify("HIST1: x=", 10, hist1, HIST1, 9, 1);
+ bucketValueAndVerify("HIST1: x=", 99, hist1, HIST1, 17, 1);
+ bucketValueAndVerify("HIST1: x=", 100, hist1, HIST1, 18, 1);
+ bucketValueAndVerify("HIST1: x=", 989, hist1, HIST1, 26, 1);
+ bucketValueAndVerify("HIST1: x=", 990, hist1, HIST1, 26, 2);
+ bucketValueAndVerify("HIST1: x=", 999, hist1, HIST1, 26, 3);
+ bucketValueAndVerify("HIST1: x=", 1000, hist1, HIST1, 27, 1);
+ bucketValueAndVerify("HIST1: x=", 9899, hist1, HIST1, 35, 1);
+ bucketValueAndVerify("HIST1: x=", 9900, hist1, HIST1, 35, 2);
+ bucketValueAndVerify("HIST1: x=", 9999, hist1, HIST1, 35, 3);
+ bucketValueAndVerify("HIST1: x=", 10000, hist1, HIST1, 36, 1);
+ bucketValueAndVerify("HIST1: x=", 98999, hist1, HIST1, 44, 1);
+ bucketValueAndVerify("HIST1: x=", 99000, hist1, HIST1, 44, 2);
+ bucketValueAndVerify("HIST1: x=", 99999, hist1, HIST1, 44, 3);
+ bucketValueAndVerify("HIST1: x=", 100000, hist1, HIST1, 45, 1);
+ bucketValueAndVerify("HIST1: x=", 989999, hist1, HIST1, 53, 1);
+ bucketValueAndVerify("HIST1: x=", 990000, hist1, HIST1, 53, 2);
+ bucketValueAndVerify("HIST1: x=", 999999, hist1, HIST1, 53, 3);
+ bucketValueAndVerify("HIST1: x=", 1000000, hist1, HIST1, 53, 4);
+ bucketValueAndVerify("HIST1: x=", 1000001, hist1, HIST1, 53, 5);
+ bucketValueAndVerify("HIST1: x=", 5000000, hist1, HIST1, 53, 6);
+ bucketValueAndVerify("HIST1: x=", 10000000, hist1, HIST1, 53, 7);
+
+ WifiMetricsProto.WifiAwareLog.HistogramBucket[] phb1 = histogramToProtoArray(hist1, HIST1);
+ collector.checkThat("Number of buckets #1", phb1.length, equalTo(hist1.size()));
+ validateProtoHistBucket("Bucket1[0]", phb1[0], 1, 2, 3);
+ validateProtoHistBucket("Bucket1[1]", phb1[1], 9, 10, 1);
+ validateProtoHistBucket("Bucket1[2]", phb1[2], 10, 20, 1);
+ validateProtoHistBucket("Bucket1[3]", phb1[3], 90, 100, 1);
+ validateProtoHistBucket("Bucket1[4]", phb1[4], 100, 200, 1);
+ validateProtoHistBucket("Bucket1[5]", phb1[5], 900, 1000, 3);
+ validateProtoHistBucket("Bucket1[6]", phb1[6], 1000, 2000, 1);
+ validateProtoHistBucket("Bucket1[7]", phb1[7], 9000, 10000, 3);
+ validateProtoHistBucket("Bucket1[8]", phb1[8], 10000, 20000, 1);
+ validateProtoHistBucket("Bucket1[9]", phb1[9], 90000, 100000, 3);
+ validateProtoHistBucket("Bucket1[10]", phb1[10], 100000, 200000, 1);
+ validateProtoHistBucket("Bucket1[11]", phb1[11], 900000, 1000000, 7);
+
+ bucketValueAndVerify("HIST2: x=", -20, hist2, HIST2, 0, 1);
+ bucketValueAndVerify("HIST2: x=", -18, hist2, HIST2, 0, 2);
+ bucketValueAndVerify("HIST2: x=", -17, hist2, HIST2, 5, 1);
+ bucketValueAndVerify("HIST2: x=", -11, hist2, HIST2, 35, 1);
+ bucketValueAndVerify("HIST2: x=", -10, hist2, HIST2, 40, 1);
+ bucketValueAndVerify("HIST2: x=", 29, hist2, HIST2, 79, 1);
+ bucketValueAndVerify("HIST2: x=", 30, hist2, HIST2, 80, 1);
+ bucketValueAndVerify("HIST2: x=", 229, hist2, HIST2, 119, 1);
+ bucketValueAndVerify("HIST2: x=", 230, hist2, HIST2, 119, 2);
+ bucketValueAndVerify("HIST2: x=", 300, hist2, HIST2, 119, 3);
+ bucketValueAndVerify("HIST2: x=", 1000000, hist2, HIST2, 119, 4);
+
+ WifiMetricsProto.WifiAwareLog.HistogramBucket[] phb2 = histogramToProtoArray(hist2, HIST2);
+ collector.checkThat("Number of buckets #2", phb2.length, equalTo(hist2.size()));
+ validateProtoHistBucket("Bucket2[0]", phb2[0], -18, -17, 2);
+ validateProtoHistBucket("Bucket2[1]", phb2[1], -17, -16, 1);
+ validateProtoHistBucket("Bucket2[2]", phb2[2], -11, -10, 1);
+ validateProtoHistBucket("Bucket2[3]", phb2[3], -10, -9, 1);
+ validateProtoHistBucket("Bucket2[4]", phb2[4], 29, 30, 1);
+ validateProtoHistBucket("Bucket2[5]", phb2[5], 30, 35, 1);
+ validateProtoHistBucket("Bucket2[6]", phb2[6], 225, 230, 4);
+ }
+
+ /**
+ * Validate the conversion to a NanStatusType proto raw histogram.
+ */
+ @Test
+ public void testNanStatusTypeHistogram() {
+ SparseIntArray statusHistogram = new SparseIntArray();
+
+ addNanHalStatusToHistogram(NanStatusType.SUCCESS, statusHistogram);
+ addNanHalStatusToHistogram(-1, statusHistogram);
+ addNanHalStatusToHistogram(NanStatusType.ALREADY_ENABLED, statusHistogram);
+ addNanHalStatusToHistogram(NanStatusType.SUCCESS, statusHistogram);
+ addNanHalStatusToHistogram(NanStatusType.INTERNAL_FAILURE, statusHistogram);
+ addNanHalStatusToHistogram(NanStatusType.SUCCESS, statusHistogram);
+ addNanHalStatusToHistogram(NanStatusType.INTERNAL_FAILURE, statusHistogram);
+ addNanHalStatusToHistogram(55, statusHistogram);
+ addNanHalStatusToHistogram(65, statusHistogram);
+
+ WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket[] sh = histogramToProtoArray(
+ statusHistogram);
+ collector.checkThat("Number of buckets", sh.length, equalTo(4));
+ validateNanStatusProtoHistBucket("Bucket[SUCCESS]", sh[0],
+ WifiMetricsProto.WifiAwareLog.SUCCESS, 3);
+ validateNanStatusProtoHistBucket("Bucket[INTERNAL_FAILURE]", sh[1],
+ WifiMetricsProto.WifiAwareLog.INTERNAL_FAILURE, 2);
+ validateNanStatusProtoHistBucket("Bucket[ALREADY_ENABLED]", sh[2],
+ WifiMetricsProto.WifiAwareLog.ALREADY_ENABLED, 1);
+ validateNanStatusProtoHistBucket("Bucket[UNKNOWN_HAL_STATUS]", sh[3],
+ WifiMetricsProto.WifiAwareLog.UNKNOWN_HAL_STATUS, 3);
+ }
+
+ // utilities
+
+ /**
+ * Mock the elapsed time since boot to the input argument.
+ */
+ private void setTime(long timeMs) {
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(timeMs);
+ }
+
+ /**
+ * Sum all the 'count' entries in the histogram array.
+ */
+ private int countAllHistogramSamples(WifiMetricsProto.WifiAwareLog.HistogramBucket[] hba) {
+ int sum = 0;
+ for (WifiMetricsProto.WifiAwareLog.HistogramBucket hb: hba) {
+ sum += hb.count;
+ }
+ return sum;
+ }
+
+ private int countAllHistogramSamples(
+ WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket[] nshba) {
+ int sum = 0;
+ for (WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket nshb: nshba) {
+ sum += nshb.count;
+ }
+ return sum;
+ }
+
+ private void bucketValueAndVerify(String logPrefix, long value, SparseIntArray h,
+ WifiAwareMetrics.HistParms hp, int expectedKey, int expectedValue) {
+ WifiAwareMetrics.addLogValueToHistogram(value, h, hp);
+ collector.checkThat(logPrefix + value, h.get(expectedKey), equalTo(expectedValue));
+ }
+
+ private void validateProtoHistBucket(String logPrefix,
+ WifiMetricsProto.WifiAwareLog.HistogramBucket bucket, long start, long end, int count) {
+ collector.checkThat(logPrefix + ": start", bucket.start, equalTo(start));
+ collector.checkThat(logPrefix + ": end", bucket.end, equalTo(end));
+ collector.checkThat(logPrefix + ": count", bucket.count, equalTo(count));
+ }
+
+ private void validateNanStatusProtoHistBucket(String logPrefix,
+ WifiMetricsProto.WifiAwareLog.NanStatusHistogramBucket bucket, int type, int count) {
+ collector.checkThat(logPrefix + ": type", bucket.nanStatusType, equalTo(type));
+ collector.checkThat(logPrefix + ": count", bucket.count, equalTo(count));
+ }
+
+ private WifiAwareNetworkSpecifier addNetworkInfoToCache(
+ Map<WifiAwareNetworkSpecifier, WifiAwareDataPathStateManager
+ .AwareNetworkRequestInformation> networkRequestCache,
+ int index, int uid, String interfaceName, String passphrase) {
+ WifiAwareNetworkSpecifier ns = new WifiAwareNetworkSpecifier(0, 0, 0, index, 0, null, null,
+ passphrase, 0);
+ WifiAwareDataPathStateManager.AwareNetworkRequestInformation anri =
+ new WifiAwareDataPathStateManager.AwareNetworkRequestInformation();
+ anri.networkSpecifier = ns;
+ anri.state = WifiAwareDataPathStateManager.AwareNetworkRequestInformation
+ .STATE_RESPONDER_CONFIRMED;
+ anri.uid = uid;
+ anri.interfaceName = interfaceName;
+
+ networkRequestCache.put(ns, anri);
+ return ns;
+ }
+
+ private void dumpDut(String prefix) {
+ StringWriter sw = new StringWriter();
+ mDut.dump(null, new PrintWriter(sw), null);
+ Log.e("WifiAwareMetrics", prefix + sw.toString());
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeApiTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeApiTest.java
new file mode 100644
index 0000000..9564189
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeApiTest.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2017 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.aware;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyShort;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.wifi.V1_0.IWifiNanIface;
+import android.hardware.wifi.V1_0.NanBandIndex;
+import android.hardware.wifi.V1_0.NanConfigRequest;
+import android.hardware.wifi.V1_0.NanEnableRequest;
+import android.hardware.wifi.V1_0.WifiStatus;
+import android.hardware.wifi.V1_0.WifiStatusCode;
+import android.net.wifi.aware.ConfigRequest;
+import android.os.RemoteException;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ErrorCollector;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.PrintWriter;
+
+/**
+ * Unit test harness for WifiAwareNativeApi
+ */
+public class WifiAwareNativeApiTest {
+ @Mock WifiAwareNativeManager mWifiAwareNativeManagerMock;
+ @Mock IWifiNanIface mIWifiNanIfaceMock;
+
+ @Rule public ErrorCollector collector = new ErrorCollector();
+
+ private WifiAwareNativeApi mDut;
+
+ /**
+ * Initializes mocks.
+ */
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ when(mWifiAwareNativeManagerMock.getWifiNanIface()).thenReturn(mIWifiNanIfaceMock);
+
+ WifiStatus status = new WifiStatus();
+ status.code = WifiStatusCode.SUCCESS;
+ when(mIWifiNanIfaceMock.enableRequest(anyShort(), any())).thenReturn(status);
+ when(mIWifiNanIfaceMock.configRequest(anyShort(), any())).thenReturn(status);
+
+ mDut = new WifiAwareNativeApi(mWifiAwareNativeManagerMock);
+ }
+
+ /**
+ * Test that the set parameter shell command executor works when parameters are valid.
+ */
+ @Test
+ public void testSetParameterShellCommandSuccess() {
+ setSettableParam(WifiAwareNativeApi.PARAM_DW_ON_IDLE_5GHZ, Integer.toString(1), true);
+ }
+
+ /**
+ * Test that the set parameter shell command executor fails on incorrect name.
+ */
+ @Test
+ public void testSetParameterShellCommandInvalidParameterName() {
+ setSettableParam("XXX", Integer.toString(1), false);
+ }
+
+ /**
+ * Test that the set parameter shell command executor fails on invalid value (not convertible
+ * to an int).
+ */
+ @Test
+ public void testSetParameterShellCommandInvalidValue() {
+ setSettableParam(WifiAwareNativeApi.PARAM_DW_ON_IDLE_5GHZ, "garbage", false);
+ }
+
+ /**
+ * Validate that the configuration parameters used to manage power state behavior is
+ * using default values at the default power state.
+ */
+ @Test
+ public void testEnableAndConfigPowerSettingsDefaults() throws RemoteException {
+ NanConfigRequest config = validateEnableAndConfigure((short) 10,
+ new ConfigRequest.Builder().build(), true, true, true, false);
+
+ collector.checkThat("validDiscoveryWindowIntervalVal-5", false,
+ equalTo(config.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ]
+ .validDiscoveryWindowIntervalVal));
+ collector.checkThat("validDiscoveryWindowIntervalVal-24", false,
+ equalTo(config.bandSpecificConfig[NanBandIndex.NAN_BAND_24GHZ]
+ .validDiscoveryWindowIntervalVal));
+ }
+
+ /**
+ * Validate that the configuration parameters used to manage power state behavior is
+ * using the specified non-interactive values when in that power state.
+ */
+ @Test
+ public void testEnableAndConfigPowerSettingsNoneInteractive() throws RemoteException {
+ byte interactive5 = 2;
+ byte interactive24 = 3;
+
+ setPowerConfigurationParams(interactive5, interactive24, (byte) -1, (byte) -1);
+ NanConfigRequest config = validateEnableAndConfigure((short) 10,
+ new ConfigRequest.Builder().build(), false, false, false, false);
+
+ collector.checkThat("validDiscoveryWindowIntervalVal-5", true,
+ equalTo(config.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ]
+ .validDiscoveryWindowIntervalVal));
+ collector.checkThat("discoveryWindowIntervalVal-5", interactive5,
+ equalTo(config.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ]
+ .discoveryWindowIntervalVal));
+ collector.checkThat("validDiscoveryWindowIntervalVal-24", true,
+ equalTo(config.bandSpecificConfig[NanBandIndex.NAN_BAND_24GHZ]
+ .validDiscoveryWindowIntervalVal));
+ collector.checkThat("discoveryWindowIntervalVal-24", interactive24,
+ equalTo(config.bandSpecificConfig[NanBandIndex.NAN_BAND_24GHZ]
+ .discoveryWindowIntervalVal));
+ }
+
+ /**
+ * Validate that the configuration parameters used to manage power state behavior is
+ * using the specified idle (doze) values when in that power state.
+ */
+ @Test
+ public void testEnableAndConfigPowerSettingsIdle() throws RemoteException {
+ byte idle5 = 2;
+ byte idle24 = -1;
+
+ setPowerConfigurationParams((byte) -1, (byte) -1, idle5, idle24);
+ NanConfigRequest config = validateEnableAndConfigure((short) 10,
+ new ConfigRequest.Builder().build(), false, true, false, true);
+
+ collector.checkThat("validDiscoveryWindowIntervalVal-5", true,
+ equalTo(config.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ]
+ .validDiscoveryWindowIntervalVal));
+ collector.checkThat("discoveryWindowIntervalVal-5", idle5,
+ equalTo(config.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ]
+ .discoveryWindowIntervalVal));
+ collector.checkThat("validDiscoveryWindowIntervalVal-24", false,
+ equalTo(config.bandSpecificConfig[NanBandIndex.NAN_BAND_24GHZ]
+ .validDiscoveryWindowIntervalVal));
+ }
+
+ // utilities
+
+ private void setPowerConfigurationParams(byte interactive5, byte interactive24, byte idle5,
+ byte idle24) {
+ setSettableParam(WifiAwareNativeApi.PARAM_DW_ON_INACTIVE_5GHZ,
+ Integer.toString(interactive5), true);
+ setSettableParam(WifiAwareNativeApi.PARAM_DW_ON_INACTIVE_24GHZ,
+ Integer.toString(interactive24), true);
+ setSettableParam(WifiAwareNativeApi.PARAM_DW_ON_IDLE_5GHZ, Integer.toString(idle5), true);
+ setSettableParam(WifiAwareNativeApi.PARAM_DW_ON_IDLE_24GHZ, Integer.toString(idle24), true);
+ }
+
+ private void setSettableParam(String name, String value, boolean expectSuccess) {
+ PrintWriter pwMock = mock(PrintWriter.class);
+ WifiAwareShellCommand parentShellMock = mock(WifiAwareShellCommand.class);
+ when(parentShellMock.getNextArgRequired()).thenReturn("set").thenReturn(name).thenReturn(
+ value);
+ when(parentShellMock.getErrPrintWriter()).thenReturn(pwMock);
+
+ collector.checkThat(mDut.onCommand(parentShellMock), equalTo(expectSuccess ? 0 : -1));
+ }
+
+ private NanConfigRequest validateEnableAndConfigure(short transactionId,
+ ConfigRequest configRequest, boolean notifyIdentityChange, boolean initialConfiguration,
+ boolean isInteractive, boolean isIdle) throws RemoteException {
+ mDut.enableAndConfigure(transactionId, configRequest, notifyIdentityChange,
+ initialConfiguration, isInteractive, isIdle);
+
+ ArgumentCaptor<NanEnableRequest> enableReqCaptor = ArgumentCaptor.forClass(
+ NanEnableRequest.class);
+ ArgumentCaptor<NanConfigRequest> configReqCaptor = ArgumentCaptor.forClass(
+ NanConfigRequest.class);
+ NanConfigRequest config;
+
+ if (initialConfiguration) {
+ verify(mIWifiNanIfaceMock).enableRequest(eq(transactionId), enableReqCaptor.capture());
+ config = enableReqCaptor.getValue().configParams;
+ } else {
+ verify(mIWifiNanIfaceMock).configRequest(eq(transactionId), configReqCaptor.capture());
+ config = configReqCaptor.getValue();
+ }
+
+ collector.checkThat("disableDiscoveryAddressChangeIndication", !notifyIdentityChange,
+ equalTo(config.disableDiscoveryAddressChangeIndication));
+ collector.checkThat("disableStartedClusterIndication", !notifyIdentityChange,
+ equalTo(config.disableStartedClusterIndication));
+ collector.checkThat("disableJoinedClusterIndication", !notifyIdentityChange,
+ equalTo(config.disableJoinedClusterIndication));
+
+ return config;
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeManagerTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeManagerTest.java
index 50a2e0d..ba984b8 100644
--- a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeManagerTest.java
@@ -73,6 +73,7 @@
mDut = new WifiAwareNativeManager(mWifiAwareStateManagerMock,
mHalDeviceManager, mWifiAwareNativeCallback);
+ mDut.start();
mInOrder = inOrder(mWifiAwareStateManagerMock, mHalDeviceManager);
}
diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java
index b5e12d2..d666262 100644
--- a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java
@@ -42,6 +42,8 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
+import com.android.server.wifi.util.WifiPermissionsWrapper;
+
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -70,11 +72,15 @@
@Mock
private WifiAwareStateManager mAwareStateManagerMock;
@Mock
+ private WifiAwareShellCommand mWifiAwareShellCommandMock;
+ @Mock
private IBinder mBinderMock;
@Mock
private IWifiAwareEventCallback mCallbackMock;
@Mock
private IWifiAwareDiscoverySessionCallback mSessionCallbackMock;
+ @Mock private WifiAwareMetrics mAwareMetricsMock;
+ @Mock private WifiPermissionsWrapper mPermissionsWrapperMock;
private HandlerThread mHandlerThread;
@@ -114,8 +120,10 @@
mDut = new WifiAwareServiceImplSpy(mContextMock);
mDut.fakeUid = mDefaultUid;
- mDut.start(mHandlerThreadMock, mAwareStateManagerMock);
- verify(mAwareStateManagerMock).start(eq(mContextMock), any());
+ mDut.start(mHandlerThreadMock, mAwareStateManagerMock, mWifiAwareShellCommandMock,
+ mAwareMetricsMock, mPermissionsWrapperMock);
+ verify(mAwareStateManagerMock).start(eq(mContextMock), any(), eq(mAwareMetricsMock),
+ eq(mPermissionsWrapperMock));
}
/**
diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java
index 2797c0e..faa87df 100644
--- a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java
@@ -23,7 +23,9 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyByte;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyShort;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
@@ -38,8 +40,10 @@
import android.app.AppOpsManager;
import android.app.test.MockAnswerUtil;
import android.app.test.TestAlarmManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.hardware.wifi.V1_0.NanStatusType;
import android.net.ConnectivityManager;
@@ -50,13 +54,18 @@
import android.net.wifi.aware.PublishConfig;
import android.net.wifi.aware.SubscribeConfig;
import android.net.wifi.aware.WifiAwareManager;
+import android.os.Handler;
+import android.os.IPowerManager;
import android.os.Message;
+import android.os.PowerManager;
import android.os.UserHandle;
import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import android.util.SparseArray;
+import com.android.server.wifi.util.WifiPermissionsWrapper;
+
import libcore.util.HexEncoding;
import org.junit.Before;
@@ -86,11 +95,16 @@
private TestLooper mMockLooper;
private Random mRandomNg = new Random(15687);
private WifiAwareStateManager mDut;
+ @Mock private WifiAwareNativeManager mMockNativeManager;
@Mock private WifiAwareNativeApi mMockNative;
@Mock private Context mMockContext;
@Mock private AppOpsManager mMockAppOpsManager;
@Mock private WifiAwareRttStateManager mMockAwareRttStateManager;
+ @Mock private WifiAwareMetrics mAwareMetricsMock;
+ @Mock private WifiPermissionsWrapper mPermissionsWrapperMock;
TestAlarmManager mAlarmManager;
+ private PowerManager mMockPowerManager;
+ private BroadcastReceiver mPowerBcastReceiver;
@Mock private WifiAwareDataPathStateManager mMockAwareDataPathStatemanager;
@Rule
@@ -109,9 +123,18 @@
when(mMockContext.getSystemService(Context.ALARM_SERVICE))
.thenReturn(mAlarmManager.getAlarmManager());
+ mMockLooper = new TestLooper();
+
+ IPowerManager powerManagerService = mock(IPowerManager.class);
+ mMockPowerManager = new PowerManager(mMockContext, powerManagerService,
+ new Handler(mMockLooper.getLooper()));
+
when(mMockContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(
mock(ConnectivityManager.class));
when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mMockAppOpsManager);
+ when(mMockContext.getSystemServiceName(PowerManager.class)).thenReturn(
+ Context.POWER_SERVICE);
+ when(mMockContext.getSystemService(PowerManager.class)).thenReturn(mMockPowerManager);
when(mMockContext.checkPermission(eq(android.Manifest.permission.ACCESS_FINE_LOCATION),
anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED);
when(mMockContext.checkPermission(eq(Manifest.permission.ACCESS_COARSE_LOCATION),
@@ -120,34 +143,75 @@
any())).thenReturn(AppOpsManager.MODE_ERRORED);
when(mMockAppOpsManager.noteOp(eq(AppOpsManager.OP_COARSE_LOCATION), anyInt(),
any())).thenReturn(AppOpsManager.MODE_ERRORED);
+ when(mMockPowerManager.isDeviceIdleMode()).thenReturn(false);
+ when(mMockPowerManager.isInteractive()).thenReturn(true);
- mMockLooper = new TestLooper();
-
+ ArgumentCaptor<BroadcastReceiver> bcastRxCaptor = ArgumentCaptor.forClass(
+ BroadcastReceiver.class);
mDut = new WifiAwareStateManager();
- mDut.setNative(mMockNative);
- mDut.start(mMockContext, mMockLooper.getLooper());
+ mDut.setNative(mMockNativeManager, mMockNative);
+ mDut.start(mMockContext, mMockLooper.getLooper(), mAwareMetricsMock,
+ mPermissionsWrapperMock);
+ mDut.startLate();
+ mMockLooper.dispatchAll();
+ verify(mMockContext).registerReceiver(bcastRxCaptor.capture(), any(IntentFilter.class));
+ mPowerBcastReceiver = bcastRxCaptor.getValue();
installMocksInStateManager(mDut, mMockAwareRttStateManager, mMockAwareDataPathStatemanager);
when(mMockNative.enableAndConfigure(anyShort(), any(), anyBoolean(),
- anyBoolean())).thenReturn(true);
+ anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(true);
when(mMockNative.disable(anyShort())).thenReturn(true);
- when(mMockNative.publish(anyShort(), anyInt(), any())).thenReturn(true);
- when(mMockNative.subscribe(anyShort(), anyInt(), any()))
+ when(mMockNative.publish(anyShort(), anyByte(), any())).thenReturn(true);
+ when(mMockNative.subscribe(anyShort(), anyByte(), any()))
.thenReturn(true);
- when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(),
+ when(mMockNative.sendMessage(anyShort(), anyByte(), anyInt(), any(),
any(), anyInt())).thenReturn(true);
- when(mMockNative.stopPublish(anyShort(), anyInt())).thenReturn(true);
- when(mMockNative.stopSubscribe(anyShort(), anyInt())).thenReturn(true);
+ when(mMockNative.stopPublish(anyShort(), anyByte())).thenReturn(true);
+ when(mMockNative.stopSubscribe(anyShort(), anyByte())).thenReturn(true);
when(mMockNative.getCapabilities(anyShort())).thenReturn(true);
}
/**
+ * Test that the set parameter shell command executor works when parameters are valid.
+ */
+ @Test
+ public void testSetParameterShellCommandSuccess() {
+ setSettableParam(WifiAwareStateManager.PARAM_ON_IDLE_DISABLE_AWARE, Integer.toString(1),
+ true);
+ }
+
+ /**
+ * Test that the set parameter shell command executor fails on incorrect name.
+ */
+ @Test
+ public void testSetParameterShellCommandInvalidParameterName() {
+ setSettableParam("XXX", Integer.toString(1), false);
+ }
+
+ /**
+ * Test that the set parameter shell command executor fails on invalid value (not convertible
+ * to an int).
+ */
+ @Test
+ public void testSetParameterShellCommandInvalidValue() {
+ setSettableParam(WifiAwareStateManager.PARAM_ON_IDLE_DISABLE_AWARE, "garbage", false);
+ }
+
+ /**
* Validate that Aware data-path interfaces are brought up and down correctly.
*/
@Test
public void testAwareDataPathInterfaceUpDown() throws Exception {
+ final int clientId = 12341;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- InOrder inOrder = inOrder(mMockContext, mMockNative, mMockAwareDataPathStatemanager);
+ InOrder inOrder = inOrder(mMockContext, mMockNative, mMockAwareDataPathStatemanager,
+ mockCallback);
// (1) enable usage
mDut.enableUsage();
@@ -156,17 +220,25 @@
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
- inOrder.verify(mMockAwareDataPathStatemanager).createAllInterfaces();
collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
- // (2) disable usage
- mDut.disableUsage();
+ // (2) connect (enable Aware)
+ mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
- inOrder.verify(mMockAwareDataPathStatemanager).onAwareDownCleanupDataPaths();
- inOrder.verify(mMockNative).disable((short) 0);
- validateCorrectAwareStatusChangeBroadcast(inOrder, false);
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true), eq(true), eq(false));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+ inOrder.verify(mMockAwareDataPathStatemanager).createAllInterfaces();
+
+ // (3) disconnect (disable Aware)
+ mDut.disconnect(clientId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).disable(transactionId.capture());
+ mDut.onDisableResponse(transactionId.getValue(), NanStatusType.SUCCESS);
+ mMockLooper.dispatchAll();
inOrder.verify(mMockAwareDataPathStatemanager).deleteAllInterfaces();
- collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
verifyNoMoreInteractions(mMockNative, mMockAwareDataPathStatemanager);
}
@@ -200,13 +272,15 @@
mDut.disableUsage();
mMockLooper.dispatchAll();
collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
- inOrder.verify(mMockNative).disable((short) 0);
+ inOrder.verify(mMockNative).disable(transactionId.capture());
+ mDut.onDisableResponse(transactionId.getValue(), NanStatusType.SUCCESS);
validateCorrectAwareStatusChangeBroadcast(inOrder, false);
- // (3) try connecting and validate that get nothing (app should be aware of non-availability
- // through state change broadcast and/or query API)
+ // (3) try connecting and validate that get failure callback (though app should be aware of
+ // non-availability through state change broadcast and/or query API)
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectFail(anyInt());
verifyNoMoreInteractions(mMockNative, mockCallback);
}
@@ -226,12 +300,15 @@
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<SparseArray> sparseArrayCaptor = ArgumentCaptor.forClass(SparseArray.class);
InOrder inOrder = inOrder(mMockContext, mMockNative, mockCallback);
+ InOrder inOrderM = inOrder(mAwareMetricsMock);
// (1) check initial state
mDut.enableUsage();
mMockLooper.dispatchAll();
validateCorrectAwareStatusChangeBroadcast(inOrder, true);
+ inOrderM.verify(mAwareMetricsMock).recordEnableUsage();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
@@ -242,22 +319,33 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
+ inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(uid), eq(false),
+ sparseArrayCaptor.capture());
+ collector.checkThat("num of clients", sparseArrayCaptor.getValue().size(), equalTo(1));
// (3) disable usage & verify callbacks
mDut.disableUsage();
mMockLooper.dispatchAll();
collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
- inOrder.verify(mMockNative).disable((short) 0);
+ inOrder.verify(mMockNative).disable(transactionId.capture());
+ inOrderM.verify(mAwareMetricsMock).recordAttachSessionDuration(anyLong());
+ inOrderM.verify(mAwareMetricsMock).recordDisableAware();
+ inOrderM.verify(mAwareMetricsMock).recordDisableUsage();
validateCorrectAwareStatusChangeBroadcast(inOrder, false);
validateInternalClientInfoCleanedUp(clientId);
+ mDut.onDisableResponse(transactionId.getValue(), NanStatusType.SUCCESS);
+ mMockLooper.dispatchAll();
+ inOrderM.verify(mAwareMetricsMock).recordDisableAware();
- // (4) try connecting again and validate that just get an onAwareDown
+ // (4) try connecting again and validate that get a failure
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectFail(anyInt());
+ inOrderM.verify(mAwareMetricsMock).recordAttachStatus(NanStatusType.INTERNAL_FAILURE);
// (5) disable usage again and validate that not much happens
mDut.disableUsage();
@@ -267,6 +355,7 @@
// (6) enable usage
mDut.enableUsage();
mMockLooper.dispatchAll();
+ inOrderM.verify(mAwareMetricsMock).recordEnableUsage();
collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
validateCorrectAwareStatusChangeBroadcast(inOrder, true);
@@ -274,12 +363,15 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
+ inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(uid), eq(false),
+ sparseArrayCaptor.capture());
+ collector.checkThat("num of clients", sparseArrayCaptor.getValue().size(), equalTo(1));
- verifyNoMoreInteractions(mMockNative, mockCallback);
+ verifyNoMoreInteractions(mMockNative, mockCallback, mAwareMetricsMock);
}
/**
@@ -298,7 +390,7 @@
InOrder inOrder = inOrder(mMockContext, mMockNative, mockCallback);
when(mMockNative.enableAndConfigure(anyShort(), any(), anyBoolean(),
- anyBoolean())).thenReturn(false);
+ anyBoolean(), eq(true), eq(false))).thenReturn(false);
// (1) check initial state
mDut.enableUsage();
@@ -312,7 +404,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
inOrder.verify(mockCallback).onConnectFail(NanStatusType.INTERNAL_FAILURE);
validateInternalClientInfoCleanedUp(clientId);
@@ -356,7 +448,7 @@
mDut.connect(clientId1, uid, pid, callingPackage, mockCallback1, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionIdCapture.capture(),
- eq(configRequest), eq(false), eq(true));
+ eq(configRequest), eq(false), eq(true), eq(true), eq(false));
short transactionId = transactionIdCapture.getValue();
mDut.onConfigSuccessResponse(transactionId);
mMockLooper.dispatchAll();
@@ -365,7 +457,7 @@
mDut.connect(clientId2, uid, pid, callingPackage, mockCallback2, configRequest, true);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionIdCapture.capture(),
- eq(configRequest), eq(true), eq(false));
+ eq(configRequest), eq(true), eq(false), eq(true), eq(false));
transactionId = transactionIdCapture.getValue();
mDut.onConfigSuccessResponse(transactionId);
mMockLooper.dispatchAll();
@@ -430,10 +522,12 @@
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
+ InOrder inOrderM = inOrder(mAwareMetricsMock);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ inOrderM.verify(mAwareMetricsMock).recordEnableUsage();
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
@@ -441,29 +535,35 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
+ inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(uid), eq(false), any());
// (2) publish + timeout
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).publish(anyShort(), eq(0), eq(publishConfig));
+ inOrder.verify(mMockNative).publish(anyShort(), eq((byte) 0), eq(publishConfig));
assertTrue(mAlarmManager.dispatch(WifiAwareStateManager.HAL_COMMAND_TIMEOUT_TAG));
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
+ inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid,
+ NanStatusType.INTERNAL_FAILURE, true);
validateInternalNoSessions(clientId);
// (3) publish + success
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
- mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, 9999);
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq((byte) 0),
+ eq(publishConfig));
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, (byte) 99);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(anyInt());
+ inOrderM.verify(mAwareMetricsMock).recordDiscoverySession(eq(uid), eq(true), any());
+ inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, NanStatusType.SUCCESS, true);
- verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
+ verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback, mAwareMetricsMock);
}
/**
@@ -486,10 +586,12 @@
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
+ InOrder inOrderM = inOrder(mAwareMetricsMock);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ inOrderM.verify(mAwareMetricsMock).recordEnableUsage();
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
@@ -497,33 +599,38 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
- eq(configRequest), eq(false), eq(true));
+ eq(configRequest), eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
+ inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(uid), eq(false), any());
// (1) initial publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq((byte) 0),
+ eq(publishConfig));
// (2) publish failure callback (i.e. firmware tried and failed)
mDut.onSessionConfigFailResponse(transactionId.getValue(), true, reasonFail);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
+ inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, reasonFail, true);
validateInternalNoSessions(clientId);
// (3) publish and get immediate failure (i.e. HAL failed)
- when(mMockNative.publish(anyShort(), anyInt(), any())).thenReturn(false);
+ when(mMockNative.publish(anyShort(), anyByte(), any())).thenReturn(false);
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq((byte) 0),
+ eq(publishConfig));
inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
+ inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, reasonFail, true);
validateInternalNoSessions(clientId);
- verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative, mAwareMetricsMock);
}
/**
@@ -539,7 +646,7 @@
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final int reasonTerminate = NanStatusType.SUCCESS;
- final int publishId = 15;
+ final byte publishId = 15;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
PublishConfig publishConfig = new PublishConfig.Builder().build();
@@ -550,10 +657,12 @@
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
+ InOrder inOrderM = inOrder(mAwareMetricsMock);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ inOrderM.verify(mAwareMetricsMock).recordEnableUsage();
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
@@ -561,25 +670,30 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
- eq(configRequest), eq(false), eq(true));
+ eq(configRequest), eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
+ inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(uid), eq(false), any());
// (1) initial publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq((byte) 0),
+ eq(publishConfig));
// (2) publish success
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+ inOrderM.verify(mAwareMetricsMock).recordDiscoverySession(eq(uid), eq(true), any());
+ inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, NanStatusType.SUCCESS, true);
// (3) publish termination (from firmware - not app!)
mDut.onSessionTerminatedNotification(publishId, reasonTerminate, true);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionTerminated(reasonTerminate);
+ inOrderM.verify(mAwareMetricsMock).recordDiscoverySessionDuration(anyLong(), eq(true));
// (4) app update session (race condition: app didn't get termination
// yet)
@@ -597,14 +711,15 @@
validateInternalSessionInfoCleanedUp(clientId, sessionId.getValue());
- verifyNoMoreInteractions(mockSessionCallback, mMockNative);
+ verifyNoMoreInteractions(mockSessionCallback, mMockNative, mAwareMetricsMock);
}
/**
* Validate the publish flow: (1) initial publish + (2) success + (3) update + (4) update
* fails (callback from firmware) + (5) update + (6). Expected: session is still alive after
* update failure so second update succeeds (no callbacks) + (7) update + immediate failure from
- * HAL.
+ * HAL + (8) update + failure for invalid ID (which should clean-up state) + (9) another update
+ * - should get no response.
*/
@Test
public void testPublishUpdateFail() throws Exception {
@@ -612,7 +727,7 @@
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
- final int publishId = 15;
+ final byte publishId = 15;
final int reasonFail = NanStatusType.INTERNAL_FAILURE;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
@@ -624,10 +739,12 @@
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
+ InOrder inOrderM = inOrder(mAwareMetricsMock);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ inOrderM.verify(mAwareMetricsMock).recordEnableUsage();
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
@@ -635,20 +752,24 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
+ inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(uid), eq(false), any());
// (1) initial publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq((byte) 0),
+ eq(publishConfig));
// (2) publish success
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+ inOrderM.verify(mAwareMetricsMock).recordDiscoverySession(eq(uid), eq(true), any());
+ inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, NanStatusType.SUCCESS, true);
// (3) update publish
mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
@@ -660,6 +781,7 @@
mDut.onSessionConfigFailResponse(transactionId.getValue(), true, reasonFail);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
+ inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, reasonFail, true);
// (5) another update publish
mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
@@ -671,17 +793,37 @@
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionConfigSuccess();
+ inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, NanStatusType.SUCCESS, true);
// (7) another update + immediate failure
- when(mMockNative.publish(anyShort(), anyInt(), any())).thenReturn(false);
+ when(mMockNative.publish(anyShort(), anyByte(), any())).thenReturn(false);
mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq(publishId),
eq(publishConfig));
inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
+ inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, reasonFail, true);
- verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
+ // (8) an update with bad ID failure
+ when(mMockNative.publish(anyShort(), anyByte(), any())).thenReturn(true);
+
+ mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(publishId),
+ eq(publishConfig));
+ mDut.onSessionConfigFailResponse(transactionId.getValue(), true,
+ NanStatusType.INVALID_SESSION_ID);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionConfigFail(NanStatusType.INVALID_SESSION_ID);
+ inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid,
+ NanStatusType.INVALID_SESSION_ID, true);
+
+ // (9) try updating again - do nothing/get nothing
+ mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
+ mMockLooper.dispatchAll();
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative, mAwareMetricsMock);
}
/**
@@ -696,7 +838,7 @@
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
- final int publishId = 15;
+ final byte publishId = 15;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
PublishConfig publishConfig = new PublishConfig.Builder().build();
@@ -706,10 +848,12 @@
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
+ InOrder inOrderM = inOrder(mAwareMetricsMock);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ inOrderM.verify(mAwareMetricsMock).recordEnableUsage();
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
@@ -717,15 +861,17 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
+ inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(uid), eq(false), any());
// (1) initial publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq((byte) 0),
+ eq(publishConfig));
// (2) disconnect (but doesn't get executed until get response for
// publish command)
@@ -737,11 +883,15 @@
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(anyInt());
inOrder.verify(mMockNative).stopPublish(transactionId.capture(), eq(publishId));
- inOrder.verify(mMockNative).disable((short) 0);
+ inOrder.verify(mMockNative).disable(anyShort());
+ inOrderM.verify(mAwareMetricsMock).recordDiscoverySession(eq(uid), eq(true), any());
+ inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, NanStatusType.SUCCESS, true);
+ inOrderM.verify(mAwareMetricsMock).recordAttachSessionDuration(anyLong());
+ inOrderM.verify(mAwareMetricsMock).recordDiscoverySessionDuration(anyLong(), eq(true));
validateInternalClientInfoCleanedUp(clientId);
- verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative, mAwareMetricsMock);
}
/**
@@ -775,7 +925,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -783,7 +933,8 @@
// (1) initial subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
+ eq(subscribeConfig));
// (2) subscribe failure
mDut.onSessionConfigFailResponse(transactionId.getValue(), false, reasonFail);
@@ -792,12 +943,13 @@
validateInternalNoSessions(clientId);
// (3) subscribe and get immediate failure (i.e. HAL failed)
- when(mMockNative.subscribe(anyShort(), anyInt(), any()))
+ when(mMockNative.subscribe(anyShort(), anyByte(), any()))
.thenReturn(false);
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
+ eq(subscribeConfig));
inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
validateInternalNoSessions(clientId);
@@ -818,7 +970,7 @@
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final int reasonTerminate = NanStatusType.SUCCESS;
- final int subscribeId = 15;
+ final byte subscribeId = 15;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
@@ -840,7 +992,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -848,7 +1000,8 @@
// (1) initial subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
+ eq(subscribeConfig));
// (2) subscribe success
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
@@ -890,7 +1043,7 @@
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
- final int subscribeId = 15;
+ final byte subscribeId = 15;
final int reasonFail = NanStatusType.INTERNAL_FAILURE;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
@@ -913,7 +1066,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -921,7 +1074,8 @@
// (1) initial subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
+ eq(subscribeConfig));
// (2) subscribe success
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
@@ -951,7 +1105,7 @@
inOrder.verify(mockSessionCallback).onSessionConfigSuccess();
// (7) another update + immediate failure
- when(mMockNative.subscribe(anyShort(), anyInt(), any()))
+ when(mMockNative.subscribe(anyShort(), anyByte(), any()))
.thenReturn(false);
mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
@@ -975,7 +1129,7 @@
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
- final int subscribeId = 15;
+ final byte subscribeId = 15;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
@@ -996,7 +1150,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -1004,7 +1158,8 @@
// (1) initial subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
+ eq(subscribeConfig));
// (2) disconnect (but doesn't get executed until get response for
// subscribe command)
@@ -1016,7 +1171,7 @@
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(anyInt());
inOrder.verify(mMockNative).stopSubscribe((short) 0, subscribeId);
- inOrder.verify(mMockNative).disable((short) 0);
+ inOrder.verify(mMockNative).disable(anyShort());
validateInternalClientInfoCleanedUp(clientId);
@@ -1036,7 +1191,7 @@
final String serviceName = "some-service-name";
final String ssi = "some much longer and more arbitrary data";
final int reasonFail = NanStatusType.INTERNAL_FAILURE;
- final int subscribeId = 15;
+ final byte subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
final String peerSsi = "some peer ssi data";
@@ -1056,6 +1211,7 @@
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
@@ -1068,7 +1224,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
- eq(configRequest), eq(false), eq(true));
+ eq(configRequest), eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -1076,7 +1232,8 @@
// (1) subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
+ eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
@@ -1085,16 +1242,18 @@
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes());
mMockLooper.dispatchAll();
- inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
- peerMatchFilter.getBytes());
+ inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor.capture(), eq(peerSsi.getBytes()),
+ eq(peerMatchFilter.getBytes()));
// (3) message Rx
mDut.onMessageReceivedNotification(subscribeId, requestorId, peerMac, peerMsg.getBytes());
mMockLooper.dispatchAll();
- inOrder.verify(mockSessionCallback).onMessageReceived(requestorId, peerMsg.getBytes());
+ inOrder.verify(mockSessionCallback).onMessageReceived(peerIdCaptor.getValue(),
+ peerMsg.getBytes());
// (4) message Tx successful queuing
- mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId, 0);
+ mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), ssi.getBytes(),
+ messageId, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
@@ -1103,8 +1262,8 @@
mMockLooper.dispatchAll();
// (5) message Tx successful queuing
- mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId2,
- 0);
+ mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), ssi.getBytes(),
+ messageId2, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId2));
@@ -1138,9 +1297,9 @@
final int clusterHigh = 7;
final int masterPref = 0;
final String serviceName = "some-service-name";
- final int publishId = 88;
- final int peerId1 = 568;
- final int peerId2 = 873;
+ final byte publishId = 88;
+ final int requestorId1 = 568;
+ final int requestorId2 = 873;
final byte[] peerMac1 = HexEncoding.decode("000102030405".toCharArray(), false);
final byte[] peerMac2 = HexEncoding.decode("060708090A0B".toCharArray(), false);
final String msgFromPeer1 = "hey from 000102...";
@@ -1159,6 +1318,7 @@
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
@@ -1174,7 +1334,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -1182,24 +1342,31 @@
// (2) publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq((byte) 0),
+ eq(publishConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
// (3) message received from peers 1 & 2
- mDut.onMessageReceivedNotification(publishId, peerId1, peerMac1, msgFromPeer1.getBytes());
- mDut.onMessageReceivedNotification(publishId, peerId2, peerMac2, msgFromPeer2.getBytes());
+ mDut.onMessageReceivedNotification(publishId, requestorId1, peerMac1,
+ msgFromPeer1.getBytes());
+ mDut.onMessageReceivedNotification(publishId, requestorId2, peerMac2,
+ msgFromPeer2.getBytes());
mMockLooper.dispatchAll();
- inOrder.verify(mockSessionCallback).onMessageReceived(peerId1, msgFromPeer1.getBytes());
- inOrder.verify(mockSessionCallback).onMessageReceived(peerId2, msgFromPeer2.getBytes());
+ inOrder.verify(mockSessionCallback).onMessageReceived(peerIdCaptor.capture(),
+ eq(msgFromPeer1.getBytes()));
+ int peerId1 = peerIdCaptor.getValue();
+ inOrder.verify(mockSessionCallback).onMessageReceived(peerIdCaptor.capture(),
+ eq(msgFromPeer2.getBytes()));
+ int peerId2 = peerIdCaptor.getValue();
// (4) sending messages back to same peers: one Tx fails, other succeeds
mDut.sendMessage(clientId, sessionId.getValue(), peerId2, msgToPeer2.getBytes(),
msgToPeerId2, 0);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId2),
- eq(peerMac2), eq(msgToPeer2.getBytes()), eq(msgToPeerId2));
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId),
+ eq(requestorId2), eq(peerMac2), eq(msgToPeer2.getBytes()), eq(msgToPeerId2));
short transactionIdVal = transactionId.getValue();
mDut.onMessageSendQueuedSuccessResponse(transactionIdVal);
mDut.onMessageSendSuccessNotification(transactionIdVal);
@@ -1208,8 +1375,8 @@
msgToPeerId1, 0);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageSendSuccess(msgToPeerId2);
- inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId1),
- eq(peerMac1), eq(msgToPeer1.getBytes()), eq(msgToPeerId1));
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId),
+ eq(requestorId1), eq(peerMac1), eq(msgToPeer1.getBytes()), eq(msgToPeerId1));
transactionIdVal = transactionId.getValue();
mDut.onMessageSendQueuedSuccessResponse(transactionIdVal);
mDut.onMessageSendFailNotification(transactionIdVal, reason);
@@ -1235,8 +1402,8 @@
final int clusterHigh = 7;
final int masterPref = 0;
final String serviceName = "some-service-name";
- final int publishId = 88;
- final int peerId = 568;
+ final byte publishId = 88;
+ final int requestorId = 568;
final byte[] peerMacOrig = HexEncoding.decode("000102030405".toCharArray(), false);
final byte[] peerMacLater = HexEncoding.decode("060708090A0B".toCharArray(), false);
final String msgFromPeer1 = "hey from 000102...";
@@ -1253,6 +1420,7 @@
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> peerId = ArgumentCaptor.forClass(Integer.class);
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
@@ -1268,7 +1436,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -1276,19 +1444,24 @@
// (2) publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq((byte) 0),
+ eq(publishConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
// (3) message received & responded to
- mDut.onMessageReceivedNotification(publishId, peerId, peerMacOrig, msgFromPeer1.getBytes());
- mDut.sendMessage(clientId, sessionId.getValue(), peerId, msgToPeer1.getBytes(),
+ mDut.onMessageReceivedNotification(publishId, requestorId, peerMacOrig,
+ msgFromPeer1.getBytes());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageReceived(peerId.capture(),
+ eq(msgFromPeer1.getBytes()));
+ mDut.sendMessage(clientId, sessionId.getValue(), peerId.getValue(), msgToPeer1.getBytes(),
msgToPeerId1, 0);
mMockLooper.dispatchAll();
- inOrder.verify(mockSessionCallback).onMessageReceived(peerId, msgFromPeer1.getBytes());
- inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId),
- eq(peerMacOrig), eq(msgToPeer1.getBytes()), eq(msgToPeerId1));
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId),
+ eq(requestorId), eq(peerMacOrig), eq(msgToPeer1.getBytes()),
+ eq(msgToPeerId1));
mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
mDut.onMessageSendSuccessNotification(transactionId.getValue());
mMockLooper.dispatchAll();
@@ -1296,14 +1469,17 @@
validateInternalSendMessageQueuesCleanedUp(msgToPeerId1);
// (4) message received with same peer ID but different MAC
- mDut.onMessageReceivedNotification(publishId, peerId, peerMacLater,
+ mDut.onMessageReceivedNotification(publishId, requestorId, peerMacLater,
msgFromPeer2.getBytes());
- mDut.sendMessage(clientId, sessionId.getValue(), peerId, msgToPeer2.getBytes(),
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageReceived(peerId.capture(),
+ eq(msgFromPeer2.getBytes()));
+ mDut.sendMessage(clientId, sessionId.getValue(), peerId.getValue(), msgToPeer2.getBytes(),
msgToPeerId2, 0);
mMockLooper.dispatchAll();
- inOrder.verify(mockSessionCallback).onMessageReceived(peerId, msgFromPeer2.getBytes());
- inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId),
- eq(peerMacLater), eq(msgToPeer2.getBytes()), eq(msgToPeerId2));
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId),
+ eq(requestorId), eq(peerMacLater), eq(msgToPeer2.getBytes()),
+ eq(msgToPeerId2));
mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
mDut.onMessageSendSuccessNotification(transactionId.getValue());
mMockLooper.dispatchAll();
@@ -1324,7 +1500,7 @@
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String ssi = "some much longer and more arbitrary data";
- final int subscribeId = 15;
+ final byte subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
final String peerSsi = "some peer ssi data";
@@ -1339,6 +1515,7 @@
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
@@ -1351,7 +1528,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -1359,18 +1536,19 @@
// (2) subscribe & match
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
+ eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
- inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
- peerMatchFilter.getBytes());
+ inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor.capture(), eq(peerSsi.getBytes()),
+ eq(peerMatchFilter.getBytes()));
// (3) send message to invalid peer ID
- mDut.sendMessage(clientId, sessionId.getValue(), requestorId + 5, ssi.getBytes(),
- messageId, 0);
+ mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue() + 5,
+ ssi.getBytes(), messageId, 0);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageSendFail(messageId,
NanStatusType.INTERNAL_FAILURE);
@@ -1391,7 +1569,7 @@
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String ssi = "some much longer and more arbitrary data";
- final int subscribeId = 15;
+ final byte subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
final String peerSsi = "some peer ssi data";
@@ -1406,6 +1584,7 @@
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
@@ -1418,7 +1597,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -1426,17 +1605,18 @@
// (2) subscribe & match
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
+ eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
- inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
- peerMatchFilter.getBytes());
+ inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor.capture(), eq(peerSsi.getBytes()),
+ eq(peerMatchFilter.getBytes()));
// (3) send 2 messages and enqueue successfully
- mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
+ mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), ssi.getBytes(),
messageId, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
@@ -1445,7 +1625,7 @@
mDut.onMessageSendQueuedSuccessResponse(transactionId1);
mMockLooper.dispatchAll();
- mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
+ mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), ssi.getBytes(),
messageId + 1, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
@@ -1455,8 +1635,8 @@
mMockLooper.dispatchAll();
// (4) send a message and get a queueing failure (not queue full)
- mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId + 2,
- 0);
+ mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), ssi.getBytes(),
+ messageId + 2, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId + 2));
@@ -1468,11 +1648,11 @@
validateInternalSendMessageQueuesCleanedUp(messageId + 2);
// (5) send a message and get an immediate failure (configure first)
- when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(),
+ when(mMockNative.sendMessage(anyShort(), anyByte(), anyInt(), any(),
any(), anyInt())).thenReturn(false);
- mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId + 3,
- 0);
+ mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), ssi.getBytes(),
+ messageId + 3, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId + 3));
@@ -1514,7 +1694,7 @@
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String ssi = "some much longer and more arbitrary data";
- final int subscribeId = 15;
+ final byte subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
final String peerSsi = "some peer ssi data";
@@ -1530,6 +1710,7 @@
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
@@ -1542,7 +1723,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -1550,17 +1731,18 @@
// (2) subscribe & match
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
+ eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
- inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
- peerMatchFilter.getBytes());
+ inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor.capture(), eq(peerSsi.getBytes()),
+ eq(peerMatchFilter.getBytes()));
// (3) send message and enqueue successfully
- mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
+ mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), ssi.getBytes(),
messageId, retryCount);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
@@ -1599,7 +1781,7 @@
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String ssi = "some much longer and more arbitrary data";
- final int subscribeId = 15;
+ final byte subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
final String peerSsi = "some peer ssi data";
@@ -1615,6 +1797,7 @@
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
@@ -1627,7 +1810,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -1635,18 +1818,19 @@
// (2) subscribe & match
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
+ eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
- inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
- peerMatchFilter.getBytes());
+ inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor.capture(), eq(peerSsi.getBytes()),
+ eq(peerMatchFilter.getBytes()));
// (3) send message and enqueue successfully
- mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId,
- retryCount);
+ mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), ssi.getBytes(),
+ messageId, retryCount);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
@@ -1684,7 +1868,7 @@
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String serviceName = "some-service-name";
- final int subscribeId = 15;
+ final byte subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
final int messageIdBase = 6948;
@@ -1701,6 +1885,7 @@
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> messageIdCaptor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
@@ -1713,7 +1898,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
- eq(configRequest), eq(false), eq(true));
+ eq(configRequest), eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -1721,7 +1906,8 @@
// (1) subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
+ eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
@@ -1729,18 +1915,18 @@
// (2) match
mDut.onMatchNotification(subscribeId, requestorId, peerMac, null, null);
mMockLooper.dispatchAll();
- inOrder.verify(mockSessionCallback).onMatch(requestorId, null, null);
+ inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor.capture(), isNull(), isNull());
// (3) transmit messages
SendMessageQueueModelAnswer answerObj = new SendMessageQueueModelAnswer(queueDepth,
null, null, null);
- when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(),
+ when(mMockNative.sendMessage(anyShort(), anyByte(), anyInt(), any(),
any(), anyInt())).thenAnswer(answerObj);
int remainingMessages = numberOfMessages;
for (int i = 0; i < numberOfMessages; ++i) {
- mDut.sendMessage(clientId, sessionId.getValue(), requestorId, null, messageIdBase + i,
- 0);
+ mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), null,
+ messageIdBase + i, 0);
mMockLooper.dispatchAll();
// at 1/2 interval have the system simulate transmitting a queued message over-the-air
if (i % 2 == 1) {
@@ -1779,7 +1965,7 @@
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String serviceName = "some-service-name";
- final int subscribeId = 15;
+ final byte subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
final int messageIdBase = 6948;
@@ -1796,7 +1982,7 @@
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
- ArgumentCaptor<Integer> messageIdCaptor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
@@ -1809,7 +1995,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
- eq(configRequest), eq(false), eq(true));
+ eq(configRequest), eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -1817,7 +2003,8 @@
// (1) subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
+ eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
@@ -1825,7 +2012,7 @@
// (2) match
mDut.onMatchNotification(subscribeId, requestorId, peerMac, null, null);
mMockLooper.dispatchAll();
- inOrder.verify(mockSessionCallback).onMatch(requestorId, null, null);
+ inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor.capture(), isNull(), isNull());
// (3) transmit messages: configure a mix of failures/success
Set<Integer> failQueueCommandImmediately = new HashSet<>();
@@ -1874,12 +2061,12 @@
SendMessageQueueModelAnswer answerObj = new SendMessageQueueModelAnswer(queueDepth,
failQueueCommandImmediately, failQueueCommandLater, numberOfRetries);
- when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(),
+ when(mMockNative.sendMessage(anyShort(), anyByte(), anyInt(), any(),
any(), anyInt())).thenAnswer(answerObj);
for (int i = 0; i < numberOfMessages; ++i) {
- mDut.sendMessage(clientId, sessionId.getValue(), requestorId, null, messageIdBase + i,
- retransmitCount);
+ mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), null,
+ messageIdBase + i, retransmitCount);
mMockLooper.dispatchAll();
}
@@ -1908,7 +2095,7 @@
final String callingPackage = "com.google.somePackage";
final String serviceName = "some-service-name";
final String ssi = "some much longer and more arbitrary data";
- final int subscribeId = 15;
+ final byte subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
final String peerSsi = "some peer ssi data";
@@ -1926,6 +2113,7 @@
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<byte[]> byteArrayCaptor = ArgumentCaptor.forClass(byte[].class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
@@ -1939,7 +2127,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
- eq(configRequest), eq(false), eq(true));
+ eq(configRequest), eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -1947,7 +2135,8 @@
// (1) subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
+ eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
@@ -1956,11 +2145,12 @@
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes());
mMockLooper.dispatchAll();
- inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
- peerMatchFilter.getBytes());
+ inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor.capture(), eq(peerSsi.getBytes()),
+ eq(peerMatchFilter.getBytes()));
// (3) message null Tx successful queuing
- mDut.sendMessage(clientId, sessionId.getValue(), requestorId, null, messageId, 0);
+ mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), null, messageId,
+ 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), isNull(byte[].class), eq(messageId));
@@ -1975,7 +2165,8 @@
validateInternalSendMessageQueuesCleanedUp(messageId);
// (5) message byte[0] Tx successful queuing
- mDut.sendMessage(clientId, sessionId.getValue(), requestorId, new byte[0], messageId, 0);
+ mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), new byte[0],
+ messageId, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), eq(new byte[0]), eq(messageId));
@@ -1990,7 +2181,8 @@
validateInternalSendMessageQueuesCleanedUp(messageId);
// (7) message "" Tx successful queuing
- mDut.sendMessage(clientId, sessionId.getValue(), requestorId, "".getBytes(), messageId, 0);
+ mDut.sendMessage(clientId, sessionId.getValue(), peerIdCaptor.getValue(), "".getBytes(),
+ messageId, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), byteArrayCaptor.capture(), eq(messageId));
@@ -2039,7 +2231,7 @@
}
}
- public boolean answer(short transactionId, int pubSubId, int requestorInstanceId,
+ public boolean answer(short transactionId, byte pubSubId, int requestorInstanceId,
byte[] dest, byte[] message, int messageId) throws Exception {
if (mFailQueueCommandImmediately != null && mFailQueueCommandImmediately.contains(
messageId)) {
@@ -2113,17 +2305,13 @@
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
- final int subscribeId = 15;
+ final byte subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
final String peerSsi = "some peer ssi data";
final String peerMatchFilter = "filter binary array represented as string";
final int rangingId = 18423;
final RttManager.RttParams[] params = new RttManager.RttParams[2];
- params[0] = new RttManager.RttParams();
- params[0].bssid = Integer.toString(requestorId);
- params[1] = new RttManager.RttParams();
- params[1].bssid = Integer.toString(requestorId + 5);
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
@@ -2134,6 +2322,7 @@
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> peerIdIdCaptor = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<WifiAwareClientState> clientCaptor =
ArgumentCaptor.forClass(WifiAwareClientState.class);
ArgumentCaptor<RttManager.RttParams[]> rttParamsCaptor =
@@ -2152,7 +2341,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -2160,21 +2349,27 @@
// (2) subscribe & match
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
+ eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
- inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
- peerMatchFilter.getBytes());
+ inOrder.verify(mockSessionCallback).onMatch(peerIdIdCaptor.capture(),
+ eq(peerSsi.getBytes()), eq(peerMatchFilter.getBytes()));
// (3) start ranging: pass along a valid peer ID and an invalid one
+ params[0] = new RttManager.RttParams();
+ params[0].bssid = Integer.toString(peerIdIdCaptor.getValue());
+ params[1] = new RttManager.RttParams();
+ params[1].bssid = Integer.toString(peerIdIdCaptor.getValue() + 5);
+
mDut.startRanging(clientId, sessionId.getValue(), params, rangingId);
mMockLooper.dispatchAll();
inOrder.verify(mMockAwareRttStateManager).startRanging(eq(rangingId),
clientCaptor.capture(), rttParamsCaptor.capture());
- collector.checkThat("RttParams[0].bssid", "06:07:08:09:0A:0B",
+ collector.checkThat("RttParams[0].bssid", "06:07:08:09:0a:0b",
equalTo(rttParamsCaptor.getValue()[0].bssid));
collector.checkThat("RttParams[1].bssid", "", equalTo(rttParamsCaptor.getValue()[1].bssid));
@@ -2243,7 +2438,7 @@
mDut.connect(clientId1, uid, pid, callingPackage, mockCallback1, configRequest1, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
- crCapture.capture(), eq(false), eq(true));
+ crCapture.capture(), eq(false), eq(true), eq(true), eq(false));
collector.checkThat("merge: stage 1", crCapture.getValue(), equalTo(configRequest1));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
@@ -2259,7 +2454,7 @@
mDut.connect(clientId3, uid, pid, callingPackage, mockCallback3, configRequest3, true);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
- crCapture.capture(), eq(true), eq(false));
+ crCapture.capture(), eq(true), eq(false), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback3).onConnectSuccess(clientId3);
@@ -2280,7 +2475,7 @@
mMockLooper.dispatchAll();
validateInternalClientInfoCleanedUp(clientId3);
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
- crCapture.capture(), eq(false), eq(false));
+ crCapture.capture(), eq(false), eq(false), eq(true), eq(false));
collector.checkThat("configRequest1", configRequest1, equalTo(crCapture.getValue()));
@@ -2291,12 +2486,81 @@
mDut.disconnect(clientId1);
mMockLooper.dispatchAll();
validateInternalClientInfoCleanedUp(clientId1);
- inOrder.verify(mMockNative).disable((short) 0);
+ inOrder.verify(mMockNative).disable(anyShort());
verifyNoMoreInteractions(mMockNative, mockCallback1, mockCallback2, mockCallback3);
}
/**
+ * Validate that identical configuration but with different identity callback requirements
+ * trigger the correct HAL sequence.
+ * 1. Attach w/o identity -> enable
+ * 2. Attach w/o identity -> nop
+ * 3. Attach w/ identity -> re-configure
+ * 4. Attach w/o identity -> nop
+ * 5. Attach w/ identity -> nop
+ */
+ @Test
+ public void testConfigsIdentityCallback() throws Exception {
+ int clientId = 9999;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+
+ InOrder inOrder = inOrder(mMockNative, mockCallback);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (1) attach w/o identity
+ mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ any(ConfigRequest.class), eq(false), eq(true), eq(true), eq(false));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (2) attach w/o identity
+ ++clientId;
+ mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (3) attach w/ identity
+ ++clientId;
+ mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, true);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ any(ConfigRequest.class), eq(true), eq(false), eq(true), eq(false));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (4) attach w/o identity
+ ++clientId;
+ mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (5) attach w/ identity
+ ++clientId;
+ mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, true);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ verifyNoMoreInteractions(mMockNative, mockCallback);
+ }
+
+ /**
* Summary: disconnect a client while there are pending transactions.
*/
@Test
@@ -2310,7 +2574,7 @@
final int masterPref = 111;
final String serviceName = "some-service-name";
final String ssi = "some much longer and more arbitrary data";
- final int publishId = 22;
+ final byte publishId = 22;
ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
.setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
@@ -2335,7 +2599,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -2343,7 +2607,8 @@
// (2) publish (no response yet)
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq((byte) 0),
+ eq(publishConfig));
// (3) disconnect (but doesn't get executed until get a RESPONSE to the
// previous publish)
@@ -2355,7 +2620,7 @@
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(anyInt());
inOrder.verify(mMockNative).stopPublish((short) 0, publishId);
- inOrder.verify(mMockNative).disable((short) 0);
+ inOrder.verify(mMockNative).disable(anyShort());
validateInternalClientInfoCleanedUp(clientId);
@@ -2409,7 +2674,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -2417,7 +2682,8 @@
// (2) publish - no response
mDut.publish(clientId, publishConfig, mockPublishSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq((byte) 0),
+ eq(publishConfig));
verifyNoMoreInteractions(mMockNative, mockCallback, mockPublishSessionCallback);
}
@@ -2452,7 +2718,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
}
@@ -2464,7 +2730,7 @@
*/
@Test
public void testInvalidCallbackIdParameters() throws Exception {
- final int pubSubId = 1235;
+ final byte pubSubId = 125;
final int clientId = 132;
final int uid = 1000;
final int pid = 2000;
@@ -2486,7 +2752,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
short transactionIdConfig = transactionId.getValue();
mDut.onConfigSuccessResponse(transactionIdConfig);
mMockLooper.dispatchAll();
@@ -2519,7 +2785,7 @@
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
- final int publishId = 25;
+ final byte publishId = 25;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
PublishConfig publishConfig = new PublishConfig.Builder().build();
@@ -2542,7 +2808,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
- eq(false), eq(true));
+ eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -2550,7 +2816,8 @@
// (2) publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq((byte) 0),
+ eq(publishConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
@@ -2573,7 +2840,7 @@
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
- final int subscribeId = 25;
+ final byte subscribeId = 25;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
PublishConfig publishConfig = new PublishConfig.Builder().build();
@@ -2596,7 +2863,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
- eq(configRequest), eq(false), eq(true));
+ eq(configRequest), eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -2604,7 +2871,8 @@
// (2) subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
+ eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
@@ -2648,7 +2916,7 @@
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
- eq(configRequest), eq(false), eq(true));
+ eq(configRequest), eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
@@ -2658,10 +2926,11 @@
// (2) publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq((byte) 0),
+ eq(publishConfig));
// (3) publish-success
- mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, i + 1);
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, (byte) (i + 1));
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
@@ -2672,6 +2941,121 @@
}
}
+ /**
+ * Validate configuration changes on power state changes when Aware is not disabled on doze.
+ */
+ @Test
+ public void testConfigOnPowerStateChanges() throws Exception {
+ final int clientId = 188;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+
+ setSettableParam(WifiAwareStateManager.PARAM_ON_IDLE_DISABLE_AWARE, Integer.toString(0),
+ true);
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ InOrder inOrder = inOrder(mMockNative, mockCallback);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (1) connect
+ mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(false), eq(true), eq(true), eq(false));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (2) power state change: SCREEN OFF
+ simulatePowerStateChangeInteractive(false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(false), eq(false), eq(false), eq(false));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+
+ // (3) power state change: DOZE
+ simulatePowerStateChangeDoze(true);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(false), eq(false), eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+
+ // (4) restore power state to default
+ simulatePowerStateChangeInteractive(true); // effectively treated as no-doze
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(false), eq(false), eq(true), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+
+ verifyNoMoreInteractions(mMockNative, mockCallback);
+ }
+
+ /**
+ * Validate aware enable/disable during doze transitions.
+ */
+ @Test
+ public void testEnableDisableOnDoze() throws Exception {
+ final int clientId = 188;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+
+ setSettableParam(WifiAwareStateManager.PARAM_ON_IDLE_DISABLE_AWARE, Integer.toString(1),
+ true);
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ InOrder inOrder = inOrder(mMockContext, mMockNativeManager, mMockNative, mockCallback);
+ inOrder.verify(mMockNativeManager).start();
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (1) connect
+ mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(false), eq(true), eq(true), eq(false));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (3) power state change: DOZE
+ simulatePowerStateChangeDoze(true);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).disable(transactionId.capture());
+ mDut.onDisableResponse(transactionId.getValue(), NanStatusType.SUCCESS);
+ validateCorrectAwareStatusChangeBroadcast(inOrder, false);
+
+ // (4) power state change: SCREEN ON (but DOZE still on - fakish but expect no changes)
+ simulatePowerStateChangeInteractive(false);
+ mMockLooper.dispatchAll();
+
+ // (5) power state change: DOZE OFF
+ simulatePowerStateChangeDoze(false);
+ mMockLooper.dispatchAll();
+ validateCorrectAwareStatusChangeBroadcast(inOrder, true);
+
+ verifyNoMoreInteractions(mMockNativeManager, mMockNative, mockCallback);
+ }
+
/*
* Tests of internal state of WifiAwareStateManager: very limited (not usually
* a good idea). However, these test that the internal state is cleaned-up
@@ -2749,6 +3133,16 @@
/*
* Utilities
*/
+ private void setSettableParam(String name, String value, boolean expectSuccess) {
+ PrintWriter pwMock = mock(PrintWriter.class);
+ WifiAwareShellCommand parentShellMock = mock(WifiAwareShellCommand.class);
+ when(parentShellMock.getNextArgRequired()).thenReturn("set").thenReturn(name).thenReturn(
+ value);
+ when(parentShellMock.getErrPrintWriter()).thenReturn(pwMock);
+
+ collector.checkThat(mDut.onCommand(parentShellMock), equalTo(expectSuccess ? 0 : -1));
+ }
+
private void dumpDut(String prefix) {
StringWriter sw = new StringWriter();
mDut.dump(null, new PrintWriter(sw), null);
@@ -2823,6 +3217,29 @@
}
}
+ /**
+ * Simulate power state change due to doze. Changes the power manager return values and
+ * dispatches a broadcast.
+ */
+ private void simulatePowerStateChangeDoze(boolean isDozeOn) {
+ when(mMockPowerManager.isDeviceIdleMode()).thenReturn(isDozeOn);
+
+ Intent intent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+ mPowerBcastReceiver.onReceive(mMockContext, intent);
+ }
+
+ /**
+ * Simulate power state change due to interactive mode change (screen on/off). Changes the power
+ * manager return values and dispatches a broadcast.
+ */
+ private void simulatePowerStateChangeInteractive(boolean isInteractive) {
+ when(mMockPowerManager.isInteractive()).thenReturn(isInteractive);
+
+ Intent intent = new Intent(
+ isInteractive ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF);
+ mPowerBcastReceiver.onReceive(mMockContext, intent);
+ }
+
private static Capabilities getCapabilities() {
Capabilities cap = new Capabilities();
cap.maxConcurrentAwareClusters = 1;
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigStoreDataTest.java
index 8e808ef..7e05c3d 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigStoreDataTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigStoreDataTest.java
@@ -60,6 +60,7 @@
private static final String TEST_CLIENT_PRIVATE_KEY_ALIAS = "ClientPrivateKey";
private static final long TEST_PROVIDER_ID = 1;
private static final int TEST_CREATOR_UID = 1234;
+ private static final boolean TEST_HAS_EVER_CONNECTED = true;
@Mock WifiKeyStore mKeyStore;
@Mock SIMAccessor mSimAccessor;
@@ -238,7 +239,7 @@
providerList.add(new PasspointProvider(createFullPasspointConfiguration(),
mKeyStore, mSimAccessor, TEST_PROVIDER_ID, TEST_CREATOR_UID,
TEST_CA_CERTIFICATE_ALIAS, TEST_CLIENT_CERTIFICATE_ALIAS,
- TEST_CLIENT_PRIVATE_KEY_ALIAS));
+ TEST_CLIENT_PRIVATE_KEY_ALIAS, TEST_HAS_EVER_CONNECTED));
// Serialize data for user store.
when(mDataSource.getProviders()).thenReturn(providerList);
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
index 70ae354..01566c2 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
@@ -18,9 +18,7 @@
import static android.net.wifi.WifiManager.ACTION_PASSPOINT_DEAUTH_IMMINENT;
import static android.net.wifi.WifiManager.ACTION_PASSPOINT_ICON;
-import static android.net.wifi.WifiManager.ACTION_PASSPOINT_OSU_PROVIDERS_LIST;
import static android.net.wifi.WifiManager.ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION;
-import static android.net.wifi.WifiManager.EXTRA_ANQP_ELEMENT_DATA;
import static android.net.wifi.WifiManager.EXTRA_BSSID_LONG;
import static android.net.wifi.WifiManager.EXTRA_DELAY;
import static android.net.wifi.WifiManager.EXTRA_ESS;
@@ -49,10 +47,13 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Icon;
+import android.net.Uri;
import android.net.wifi.EAPConstants;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.WifiSsid;
+import android.net.wifi.hotspot2.OsuProvider;
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.net.wifi.hotspot2.pps.Credential;
import android.net.wifi.hotspot2.pps.HomeSp;
@@ -68,11 +69,14 @@
import com.android.server.wifi.WifiConfigManager;
import com.android.server.wifi.WifiConfigStore;
import com.android.server.wifi.WifiKeyStore;
+import com.android.server.wifi.WifiMetrics;
import com.android.server.wifi.WifiNative;
import com.android.server.wifi.hotspot2.anqp.ANQPElement;
import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType;
import com.android.server.wifi.hotspot2.anqp.DomainNameElement;
-import com.android.server.wifi.hotspot2.anqp.RawByteElement;
+import com.android.server.wifi.hotspot2.anqp.HSOsuProvidersElement;
+import com.android.server.wifi.hotspot2.anqp.I18Name;
+import com.android.server.wifi.hotspot2.anqp.OsuProviderInfo;
import com.android.server.wifi.util.ScanResultUtil;
import org.junit.Before;
@@ -87,6 +91,7 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
/**
@@ -124,6 +129,7 @@
@Mock WifiConfigManager mWifiConfigManager;
@Mock WifiConfigStore mWifiConfigStore;
@Mock PasspointConfigStoreData.DataSource mDataSource;
+ @Mock WifiMetrics mWifiMetrics;
PasspointManager mManager;
/** Sets up test. */
@@ -135,7 +141,7 @@
.thenReturn(mAnqpRequestManager);
when(mObjectFactory.makeCertificateVerifier()).thenReturn(mCertVerifier);
mManager = new PasspointManager(mContext, mWifiNative, mWifiKeyStore, mClock,
- mSimAccessor, mObjectFactory, mWifiConfigManager, mWifiConfigStore);
+ mSimAccessor, mObjectFactory, mWifiConfigManager, mWifiConfigStore, mWifiMetrics);
ArgumentCaptor<PasspointEventHandler.Callbacks> callbacks =
ArgumentCaptor.forClass(PasspointEventHandler.Callbacks.class);
verify(mObjectFactory).makePasspointEventHandler(any(WifiNative.class),
@@ -268,6 +274,7 @@
scanResult.SSID = TEST_SSID;
scanResult.BSSID = TEST_BSSID_STRING;
scanResult.hessid = TEST_HESSID;
+ scanResult.flags = ScanResult.FLAG_PASSPOINT_NETWORK;
return scanResult;
}
@@ -291,36 +298,6 @@
}
/**
- * Verify that the ANQP elements will be added to the AQNP cache and an
- * {@link WifiManager#ACTION_PASSPOINT_OSU_PROVIDER_LIST} intent will be broadcasted when
- * receiving an ANQP response containing OSU Providers element.
- *
- * @throws Exception
- */
- @Test
- public void anqpResponseWithOSUProviders() throws Exception {
- Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
- byte[] testData = new byte[] {0x12, 0x34, 0x56, 0x78};
- anqpElementMap.put(ANQPElementType.HSOSUProviders,
- new RawByteElement(ANQPElementType.HSOSUProviders, testData));
-
- when(mAnqpRequestManager.onRequestCompleted(TEST_BSSID, true)).thenReturn(TEST_ANQP_KEY);
- mCallbacks.onANQPResponse(TEST_BSSID, anqpElementMap);
- verify(mAnqpCache).addEntry(TEST_ANQP_KEY, anqpElementMap);
-
- // Verify the broadcast intent for OSU providers.
- ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
- verify(mContext).sendBroadcastAsUser(intent.capture(), eq(UserHandle.ALL),
- eq(android.Manifest.permission.ACCESS_WIFI_STATE));
- assertEquals(ACTION_PASSPOINT_OSU_PROVIDERS_LIST, intent.getValue().getAction());
- assertTrue(intent.getValue().getExtras().containsKey(EXTRA_BSSID_LONG));
- assertEquals(TEST_BSSID, intent.getValue().getExtras().getLong(EXTRA_BSSID_LONG));
- assertTrue(intent.getValue().getExtras().containsKey(EXTRA_ANQP_ELEMENT_DATA));
- assertTrue(Arrays.equals(testData,
- intent.getValue().getExtras().getByteArray(EXTRA_ANQP_ELEMENT_DATA)));
- }
-
- /**
* Verify that no ANQP elements will be added to the ANQP cache on receiving a successful
* response for a request that's not sent by us.
*
@@ -436,6 +413,8 @@
@Test
public void addProviderWithNullConfig() throws Exception {
assertFalse(mManager.addOrUpdateProvider(null, TEST_CREATOR_UID));
+ verify(mWifiMetrics).incrementNumPasspointProviderInstallation();
+ verify(mWifiMetrics, never()).incrementNumPasspointProviderInstallSuccess();
}
/**
@@ -446,6 +425,8 @@
@Test
public void addProviderWithEmptyConfig() throws Exception {
assertFalse(mManager.addOrUpdateProvider(new PasspointConfiguration(), TEST_CREATOR_UID));
+ verify(mWifiMetrics).incrementNumPasspointProviderInstallation();
+ verify(mWifiMetrics, never()).incrementNumPasspointProviderInstallSuccess();
}
/**
@@ -460,6 +441,8 @@
// EAP-TLS not allowed for user credential.
config.getCredential().getUserCredential().setEapType(EAPConstants.EAP_TLS);
assertFalse(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID));
+ verify(mWifiMetrics).incrementNumPasspointProviderInstallation();
+ verify(mWifiMetrics, never()).incrementNumPasspointProviderInstallSuccess();
}
/**
@@ -476,6 +459,9 @@
assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID));
verifyInstalledConfig(config);
verify(mWifiConfigManager).saveToStore(true);
+ verify(mWifiMetrics).incrementNumPasspointProviderInstallation();
+ verify(mWifiMetrics).incrementNumPasspointProviderInstallSuccess();
+ reset(mWifiMetrics);
reset(mWifiConfigManager);
// Verify content in the data source.
@@ -489,6 +475,8 @@
assertTrue(mManager.removeProvider(TEST_FQDN));
verify(provider).uninstallCertsAndKeys();
verify(mWifiConfigManager).saveToStore(true);
+ verify(mWifiMetrics).incrementNumPasspointProviderUninstallation();
+ verify(mWifiMetrics).incrementNumPasspointProviderUninstallSuccess();
assertTrue(mManager.getProviderConfigs().isEmpty());
// Verify content in the data source.
@@ -511,6 +499,9 @@
assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID));
verifyInstalledConfig(config);
verify(mWifiConfigManager).saveToStore(true);
+ verify(mWifiMetrics).incrementNumPasspointProviderInstallation();
+ verify(mWifiMetrics).incrementNumPasspointProviderInstallSuccess();
+ reset(mWifiMetrics);
reset(mWifiConfigManager);
// Verify content in the data source.
@@ -524,6 +515,8 @@
assertTrue(mManager.removeProvider(TEST_FQDN));
verify(provider).uninstallCertsAndKeys();
verify(mWifiConfigManager).saveToStore(true);
+ verify(mWifiMetrics).incrementNumPasspointProviderUninstallation();
+ verify(mWifiMetrics).incrementNumPasspointProviderUninstallSuccess();
assertTrue(mManager.getProviderConfigs().isEmpty());
// Verify content in the data source.
@@ -549,6 +542,9 @@
assertTrue(mManager.addOrUpdateProvider(origConfig, TEST_CREATOR_UID));
verifyInstalledConfig(origConfig);
verify(mWifiConfigManager).saveToStore(true);
+ verify(mWifiMetrics).incrementNumPasspointProviderInstallation();
+ verify(mWifiMetrics).incrementNumPasspointProviderInstallSuccess();
+ reset(mWifiMetrics);
reset(mWifiConfigManager);
// Verify data source content.
@@ -566,6 +562,8 @@
assertTrue(mManager.addOrUpdateProvider(newConfig, TEST_CREATOR_UID));
verifyInstalledConfig(newConfig);
verify(mWifiConfigManager).saveToStore(true);
+ verify(mWifiMetrics).incrementNumPasspointProviderInstallation();
+ verify(mWifiMetrics).incrementNumPasspointProviderInstallSuccess();
// Verify data source content.
List<PasspointProvider> newProviders = mDataSource.getProviders();
@@ -588,6 +586,8 @@
when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore),
eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID))).thenReturn(provider);
assertFalse(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID));
+ verify(mWifiMetrics).incrementNumPasspointProviderInstallation();
+ verify(mWifiMetrics, never()).incrementNumPasspointProviderInstallSuccess();
}
/**
@@ -601,6 +601,8 @@
doThrow(new GeneralSecurityException())
.when(mCertVerifier).verifyCaCert(any(X509Certificate.class));
assertFalse(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID));
+ verify(mWifiMetrics).incrementNumPasspointProviderInstallation();
+ verify(mWifiMetrics, never()).incrementNumPasspointProviderInstallSuccess();
}
/**
@@ -619,6 +621,8 @@
assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID));
verify(mCertVerifier, never()).verifyCaCert(any(X509Certificate.class));
verifyInstalledConfig(config);
+ verify(mWifiMetrics).incrementNumPasspointProviderInstallation();
+ verify(mWifiMetrics).incrementNumPasspointProviderInstallSuccess();
}
/**
@@ -629,6 +633,8 @@
@Test
public void removeNonExistingProvider() throws Exception {
assertFalse(mManager.removeProvider(TEST_FQDN));
+ verify(mWifiMetrics).incrementNumPasspointProviderUninstallation();
+ verify(mWifiMetrics, never()).incrementNumPasspointProviderUninstallSuccess();
}
/**
@@ -803,8 +809,8 @@
}
/**
- * Verify that a {@code null} will returned when trying to get a matching
- * {@link WifiConfiguration} a {@code null} {@link ScanResult}.
+ * Verify that a {@code null} will be returned when trying to get a matching
+ * {@link WifiConfiguration} for a {@code null} {@link ScanResult}.
*
* @throws Exception
*/
@@ -814,6 +820,132 @@
}
/**
+ * Verify that a {@code null} will be returned when trying to get a matching
+ * {@link WifiConfiguration} for a {@link ScanResult} with a {@code null} BSSID.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getMatchingWifiConfigWithNullBSSID() throws Exception {
+ ScanResult scanResult = createTestScanResult();
+ scanResult.BSSID = null;
+ assertNull(mManager.getMatchingWifiConfig(scanResult));
+ }
+
+ /**
+ * Verify that a {@code null} will be returned when trying to get a matching
+ * {@link WifiConfiguration} for a {@link ScanResult} with an invalid BSSID.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getMatchingWifiConfigWithInvalidBSSID() throws Exception {
+ ScanResult scanResult = createTestScanResult();
+ scanResult.BSSID = "asdfdasfas";
+ assertNull(mManager.getMatchingWifiConfig(scanResult));
+ }
+
+ /**
+ * Verify that a {@code null} will be returned when trying to get a matching
+ * {@link WifiConfiguration} for a non-Passpoint AP.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getMatchingWifiConfigForNonPasspointAP() throws Exception {
+ ScanResult scanResult = createTestScanResult();
+ scanResult.flags = 0;
+ assertNull(mManager.getMatchingWifiConfig(scanResult));
+ }
+
+ /**
+ * Verify that an empty list will be returned when retrieving OSU providers for an AP with
+ * null scan result.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getMatchingOsuProvidersForNullScanResult() throws Exception {
+ assertTrue(mManager.getMatchingOsuProviders(null).isEmpty());
+ }
+
+ /**
+ * Verify that an empty list will be returned when retrieving OSU providers for an AP with
+ * invalid BSSID.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getMatchingOsuProvidersForInvalidBSSID() throws Exception {
+ ScanResult scanResult = createTestScanResult();
+ scanResult.BSSID = "asdfdasfas";
+ assertTrue(mManager.getMatchingOsuProviders(scanResult).isEmpty());
+ }
+
+ /**
+ * Verify that an empty list will be returned when retrieving OSU providers for a
+ * non-Passpoint AP.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getMatchingOsuProvidersForNonPasspointAP() throws Exception {
+ ScanResult scanResult = createTestScanResult();
+ scanResult.flags = 0;
+ assertTrue(mManager.getMatchingOsuProviders(scanResult).isEmpty());
+ }
+
+ /**
+ * Verify that an empty list will be returned when no match is found from the ANQP cache.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getMatchingOsuProviderWithNoMatch() throws Exception {
+ when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(null);
+ assertTrue(mManager.getMatchingOsuProviders(createTestScanResult()).isEmpty());
+ }
+
+ /**
+ * Verify that an expected provider list will be returned when a match is found from
+ * the ANQP cache.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getMatchingOsuProvidersWithMatch() throws Exception {
+ // Test data.
+ WifiSsid osuSsid = WifiSsid.createFromAsciiEncoded("Test SSID");
+ String friendlyName = "Test Provider";
+ String serviceDescription = "Dummy Service";
+ Uri serverUri = Uri.parse("https://test.com");
+ String nai = "access.test.com";
+ List<Integer> methodList = Arrays.asList(1);
+ List<I18Name> friendlyNames = Arrays.asList(
+ new I18Name(Locale.ENGLISH.getLanguage(), Locale.ENGLISH, friendlyName));
+ List<I18Name> serviceDescriptions = Arrays.asList(
+ new I18Name(Locale.ENGLISH.getLanguage(), Locale.ENGLISH, serviceDescription));
+
+ // Setup OSU providers ANQP element.
+ List<OsuProviderInfo> providerInfoList = new ArrayList<>();
+ providerInfoList.add(new OsuProviderInfo(
+ friendlyNames, serverUri, methodList, null, nai, serviceDescriptions));
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.HSOSUProviders,
+ new HSOsuProvidersElement(osuSsid, providerInfoList));
+ ANQPData entry = new ANQPData(mClock, anqpElementMap);
+
+ // Setup expectation.
+ OsuProvider provider = new OsuProvider(
+ osuSsid, friendlyName, serviceDescription, serverUri, nai, methodList, null);
+ List<OsuProvider> expectedList = new ArrayList<>();
+ expectedList.add(provider);
+
+ when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(entry);
+ assertEquals(expectedList, mManager.getMatchingOsuProviders(createTestScanResult()));
+ }
+
+ /**
* Verify that the provider list maintained by the PasspointManager after the list is updated
* in the data source.
*
@@ -1083,4 +1215,62 @@
assertFalse(PasspointManager.addLegacyPasspointConfig(wifiConfig));
}
+
+ /**
+ * Verify that the provider's "hasEverConnected" flag will be set to true and the associated
+ * metric is updated after the provider was used to successfully connect to a Passpoint
+ * network for the first time.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void providerNetworkConnectedFirstTime() throws Exception {
+ PasspointProvider provider = addTestProvider();
+ when(provider.getHasEverConnected()).thenReturn(false);
+ mManager.onPasspointNetworkConnected(TEST_FQDN);
+ verify(provider).setHasEverConnected(eq(true));
+ }
+
+ /**
+ * Verify that the provider's "hasEverConnected" flag the associated metric is not updated
+ * after the provider was used to successfully connect to a Passpoint network for non-first
+ * time.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void providerNetworkConnectedNotFirstTime() throws Exception {
+ PasspointProvider provider = addTestProvider();
+ when(provider.getHasEverConnected()).thenReturn(true);
+ mManager.onPasspointNetworkConnected(TEST_FQDN);
+ verify(provider, never()).setHasEverConnected(anyBoolean());
+ }
+
+ /**
+ * Verify that the expected Passpoint metrics are updated when
+ * {@link PasspointManager#updateMetrics} is invoked.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void updateMetrics() throws Exception {
+ PasspointProvider provider = addTestProvider();
+
+ // Provider have not provided a successful network connection.
+ int expectedInstalledProviders = 1;
+ int expectedConnectedProviders = 0;
+ when(provider.getHasEverConnected()).thenReturn(false);
+ mManager.updateMetrics();
+ verify(mWifiMetrics).updateSavedPasspointProfiles(
+ eq(expectedInstalledProviders), eq(expectedConnectedProviders));
+ reset(provider);
+ reset(mWifiMetrics);
+
+ // Provider have provided a successful network connection.
+ expectedConnectedProviders = 1;
+ when(provider.getHasEverConnected()).thenReturn(true);
+ mManager.updateMetrics();
+ verify(mWifiMetrics).updateSavedPasspointProfiles(
+ eq(expectedInstalledProviders), eq(expectedConnectedProviders));
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java
index 2224486..9f61ca0 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java
@@ -333,4 +333,30 @@
assertEquals(ScanResultUtil.createQuotedSSID(TEST_SSID2), config.SSID);
assertEquals(TEST_NETWORK_ID, config.networkId);
}
+
+ /**
+ * Verify that null will be returned when matching a SIM credential provider without SIM
+ * card installed.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void evaluateScanMatchingSIMProviderWithoutSIMCard() throws Exception {
+ // Setup ScanDetail and match providers.
+ List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] {
+ generateScanDetail(TEST_SSID1)});
+ PasspointProvider testProvider = mock(PasspointProvider.class);
+ Pair<PasspointProvider, PasspointMatch> homeProvider = Pair.create(
+ testProvider, PasspointMatch.HomeProvider);
+
+ List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
+ when(mPasspointManager.matchProvider(any(ScanResult.class))).thenReturn(homeProvider);
+ when(testProvider.isSimCredential()).thenReturn(true);
+ when(mWifiConfigManager.isSimPresent()).thenReturn(false);
+ assertEquals(null, mEvaluator.evaluateNetworks(
+ scanDetails, null, null, false, false, connectableNetworks));
+ assertTrue(connectableNetworks.isEmpty());
+ verify(testProvider, never()).getWifiConfig();
+
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java
index c416a96..9ee9fc6 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java
@@ -898,4 +898,92 @@
assertEquals(passpointConfig, PasspointProvider.convertFromWifiConfig(wifiConfig));
}
+ /**
+ * Verify that {@link PasspointProvider#isSimCredential} will return true for provider that's
+ * backed by a SIM credential.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void providerBackedBySimCredential() throws Exception {
+ // Test data.
+ String fqdn = "test.com";
+ String friendlyName = "Friendly Name";
+ long[] rcOIs = new long[] {0x1234L, 0x2345L};
+ String realm = "realm.com";
+ String imsi = "1234*";
+
+ // Create provider with SIM credential.
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn(fqdn);
+ homeSp.setFriendlyName(friendlyName);
+ homeSp.setRoamingConsortiumOis(rcOIs);
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ credential.setRealm(realm);
+ Credential.SimCredential simCredential = new Credential.SimCredential();
+ simCredential.setImsi(imsi);
+ simCredential.setEapType(EAPConstants.EAP_SIM);
+ credential.setSimCredential(simCredential);
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+
+ assertTrue(mProvider.isSimCredential());
+ }
+
+ /**
+ * Verify that {@link PasspointProvider#isSimCredential} will return false for provider that's
+ * not backed by a SIM credential.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void providerNotBackedBySimCredential() throws Exception {
+ // Test data.
+ String fqdn = "test.com";
+ String friendlyName = "Friendly Name";
+ long[] rcOIs = new long[] {0x1234L, 0x2345L};
+ String realm = "realm.com";
+
+ // Create provider with certificate credential.
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn(fqdn);
+ homeSp.setFriendlyName(friendlyName);
+ homeSp.setRoamingConsortiumOis(rcOIs);
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ Credential.CertificateCredential certCredential = new Credential.CertificateCredential();
+ certCredential.setCertType(Credential.CertificateCredential.CERT_TYPE_X509V3);
+ credential.setCertCredential(certCredential);
+ credential.setRealm(realm);
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+
+ assertFalse(mProvider.isSimCredential());
+ }
+
+ /**
+ * Verify that hasEverConnected flag is set correctly using
+ * {@link PasspointProvider#setHasEverConnected}.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void setHasEverConnected() throws Exception {
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn("test1");
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ credential.setUserCredential(new Credential.UserCredential());
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+ verifyInstalledConfig(config, true);
+
+ assertFalse(mProvider.getHasEverConnected());
+ mProvider.setHasEverConnected(true);
+ assertTrue(mProvider.getHasEverConnected());
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ANQPParserTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ANQPParserTest.java
index 8f019e0..59332e4 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ANQPParserTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ANQPParserTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
+import android.net.wifi.WifiSsid;
import android.test.suitebuilder.annotation.SmallTest;
import org.junit.Test;
@@ -216,6 +217,22 @@
}
/**
+ * Helper function for generating payload for a Hotspot 2.0 OSU Providers List ANQP
+ * element.
+ *
+ * @param osuSsidBytes Bytes of OSU SSID
+ * @return byte[]
+ */
+ private static byte[] getHSOsuProvidersPayload(byte[] osuSsidBytes) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write((byte) osuSsidBytes.length);
+ out.write(osuSsidBytes);
+ out.write((byte) 1);
+ out.write(OsuProviderInfoTestUtil.TEST_OSU_PROVIDER_INFO_RAW_BYTES);
+ return out.toByteArray();
+ }
+
+ /**
* Helper function for generating payload for a list of I18Name.
*
* @param language Array of language
@@ -473,10 +490,12 @@
*/
@Test
public void parseHSOUSProvidersElement() throws Exception {
- byte[] data = new byte[10];
+ byte[] osuSsidBytes = "Test SSID".getBytes(StandardCharsets.UTF_8);
+ byte[] data = getHSOsuProvidersPayload(osuSsidBytes);
- RawByteElement expected =
- new RawByteElement(Constants.ANQPElementType.HSOSUProviders, data);
+ HSOsuProvidersElement expected = new HSOsuProvidersElement(
+ WifiSsid.createFromByteArray(osuSsidBytes),
+ Arrays.asList(OsuProviderInfoTestUtil.TEST_OSU_PROVIDER_INFO));
ByteBuffer buffer = ByteBuffer.wrap(data);
assertEquals(expected,
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSIconFileElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSIconFileElementTest.java
new file mode 100644
index 0000000..c72d1d0
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSIconFileElementTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 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.hotspot2.anqp;
+
+import static org.junit.Assert.assertEquals;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.HSIconFileElement}.
+ */
+@SmallTest
+public class HSIconFileElementTest {
+ private static final String TEST_ICON_TYPE = "png";
+ private static final byte[] TEST_ICON_DATA = new byte[8];
+
+ /**
+ * Utility function for generating test data.
+ *
+ * @param statusCode Status code of the icon file download
+ * @return byte[]
+ */
+ private static byte[] getTestData(int statusCode) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write((byte) statusCode);
+ if (statusCode != HSIconFileElement.STATUS_CODE_SUCCESS) {
+ // No need to write other data if status code is not success.
+ return out.toByteArray();
+ }
+
+ byte[] iconTypeBytes = TEST_ICON_TYPE.getBytes(StandardCharsets.US_ASCII);
+ out.write(iconTypeBytes.length);
+ out.write(iconTypeBytes);
+ out.write(TEST_ICON_DATA.length & 0xFF);
+ out.write((TEST_ICON_DATA.length >> 8) & 0xFF);
+ out.write(TEST_ICON_DATA);
+ return out.toByteArray();
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing an empty buffer.
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseEmptyBuffer() throws Exception {
+ HSIconFileElement.parse(ByteBuffer.allocate(0));
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing a truncated buffer
+ * (missing a byte at the end).
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseTruncatedBuffer() throws Exception {
+ ByteBuffer buffer = ByteBuffer.wrap(getTestData(HSIconFileElement.STATUS_CODE_SUCCESS));
+ buffer.limit(buffer.remaining() - 1);
+ HSIconFileElement.parse(buffer);
+ }
+
+ /**
+ * Verify that an expected {@link HSIconFileElement} is returned when parsing a buffer
+ * containing icon data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithIconData() throws Exception {
+ ByteBuffer buffer = ByteBuffer.wrap(getTestData(HSIconFileElement.STATUS_CODE_SUCCESS));
+ HSIconFileElement expected = new HSIconFileElement(
+ HSIconFileElement.STATUS_CODE_SUCCESS, TEST_ICON_TYPE, TEST_ICON_DATA);
+ assertEquals(expected, HSIconFileElement.parse(buffer));
+ }
+
+ /**
+ * Verify that an expected {@link HSIconFileElement} is returned when parsing a buffer
+ * without icon data (icon file not found).
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithoutIconData() throws Exception {
+ ByteBuffer buffer =
+ ByteBuffer.wrap(getTestData(HSIconFileElement.STATUS_CODE_FILE_NOT_FOUND));
+ HSIconFileElement expected =
+ new HSIconFileElement(HSIconFileElement.STATUS_CODE_FILE_NOT_FOUND, null, null);
+ assertEquals(expected, HSIconFileElement.parse(buffer));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSOsuProvidersElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSOsuProvidersElementTest.java
new file mode 100644
index 0000000..c4f7387
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSOsuProvidersElementTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 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.hotspot2.anqp;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.wifi.WifiSsid;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.HSOsuProvidersElement}.
+ */
+@SmallTest
+public class HSOsuProvidersElementTest {
+ private static final byte[] TEST_OSU_SSID_BYTES = "Test SSID".getBytes(StandardCharsets.UTF_8);
+ private static final WifiSsid TEST_OSU_SSID =
+ WifiSsid.createFromByteArray(TEST_OSU_SSID_BYTES);
+ private static final List<OsuProviderInfo> TEST_PROVIDER_LIST =
+ Arrays.asList(OsuProviderInfoTestUtil.TEST_OSU_PROVIDER_INFO);
+
+ private static final HSOsuProvidersElement TEST_OSU_PROVIDERS_ELEMENT =
+ new HSOsuProvidersElement(TEST_OSU_SSID, TEST_PROVIDER_LIST);
+
+ /**
+ * Utility function for generating test data.
+ *
+ * @param osuSsidBytes The OSU SSID bytes
+ * @return byte[]
+ */
+ private static byte[] getTestData(byte[] osuSsidBytes) {
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write((byte) osuSsidBytes.length);
+ out.write(osuSsidBytes);
+ out.write((byte) TEST_PROVIDER_LIST.size());
+ out.write(OsuProviderInfoTestUtil.TEST_OSU_PROVIDER_INFO_RAW_BYTES);
+ return out.toByteArray();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing an empty buffer.
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseEmptyBuffer() throws Exception {
+ HSOsuProvidersElement.parse(ByteBuffer.allocate(0));
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing a truncated buffer
+ * (missing a byte at the end).
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseTruncatedBuffer() throws Exception {
+ ByteBuffer buffer = ByteBuffer.wrap(getTestData(TEST_OSU_SSID_BYTES));
+ buffer.limit(buffer.remaining() - 1);
+ HSOsuProvidersElement.parse(buffer);
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when parsing a buffer containing an
+ * invalid OSU SSID.
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseBufferWithInvalidLength() throws Exception {
+ byte[] invalidSsidBytes = new byte[HSOsuProvidersElement.MAXIMUM_OSU_SSID_LENGTH + 1];
+ ByteBuffer buffer = ByteBuffer.wrap(getTestData(invalidSsidBytes));
+ HSOsuProvidersElement.parse(buffer);
+ }
+
+ /**
+ * Verify that an expected {@link HSOsuProvidersElement} will be returned when parsing a buffer
+ * containing pre-defined test data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithTestData() throws Exception {
+ ByteBuffer buffer = ByteBuffer.wrap(getTestData(TEST_OSU_SSID_BYTES));
+ assertEquals(TEST_OSU_PROVIDERS_ELEMENT, HSOsuProvidersElement.parse(buffer));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IconInfoTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IconInfoTest.java
new file mode 100644
index 0000000..429b36a
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IconInfoTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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.hotspot2.anqp;
+
+import static org.junit.Assert.assertEquals;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.IconInfo}.
+ */
+@SmallTest
+public class IconInfoTest {
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing an empty buffer.
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseEmptyBuffer() throws Exception {
+ IconInfo.parse(ByteBuffer.allocate(0));
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing a truncated buffer
+ * (missing a byte at the end).
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseTruncatedBuffer() throws Exception {
+ ByteBuffer buffer = ByteBuffer.wrap(IconInfoTestUtil.TEST_ICON_INFO_RAW_BYTES);
+ buffer.limit(buffer.remaining() - 1);
+ IconInfo.parse(buffer);
+ }
+
+ /**
+ * Verify that an expected {@link IconInfo} will be returned when parsing a buffer containing
+ * pre-defined test data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithTestData() throws Exception {
+ ByteBuffer buffer = ByteBuffer.wrap(IconInfoTestUtil.TEST_ICON_INFO_RAW_BYTES);
+ assertEquals(IconInfoTestUtil.TEST_ICON_INFO, IconInfo.parse(buffer));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IconInfoTestUtil.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IconInfoTestUtil.java
new file mode 100644
index 0000000..fa0e9f8
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IconInfoTestUtil.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 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.hotspot2.anqp;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Utility class containing test data and object for {@link IconInfo}.
+ */
+public class IconInfoTestUtil {
+ // Test data
+ private static final int TEST_WIDTH = 9811;
+ private static final int TEST_HEIGHT = 4523;
+ private static final String TEST_LANGUAGE = "en";
+ private static final String TEST_TYPE = "png";
+ private static final String TEST_FILENAME = "testicon.png";
+
+ /**
+ * {@link IconInfo} object with pre-defined test data.
+ */
+ public static final IconInfo TEST_ICON_INFO =
+ new IconInfo(TEST_WIDTH, TEST_HEIGHT, TEST_LANGUAGE, TEST_TYPE, TEST_FILENAME);
+
+ /**
+ * Raw bytes of icon info with pre-defined test data.
+ */
+ public static final byte[] TEST_ICON_INFO_RAW_BYTES = getTestData();
+
+ /**
+ * Generate raw bytes based on the pre-defined test data.
+ *
+ * @return array of bytes
+ */
+ private static byte[] getTestData() {
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writeShortLE(out, TEST_WIDTH);
+ writeShortLE(out, TEST_HEIGHT);
+ out.write(TEST_LANGUAGE.getBytes(StandardCharsets.US_ASCII));
+ out.write((byte) 0); // Padding for language code.
+ writeByteArrayWithLength(out, TEST_TYPE.getBytes(StandardCharsets.US_ASCII));
+ writeByteArrayWithLength(out, TEST_FILENAME.getBytes(StandardCharsets.UTF_8));
+ return out.toByteArray();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Write the lower 2-bytes of an integer to the given output stream in Little-Endian.
+ *
+ * @param out The output stream to write to
+ * @param value The integer value to write
+ */
+ private static void writeShortLE(ByteArrayOutputStream out, int value) {
+ out.write(value & 0xFF);
+ out.write((value >> 8) & 0xFF);
+ }
+
+ /**
+ * Write the given byte array to the given output stream, the array data is prefixed with a
+ * byte specifying the length of the byte array.
+ *
+ * @param out The output stream to write to
+ * @param data The byte array to write
+ * @throws IOException
+ */
+ private static void writeByteArrayWithLength(ByteArrayOutputStream out, byte[] data)
+ throws IOException {
+ out.write((byte) data.length);
+ out.write(data);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/OsuProviderInfoTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/OsuProviderInfoTest.java
new file mode 100644
index 0000000..8ef93f0
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/OsuProviderInfoTest.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2017 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.hotspot2.anqp;
+
+import static org.junit.Assert.assertEquals;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.OsuProviderInfo}.
+ */
+@SmallTest
+public class OsuProviderInfoTest {
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing an empty buffer.
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseEmptyBuffer() throws Exception {
+ OsuProviderInfo.parse(ByteBuffer.allocate(0));
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing a truncated buffer
+ * (missing a byte at the end).
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseTruncatedBuffer() throws Exception {
+ ByteBuffer buffer = ByteBuffer.wrap(
+ OsuProviderInfoTestUtil.TEST_OSU_PROVIDER_INFO_RAW_BYTES);
+ buffer.limit(buffer.remaining() - 1);
+ OsuProviderInfo.parse(buffer);
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when parsing a buffer containing an
+ * invalid length value.
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseBufferWithInvalidLength() throws Exception {
+ ByteBuffer buffer = ByteBuffer.wrap(
+ OsuProviderInfoTestUtil.TEST_OSU_PROVIDER_INFO_RAW_BYTES_WITH_INVALID_LENGTH);
+ OsuProviderInfo.parse(buffer);
+ }
+
+ /**
+ * Verify that an expected {@link OsuProviderInfo} will be returned when parsing a buffer
+ * containing pre-defined test data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithTestData() throws Exception {
+ ByteBuffer buffer = ByteBuffer.wrap(
+ OsuProviderInfoTestUtil.TEST_OSU_PROVIDER_INFO_RAW_BYTES);
+ assertEquals(OsuProviderInfoTestUtil.TEST_OSU_PROVIDER_INFO,
+ OsuProviderInfo.parse(buffer));
+ }
+
+ /**
+ * Verify that when a provider contained multiple friendly names in different languages, the
+ * friendly name that's in default language is returned.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getFriendlyNameMatchingDefaultLocale() throws Exception {
+ List<I18Name> friendlyNames = new ArrayList<>();
+ Locale defaultLocale = Locale.getDefault();
+ Locale nonDefaultLocale = Locale.FRENCH;
+ if (defaultLocale.equals(nonDefaultLocale)) {
+ nonDefaultLocale = Locale.ENGLISH;
+ }
+ String nonDefaultString = "Non-default";
+ String defaultString = "Default";
+ friendlyNames.add(
+ new I18Name(nonDefaultLocale.getLanguage(), nonDefaultLocale, nonDefaultString));
+ friendlyNames.add(new I18Name(defaultLocale.getLanguage(), defaultLocale, defaultString));
+ OsuProviderInfo providerInfo =
+ new OsuProviderInfo(friendlyNames, null, null, null, null, null);
+ assertEquals(defaultString, providerInfo.getFriendlyName());
+ }
+
+ /**
+ * Verify that when a provider contained multiple friendly names where no friendly name
+ * is in default language, the first name in the list is returned.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getFriendlyNameNotMatchingDefaultLocale() throws Exception {
+ List<I18Name> friendlyNames = new ArrayList<>();
+ Locale nonDefaultLocale = Locale.FRENCH;
+ if (nonDefaultLocale.equals(Locale.getDefault())) {
+ nonDefaultLocale = Locale.ENGLISH;
+ }
+ String firstString = "First name";
+ String secondString = "Second name";
+ friendlyNames.add(
+ new I18Name(nonDefaultLocale.getLanguage(), nonDefaultLocale, firstString));
+ friendlyNames.add(
+ new I18Name(nonDefaultLocale.getLanguage(), nonDefaultLocale, secondString));
+ OsuProviderInfo providerInfo =
+ new OsuProviderInfo(friendlyNames, null, null, null, null, null);
+ assertEquals(firstString, providerInfo.getFriendlyName());
+ }
+
+ /**
+ * Verify that null will be returned for a provider containing empty friendly name list.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getFriendlyNameWithEmptyList() throws Exception {
+ OsuProviderInfo providerInfo =
+ new OsuProviderInfo(new ArrayList<I18Name>(), null, null, null, null, null);
+ assertEquals(null, providerInfo.getFriendlyName());
+ }
+
+ /**
+ * Verify that when a provider contained multiple service descriptions in different languages,
+ * the service description that's in default language is returned.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getServiceDescriptionMatchingDefaultLocale() throws Exception {
+ List<I18Name> serviceDescriptions = new ArrayList<>();
+ Locale defaultLocale = Locale.getDefault();
+ Locale nonDefaultLocale = Locale.FRENCH;
+ if (defaultLocale.equals(nonDefaultLocale)) {
+ nonDefaultLocale = Locale.ENGLISH;
+ }
+ String nonDefaultString = "Non-default";
+ String defaultString = "Default";
+ serviceDescriptions.add(
+ new I18Name(nonDefaultLocale.getLanguage(), nonDefaultLocale, nonDefaultString));
+ serviceDescriptions.add(
+ new I18Name(defaultLocale.getLanguage(), defaultLocale, defaultString));
+ OsuProviderInfo providerInfo =
+ new OsuProviderInfo(null, null, null, null, null, serviceDescriptions);
+ assertEquals(defaultString, providerInfo.getServiceDescription());
+ }
+
+ /**
+ * Verify that when a provider contained multiple service descriptions where none of them
+ * is in default language, the first element in the list is returned.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getServiceDescriptionNotMatchingDefaultLocale() throws Exception {
+ List<I18Name> serviceDescriptions = new ArrayList<>();
+ Locale nonDefaultLocale = Locale.FRENCH;
+ if (nonDefaultLocale.equals(Locale.getDefault())) {
+ nonDefaultLocale = Locale.ENGLISH;
+ }
+ String firstString = "First name";
+ String secondString = "Second name";
+ serviceDescriptions.add(
+ new I18Name(nonDefaultLocale.getLanguage(), nonDefaultLocale, firstString));
+ serviceDescriptions.add(
+ new I18Name(nonDefaultLocale.getLanguage(), nonDefaultLocale, secondString));
+ OsuProviderInfo providerInfo =
+ new OsuProviderInfo(null, null, null, null, null, serviceDescriptions);
+ assertEquals(firstString, providerInfo.getServiceDescription());
+ }
+
+ /**
+ * Verify that null will be returned for a provider containing empty friendly name list.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getServiceDescriptionWithEmptyList() throws Exception {
+ OsuProviderInfo providerInfo =
+ new OsuProviderInfo(null, null, null, null, null, new ArrayList<I18Name>());
+ assertEquals(null, providerInfo.getServiceDescription());
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/OsuProviderInfoTestUtil.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/OsuProviderInfoTestUtil.java
new file mode 100644
index 0000000..2b4c631
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/OsuProviderInfoTestUtil.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2017 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.hotspot2.anqp;
+
+import android.net.Uri;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Utility class containing test data and object for {@link OsuProviderInfo}.
+ */
+public class OsuProviderInfoTestUtil {
+ // Test data
+ private static final List<I18Name> TEST_FRIENDLY_NAMES =
+ Arrays.asList(new I18Name("en", Locale.forLanguageTag("en"), "FriendlyName"));
+ private static final String TEST_SERVER_URI = "test.server.com";
+ private static final List<Integer> TEST_METHOD_LIST = Arrays.asList(0, 1);
+ private static final List<IconInfo> TEST_ICON_INFO_LIST =
+ Arrays.asList(IconInfoTestUtil.TEST_ICON_INFO);
+ private static final String TEST_NAI = "network_access@test.com";
+ private static final List<I18Name> TEST_SERVICE_DESCRIPTIONS =
+ Arrays.asList(new I18Name("en", Locale.forLanguageTag("en"), "Test Service"));
+
+ /**
+ * {@link IconInfo} object with pre-defined test data.
+ */
+ public static final OsuProviderInfo TEST_OSU_PROVIDER_INFO =
+ new OsuProviderInfo(TEST_FRIENDLY_NAMES, Uri.parse(TEST_SERVER_URI), TEST_METHOD_LIST,
+ TEST_ICON_INFO_LIST, TEST_NAI, TEST_SERVICE_DESCRIPTIONS);
+
+ /**
+ * Raw bytes of icon info with pre-defined test data.
+ */
+ public static final byte[] TEST_OSU_PROVIDER_INFO_RAW_BYTES = getTestData();
+
+ public static final byte[] TEST_OSU_PROVIDER_INFO_RAW_BYTES_WITH_INVALID_LENGTH =
+ getTestDataWithInvalidLength();
+
+ /**
+ * Generate and return the raw data based on pre-defined test data.
+ *
+ * @return byte[]
+ */
+ private static byte[] getTestData() {
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] payload = getTestPayload();
+ writeShortLE(out, payload.length);
+ out.write(payload);
+ return out.toByteArray();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Generate and return the raw data based on pre-defined test data.
+ *
+ * @return byte[]
+ */
+ private static byte[] getTestDataWithInvalidLength() {
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] payload = getTestPayload();
+ // Set length to less than the minimum required.
+ writeShortLE(out, OsuProviderInfo.MINIMUM_LENGTH - 1);
+ out.write(payload);
+ return out.toByteArray();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Generate and return the payload containing OSU provider test data, excluding the length
+ * field.
+ *
+ * @return byte[]
+ * @throws IOException
+ */
+ private static byte[] getTestPayload() throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ // Write friendly name list.
+ byte[] friendlyNamesData = getI18NameListData(TEST_FRIENDLY_NAMES);
+ writeShortLE(out, friendlyNamesData.length);
+ out.write(friendlyNamesData);
+
+ // Write server URI.
+ writeByteArrayWithLength(out, TEST_SERVER_URI.getBytes(StandardCharsets.UTF_8));
+
+ // Write method list.
+ out.write((byte) TEST_METHOD_LIST.size());
+ for (Integer method : TEST_METHOD_LIST) {
+ out.write((byte) method.intValue());
+ }
+
+ // Write icon info list.
+ writeShortLE(out, IconInfoTestUtil.TEST_ICON_INFO_RAW_BYTES.length);
+ out.write(IconInfoTestUtil.TEST_ICON_INFO_RAW_BYTES);
+
+ // Write NAI.
+ writeByteArrayWithLength(out, TEST_NAI.getBytes(StandardCharsets.UTF_8));
+
+ // Write service descriptions.
+ byte[] serviceDescriptionsData = getI18NameListData(TEST_SERVICE_DESCRIPTIONS);
+ writeShortLE(out, serviceDescriptionsData.length);
+ out.write(serviceDescriptionsData);
+
+ return out.toByteArray();
+ }
+
+ private static byte[] getI18NameListData(List<I18Name> nameList) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ for (I18Name name : nameList) {
+ byte[] data = getI18NameData(name);
+ out.write((byte) data.length);
+ out.write(data);
+ }
+ return out.toByteArray();
+ }
+
+ /**
+ * Format the raw bytes for the given {@link I18Name}.
+ *
+ * @param value The {@link I18Name} to serialize
+ * @return byte[]
+ * @throws IOException
+ */
+ private static byte[] getI18NameData(I18Name value) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(value.getLanguage().getBytes(StandardCharsets.US_ASCII));
+ out.write((byte) 0); // Padding for language code.
+ out.write(value.getText().getBytes(StandardCharsets.UTF_8));
+ return out.toByteArray();
+ }
+
+ /**
+ * Write the lower 2-bytes of an integer to the given output stream in Little-Endian.
+ *
+ * @param out The output stream to write to
+ * @param value The integer value to write
+ */
+ private static void writeShortLE(ByteArrayOutputStream out, int value) {
+ out.write(value & 0xFF);
+ out.write((value >> 8) & 0xFF);
+ }
+
+ /**
+ * Write the given byte array to the given output stream, the array data is prefixed with a
+ * byte specifying the length of the byte array.
+ *
+ * @param out The output stream to write to
+ * @param data The byte array to write
+ * @throws IOException
+ */
+ private static void writeByteArrayWithLength(ByteArrayOutputStream out, byte[] data)
+ throws IOException {
+ out.write((byte) data.length);
+ out.write(data);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalTest.java b/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalTest.java
index bcce0ac..24f4854 100644
--- a/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalTest.java
@@ -1030,7 +1030,7 @@
// Default value when service is not initialized.
assertNull(mDut.getSsid(mPeerMacAddress));
executeAndValidateInitializationSequence(false, false, false);
- assertEquals(mSsid, mDut.getSsid(mPeerMacAddress));
+ assertEquals(NativeUtil.removeEnclosingQuotes(mSsid), mDut.getSsid(mPeerMacAddress));
}
/**
diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java
index 85b19fe..01835cd 100644
--- a/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java
@@ -55,6 +55,7 @@
import com.android.server.wifi.WifiInjector;
import com.android.server.wifi.WifiMetrics;
import com.android.server.wifi.WifiNative;
+import com.android.server.wifi.aware.WifiAwareMetrics;
import com.android.server.wifi.nano.WifiMetricsProto;
import com.android.server.wifi.util.WifiAsyncChannel;
@@ -114,7 +115,7 @@
new int[]{5600, 5650, 5660});
mLooper = new TestLooper();
- mWifiMetrics = new WifiMetrics(mClock, mLooper.getLooper());
+ mWifiMetrics = new WifiMetrics(mClock, mLooper.getLooper(), new WifiAwareMetrics(mClock));
when(mWifiScannerImplFactory
.create(any(), any(), any()))
.thenReturn(mWifiScannerImpl);
diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/WificondScannerTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/WificondScannerTest.java
index 3a5f6b9..ed7c582 100644
--- a/tests/wifitests/src/com/android/server/wifi/scanner/WificondScannerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/scanner/WificondScannerTest.java
@@ -37,8 +37,12 @@
import org.junit.Test;
import org.mockito.InOrder;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Set;
+import java.util.regex.Pattern;
/**
* Unit tests for {@link com.android.server.wifi.scanner.WificondScannerImpl}.
@@ -554,6 +558,28 @@
}
/**
+ * Test that dump() of WificondScannerImpl dumps native scan results.
+ */
+ @Test
+ public void dumpContainsNativeScanResults() {
+ assertDumpContainsRequestLog("Latest native scan results:");
+ }
+
+ private void assertDumpContainsRequestLog(String log) {
+ String objectDump = dumpObject();
+ Pattern logLineRegex = Pattern.compile(".*" + log + ".*");
+ assertTrue("dump did not contain log = " + log + "\n " + objectDump + "\n",
+ logLineRegex.matcher(objectDump).find());
+ }
+
+ private String dumpObject() {
+ StringWriter stringWriter = new StringWriter();
+ mScanner.dump(new FileDescriptor(), new PrintWriter(stringWriter),
+ new String[0]);
+ return stringWriter.toString();
+ }
+
+ /**
* Run a test with the given settings where all native scans succeed
* This will execute expectedPeriods.length scan periods by first
* starting the scan settings and then dispatching the scan period alarm to start the
diff --git a/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java
index 7b03698..92cae3b 100644
--- a/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java
@@ -85,32 +85,6 @@
}
@Test
- public void testScanResultMatchingWithNetwork() {
- final String ssid = "Another SSid";
- WifiConfiguration config = new WifiConfiguration();
- config.SSID = ScanResultUtil.createQuotedSSID(ssid);
- ScanResult scanResult = new ScanResult(ssid, "ab:cd:01:ef:45:89", 1245, 0, "",
- -78, 2450, 1025, 22, 33, 20, 0, 0, true);
-
- config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
- scanResult.capabilities = "";
- assertTrue(ScanResultUtil.doesScanResultMatchWithNetwork(scanResult, config));
-
- config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
- config.wepKeys[0] = "45592364648547";
- scanResult.capabilities = "WEP";
- assertTrue(ScanResultUtil.doesScanResultMatchWithNetwork(scanResult, config));
-
- config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
- scanResult.capabilities = "PSK";
- assertTrue(ScanResultUtil.doesScanResultMatchWithNetwork(scanResult, config));
-
- config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
- scanResult.capabilities = "EAP";
- assertTrue(ScanResultUtil.doesScanResultMatchWithNetwork(scanResult, config));
- }
-
- @Test
public void testNetworkCreationFromScanResult() {
final String ssid = "Another SSid";
ScanResult scanResult = new ScanResult(ssid, "ab:cd:01:ef:45:89", 1245, 0, "",