WifiScanningServiceImpl: Add ClientInfo null checks am: 5382acb5eb
am: 0df9502510
Change-Id: Ibd4c01c4a32935d2a1d72ef179dbca9d8b3ff2c7
diff --git a/libwifi_hal/Android.mk b/libwifi_hal/Android.mk
new file mode 100644
index 0000000..f39617c
--- /dev/null
+++ b/libwifi_hal/Android.mk
@@ -0,0 +1,128 @@
+# 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)
+ifneq ($(TARGET_BUILD_PDK), true)
+
+wifi_hal_cflags := \
+ -Wall \
+ -Werror \
+ -Wextra \
+ -Winit-self \
+ -Wno-unused-function \
+ -Wno-unused-parameter \
+ -Wshadow \
+ -Wunused-variable \
+ -Wwrite-strings
+ifdef WIFI_DRIVER_MODULE_PATH
+wifi_hal_cflags += -DWIFI_DRIVER_MODULE_PATH=\"$(WIFI_DRIVER_MODULE_PATH)\"
+endif
+ifdef WIFI_DRIVER_MODULE_ARG
+wifi_hal_cflags += -DWIFI_DRIVER_MODULE_ARG=\"$(WIFI_DRIVER_MODULE_ARG)\"
+endif
+ifdef WIFI_DRIVER_MODULE_NAME
+wifi_hal_cflags += -DWIFI_DRIVER_MODULE_NAME=\"$(WIFI_DRIVER_MODULE_NAME)\"
+endif
+ifdef WIFI_DRIVER_FW_PATH_STA
+wifi_hal_cflags += -DWIFI_DRIVER_FW_PATH_STA=\"$(WIFI_DRIVER_FW_PATH_STA)\"
+endif
+ifdef WIFI_DRIVER_FW_PATH_AP
+wifi_hal_cflags += -DWIFI_DRIVER_FW_PATH_AP=\"$(WIFI_DRIVER_FW_PATH_AP)\"
+endif
+ifdef WIFI_DRIVER_FW_PATH_P2P
+wifi_hal_cflags += -DWIFI_DRIVER_FW_PATH_P2P=\"$(WIFI_DRIVER_FW_PATH_P2P)\"
+endif
+ifdef WIFI_DRIVER_FW_PATH_PARAM
+wifi_hal_cflags += -DWIFI_DRIVER_FW_PATH_PARAM=\"$(WIFI_DRIVER_FW_PATH_PARAM)\"
+endif
+
+ifdef WIFI_DRIVER_STATE_CTRL_PARAM
+wifi_hal_cflags += -DWIFI_DRIVER_STATE_CTRL_PARAM=\"$(WIFI_DRIVER_STATE_CTRL_PARAM)\"
+endif
+ifdef WIFI_DRIVER_STATE_ON
+wifi_hal_cflags += -DWIFI_DRIVER_STATE_ON=\"$(WIFI_DRIVER_STATE_ON)\"
+endif
+ifdef WIFI_DRIVER_STATE_OFF
+wifi_hal_cflags += -DWIFI_DRIVER_STATE_OFF=\"$(WIFI_DRIVER_STATE_OFF)\"
+endif
+
+# Common code shared between the HALs.
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libwifi-hal-common
+LOCAL_CFLAGS := $(wifi_hal_cflags)
+LOCAL_SRC_FILES := wifi_hal_common.cpp
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+include $(BUILD_STATIC_LIBRARY)
+
+# A fallback "vendor" HAL library.
+# Don't link this, link libwifi-hal.
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libwifi-hal-fallback
+LOCAL_CFLAGS := $(wifi_hal_cflags)
+LOCAL_SRC_FILES := wifi_hal_fallback.cpp
+LOCAL_C_INCLUDES := $(call include-path-for, libhardware_legacy)
+include $(BUILD_STATIC_LIBRARY)
+
+# Pick a vendor provided HAL implementation library.
+# ============================================================
+LIB_WIFI_HAL := libwifi-hal-fallback
+ifeq ($(BOARD_WLAN_DEVICE), bcmdhd)
+ LIB_WIFI_HAL := libwifi-hal-bcm
+else ifeq ($(BOARD_WLAN_DEVICE), qcwcn)
+ LIB_WIFI_HAL := libwifi-hal-qcom
+else ifeq ($(BOARD_WLAN_DEVICE), mrvl)
+ # this is commented because none of the nexus devices
+ # that sport Marvell's wifi have support for HAL
+ # LIB_WIFI_HAL := libwifi-hal-mrvl
+else ifeq ($(BOARD_WLAN_DEVICE), MediaTek)
+ # support MTK WIFI HAL
+ LIB_WIFI_HAL := libwifi-hal-mt66xx
+endif
+
+# The WiFi HAL that you should be linking.
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libwifi-hal
+LOCAL_CFLAGS := $(wifi_hal_cflags)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+ $(LOCAL_PATH)/include \
+ $(call include-path-for, libhardware_legacy)
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ liblog \
+ libnl \
+ libutils
+LOCAL_SRC_FILES := \
+ driver_tool.cpp
+LOCAL_WHOLE_STATIC_LIBRARIES := $(LIB_WIFI_HAL) libwifi-hal-common
+include $(BUILD_SHARED_LIBRARY)
+
+# Test utilities (e.g. mock classes) for libwifi-hal
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libwifi-hal-test
+LOCAL_CFLAGS := $(wifi_hal_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)
+
+endif
diff --git a/libwifi_hal/driver_tool.cpp b/libwifi_hal/driver_tool.cpp
new file mode 100644
index 0000000..688017b
--- /dev/null
+++ b/libwifi_hal/driver_tool.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#include "wifi_hal/driver_tool.h"
+
+#include "hardware_legacy/wifi.h"
+
+namespace android {
+namespace wifi_hal {
+
+const int DriverTool::kFirmwareModeSta = WIFI_GET_FW_PATH_STA;
+const int DriverTool::kFirmwareModeAp = WIFI_GET_FW_PATH_AP;
+const int DriverTool::kFirmwareModeP2p = WIFI_GET_FW_PATH_P2P;
+
+bool DriverTool::LoadDriver() {
+ return ::wifi_load_driver() == 0;
+}
+
+bool DriverTool::UnloadDriver() {
+ return ::wifi_unload_driver() == 0;
+}
+
+bool DriverTool::IsDriverLoaded() {
+ return ::wifi_unload_driver() != 0;
+}
+
+bool DriverTool::ChangeFirmwareMode(int mode) {
+ const char* fwpath = wifi_get_fw_path(mode);
+ if (!fwpath) {
+ return true; // HAL doesn't think we need to load firmware for this mode.
+ }
+ if (wifi_change_fw_path(fwpath) != 0) {
+ // Not all devices actually require firmware reloads, but
+ // failure to change the firmware path when it is defined is an error.
+ return false;
+ }
+ return true;
+}
+
+} // namespace wifi_hal
+} // namespace android
diff --git a/libwifi_hal/include/hardware_legacy/wifi.h b/libwifi_hal/include/hardware_legacy/wifi.h
new file mode 100644
index 0000000..defff0a
--- /dev/null
+++ b/libwifi_hal/include/hardware_legacy/wifi.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#ifndef HARDWARE_LEGACY_WIFI_H
+#define HARDWARE_LEGACY_WIFI_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/**
+ * Load the Wi-Fi driver.
+ *
+ * @return 0 on success, < 0 on failure.
+ */
+int wifi_load_driver();
+
+/**
+ * Unload the Wi-Fi driver.
+ *
+ * @return 0 on success, < 0 on failure.
+ */
+int wifi_unload_driver();
+
+/**
+ * Check if the Wi-Fi driver is loaded.
+ * Check if the Wi-Fi driver is loaded.
+
+ * @return 0 on success, < 0 on failure.
+ */
+int is_wifi_driver_loaded();
+
+/**
+ * Return the path to requested firmware
+ */
+#define WIFI_GET_FW_PATH_STA 0
+#define WIFI_GET_FW_PATH_AP 1
+#define WIFI_GET_FW_PATH_P2P 2
+const char *wifi_get_fw_path(int fw_type);
+
+/**
+ * Change the path to firmware for the wlan driver
+ */
+int wifi_change_fw_path(const char *fwpath);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* HARDWARE_LEGACY_WIFI_H */
diff --git a/libwifi_hal/include/wifi_hal/driver_tool.h b/libwifi_hal/include/wifi_hal/driver_tool.h
new file mode 100644
index 0000000..f1a43cc
--- /dev/null
+++ b/libwifi_hal/include/wifi_hal/driver_tool.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_WIFI_SYSTEM_DRIVER_TOOL_H
+#define ANDROID_WIFI_SYSTEM_DRIVER_TOOL_H
+
+namespace android {
+namespace wifi_hal {
+
+// Utilities for interacting with the driver.
+class DriverTool {
+ public:
+ static const int kFirmwareModeSta;
+ static const int kFirmwareModeAp;
+ static const int kFirmwareModeP2p;
+
+ DriverTool() = default;
+ virtual ~DriverTool() = default;
+
+ // These methods allow manipulation of the WiFi driver.
+ // They all return true on success, and false otherwise.
+ virtual bool LoadDriver();
+ virtual bool UnloadDriver();
+ virtual bool IsDriverLoaded();
+
+ // Change the firmware mode.
+ // |mode| is one of the kFirmwareMode* constants defined above.
+ // Returns true on success, and false otherwise.
+ virtual bool ChangeFirmwareMode(int mode);
+
+}; // class DriverTool
+
+} // namespace wifi_hal
+} // namespace android
+
+#endif // ANDROID_WIFI_SYSTEM_DRIVER_TOOL_H
+
diff --git a/libwifi_hal/testlib/include/wifi_hal_test/mock_driver_tool.h b/libwifi_hal/testlib/include/wifi_hal_test/mock_driver_tool.h
new file mode 100644
index 0000000..fef0dab
--- /dev/null
+++ b/libwifi_hal/testlib/include/wifi_hal_test/mock_driver_tool.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_WIFI_HAL_MOCK_DRIVER_TOOL_H
+#define ANDROID_WIFI_HAL_MOCK_DRIVER_TOOL_H
+
+#include <wifi_hal/driver_tool.h>
+
+namespace android {
+namespace wifi_hal {
+
+class MockDriverTool : public DriverTool {
+ public:
+ ~MockDriverTool() override = default;
+ MOCK_METHOD0(LoadDriver, bool());
+ MOCK_METHOD0(UnloadDriver, bool());
+ MOCK_METHOD0(IsDriverLoaded, bool());
+ MOCK_METHOD1(ChangeFirmwareMode, bool(int mode));
+
+}; // class MockDriverTool
+
+} // namespace wifi_hal
+} // namespace android
+
+#endif // ANDROID_WIFI_HAL_MOCK_DRIVER_TOOL_H
+
diff --git a/libwifi_hal/wifi_hal_common.cpp b/libwifi_hal/wifi_hal_common.cpp
new file mode 100644
index 0000000..54c38e1
--- /dev/null
+++ b/libwifi_hal/wifi_hal_common.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright 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.
+ */
+
+#include "hardware_legacy/wifi.h"
+
+#define LOG_TAG "WifiHalCommon"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <cutils/log.h>
+#include <cutils/misc.h>
+#include <cutils/properties.h>
+
+extern int init_module(void *, unsigned long, const char *);
+extern int delete_module(const char *, unsigned int);
+
+#ifndef WIFI_DRIVER_FW_PATH_STA
+#define WIFI_DRIVER_FW_PATH_STA NULL
+#endif
+#ifndef WIFI_DRIVER_FW_PATH_AP
+#define WIFI_DRIVER_FW_PATH_AP NULL
+#endif
+#ifndef WIFI_DRIVER_FW_PATH_P2P
+#define WIFI_DRIVER_FW_PATH_P2P NULL
+#endif
+#ifndef WIFI_DRIVER_FW_PATH_PARAM
+#define WIFI_DRIVER_FW_PATH_PARAM "/sys/module/wlan/parameters/fwpath"
+#endif
+
+#ifndef WIFI_DRIVER_MODULE_ARG
+#define WIFI_DRIVER_MODULE_ARG ""
+#endif
+
+static const char DRIVER_PROP_NAME[] = "wlan.driver.status";
+#ifdef WIFI_DRIVER_MODULE_PATH
+static const char DRIVER_MODULE_NAME[] = WIFI_DRIVER_MODULE_NAME;
+static const char DRIVER_MODULE_TAG[] = WIFI_DRIVER_MODULE_NAME " ";
+static const char DRIVER_MODULE_PATH[] = WIFI_DRIVER_MODULE_PATH;
+static const char DRIVER_MODULE_ARG[] = WIFI_DRIVER_MODULE_ARG;
+static const char MODULE_FILE[] = "/proc/modules";
+#endif
+
+static int insmod(const char *filename, const char *args)
+{
+ void *module;
+ unsigned int size;
+ int ret;
+
+ module = load_file(filename, &size);
+ if (!module)
+ return -1;
+
+ ret = init_module(module, size, args);
+
+ free(module);
+
+ return ret;
+}
+
+static int rmmod(const char *modname)
+{
+ int ret = -1;
+ int maxtry = 10;
+
+ while (maxtry-- > 0) {
+ ret = delete_module(modname, O_NONBLOCK | O_EXCL);
+ if (ret < 0 && errno == EAGAIN)
+ usleep(500000);
+ else
+ break;
+ }
+
+ if (ret != 0)
+ ALOGD("Unable to unload driver module \"%s\": %s\n",
+ modname, strerror(errno));
+ return ret;
+}
+
+#ifdef WIFI_DRIVER_STATE_CTRL_PARAM
+int wifi_change_driver_state(const char *state)
+{
+ int len;
+ int fd;
+ int ret = 0;
+
+ if (!state)
+ return -1;
+ fd = TEMP_FAILURE_RETRY(open(WIFI_DRIVER_STATE_CTRL_PARAM, O_WRONLY));
+ if (fd < 0) {
+ ALOGE("Failed to open driver state control param (%s)", strerror(errno));
+ return -1;
+ }
+ len = strlen(state) + 1;
+ if (TEMP_FAILURE_RETRY(write(fd, state, len)) != len) {
+ ALOGE("Failed to write driver state control param (%s)", strerror(errno));
+ ret = -1;
+ }
+ close(fd);
+ return ret;
+}
+#endif
+
+int is_wifi_driver_loaded() {
+ char driver_status[PROPERTY_VALUE_MAX];
+#ifdef WIFI_DRIVER_MODULE_PATH
+ FILE *proc;
+ char line[sizeof(DRIVER_MODULE_TAG)+10];
+#endif
+
+ if (!property_get(DRIVER_PROP_NAME, driver_status, NULL)
+ || strcmp(driver_status, "ok") != 0) {
+ return 0; /* driver not loaded */
+ }
+#ifdef WIFI_DRIVER_MODULE_PATH
+ /*
+ * If the property says the driver is loaded, check to
+ * make sure that the property setting isn't just left
+ * over from a previous manual shutdown or a runtime
+ * crash.
+ */
+ if ((proc = fopen(MODULE_FILE, "r")) == NULL) {
+ ALOGW("Could not open %s: %s", MODULE_FILE, strerror(errno));
+ property_set(DRIVER_PROP_NAME, "unloaded");
+ return 0;
+ }
+ while ((fgets(line, sizeof(line), proc)) != NULL) {
+ if (strncmp(line, DRIVER_MODULE_TAG, strlen(DRIVER_MODULE_TAG)) == 0) {
+ fclose(proc);
+ return 1;
+ }
+ }
+ fclose(proc);
+ property_set(DRIVER_PROP_NAME, "unloaded");
+ return 0;
+#else
+ return 1;
+#endif
+}
+
+int wifi_load_driver()
+{
+#ifdef WIFI_DRIVER_MODULE_PATH
+ if (is_wifi_driver_loaded()) {
+ return 0;
+ }
+
+ if (insmod(DRIVER_MODULE_PATH, DRIVER_MODULE_ARG) < 0)
+ return -1;
+
+#elif defined WIFI_DRIVER_STATE_CTRL_PARAM
+ if (is_wifi_driver_loaded()) {
+ return 0;
+ }
+
+ if (wifi_change_driver_state(WIFI_DRIVER_STATE_ON) < 0)
+ return -1;
+#endif
+ property_set(DRIVER_PROP_NAME, "ok");
+ return 0;
+}
+
+int wifi_unload_driver()
+{
+ usleep(200000); /* allow to finish interface down */
+#ifdef WIFI_DRIVER_MODULE_PATH
+ if (rmmod(DRIVER_MODULE_NAME) == 0) {
+ int count = 20; /* wait at most 10 seconds for completion */
+ while (count-- > 0) {
+ if (!is_wifi_driver_loaded())
+ break;
+ usleep(500000);
+ }
+ usleep(500000); /* allow card removal */
+ if (count) {
+ return 0;
+ }
+ return -1;
+ } else
+ return -1;
+#else
+#ifdef WIFI_DRIVER_STATE_CTRL_PARAM
+ if (is_wifi_driver_loaded()) {
+ if (wifi_change_driver_state(WIFI_DRIVER_STATE_OFF) < 0)
+ return -1;
+ }
+#endif
+ property_set(DRIVER_PROP_NAME, "unloaded");
+ return 0;
+#endif
+}
+
+const char *wifi_get_fw_path(int fw_type)
+{
+ switch (fw_type) {
+ case WIFI_GET_FW_PATH_STA:
+ return WIFI_DRIVER_FW_PATH_STA;
+ case WIFI_GET_FW_PATH_AP:
+ return WIFI_DRIVER_FW_PATH_AP;
+ case WIFI_GET_FW_PATH_P2P:
+ return WIFI_DRIVER_FW_PATH_P2P;
+ }
+ return NULL;
+}
+
+int wifi_change_fw_path(const char *fwpath)
+{
+ int len;
+ int fd;
+ int ret = 0;
+
+ if (!fwpath)
+ return ret;
+ fd = TEMP_FAILURE_RETRY(open(WIFI_DRIVER_FW_PATH_PARAM, O_WRONLY));
+ if (fd < 0) {
+ ALOGE("Failed to open wlan fw path param (%s)", strerror(errno));
+ return -1;
+ }
+ len = strlen(fwpath) + 1;
+ if (TEMP_FAILURE_RETRY(write(fd, fwpath, len)) != len) {
+ ALOGE("Failed to write wlan fw path param (%s)", strerror(errno));
+ ret = -1;
+ }
+ close(fd);
+ return ret;
+}
diff --git a/libwifi_hal/wifi_hal_fallback.cpp b/libwifi_hal/wifi_hal_fallback.cpp
new file mode 100644
index 0000000..64259a1
--- /dev/null
+++ b/libwifi_hal/wifi_hal_fallback.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright 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.
+ */
+
+#include "hardware_legacy/wifi_hal.h"
+
+wifi_error init_wifi_vendor_hal_func_table(wifi_hal_fn *fn) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
diff --git a/libwifi_system/Android.mk b/libwifi_system/Android.mk
new file mode 100644
index 0000000..61d340a
--- /dev/null
+++ b/libwifi_system/Android.mk
@@ -0,0 +1,91 @@
+# 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)
+
+ifneq ($(TARGET_BUILD_PDK), true)
+
+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 \
+ liblog \
+ libnetutils \
+ libnl \
+ libwifi-hal
+
+# Tolerate certain emulators which apparently don't have supplicant installed.
+ifdef WPA_SUPPLICANT_VERSION
+LOCAL_CFLAGS += -DLIBWPA_CLIENT_EXISTS
+LOCAL_SHARED_LIBRARIES += libwpa_client
+endif
+
+LOCAL_SRC_FILES := \
+ hostapd_manager.cpp \
+ interface_tool.cpp \
+ hal_tool.cpp \
+ wifi.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)
+
+endif
diff --git a/libwifi_system/hal_tool.cpp b/libwifi_system/hal_tool.cpp
new file mode 100644
index 0000000..4c7401d
--- /dev/null
+++ b/libwifi_system/hal_tool.cpp
@@ -0,0 +1,585 @@
+/*
+ * 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.
+ */
+
+#include "wifi_system/hal_tool.h"
+
+#include <log/log.h>
+
+namespace android {
+namespace wifi_system {
+namespace {
+
+wifi_error wifi_initialize_stub(wifi_handle* handle) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+void wifi_cleanup_stub(wifi_handle handle, wifi_cleaned_up_handler handler) {}
+
+void wifi_event_loop_stub(wifi_handle handle) {}
+
+void wifi_get_error_info_stub(wifi_error err, const char** msg) { *msg = NULL; }
+
+wifi_error wifi_get_supported_feature_set_stub(wifi_interface_handle handle,
+ feature_set* set) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_get_concurrency_matrix_stub(wifi_interface_handle handle,
+ int max_size, feature_set* matrix,
+ int* size) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_set_scanning_mac_oui_stub(wifi_interface_handle handle,
+ unsigned char* oui_data) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+/* List of all supported channels, including 5GHz channels */
+wifi_error wifi_get_supported_channels_stub(wifi_handle handle, int* size,
+ wifi_channel* list) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+/* Enhanced power reporting */
+wifi_error wifi_is_epr_supported_stub(wifi_handle handle) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+/* multiple interface support */
+wifi_error wifi_get_ifaces_stub(wifi_handle handle, int* num_ifaces,
+ wifi_interface_handle** ifaces) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_get_iface_name_stub(wifi_interface_handle iface, char* name,
+ size_t size) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_set_iface_event_handler_stub(wifi_request_id id,
+ wifi_interface_handle iface,
+ wifi_event_handler eh) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_reset_iface_event_handler_stub(wifi_request_id id,
+ wifi_interface_handle iface) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_start_gscan_stub(wifi_request_id id,
+ wifi_interface_handle iface,
+ wifi_scan_cmd_params params,
+ wifi_scan_result_handler handler) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_stop_gscan_stub(wifi_request_id id,
+ wifi_interface_handle iface) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_get_cached_gscan_results_stub(wifi_interface_handle iface,
+ byte flush, int max,
+ wifi_cached_scan_results* results,
+ int* num) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_set_bssid_hotlist_stub(wifi_request_id id,
+ wifi_interface_handle iface,
+ wifi_bssid_hotlist_params params,
+ wifi_hotlist_ap_found_handler handler) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_reset_bssid_hotlist_stub(wifi_request_id id,
+ wifi_interface_handle iface) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_set_significant_change_handler_stub(
+ wifi_request_id id, wifi_interface_handle iface,
+ wifi_significant_change_params params,
+ wifi_significant_change_handler handler) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_reset_significant_change_handler_stub(
+ wifi_request_id id, wifi_interface_handle iface) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_get_gscan_capabilities_stub(
+ wifi_interface_handle handle, wifi_gscan_capabilities* capabilities) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_set_link_stats_stub(wifi_interface_handle iface,
+ wifi_link_layer_params params) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_get_link_stats_stub(wifi_request_id id,
+ wifi_interface_handle iface,
+ wifi_stats_result_handler handler) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_clear_link_stats_stub(wifi_interface_handle iface,
+ u32 stats_clear_req_mask,
+ u32* stats_clear_rsp_mask, u8 stop_req,
+ u8* stop_rsp) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+wifi_error wifi_get_valid_channels_stub(wifi_interface_handle handle, int band,
+ int max_channels,
+ wifi_channel* channels,
+ int* num_channels) {
+ return WIFI_ERROR_UNINITIALIZED;
+}
+
+/* API to request RTT measurement */
+wifi_error wifi_rtt_range_request_stub(wifi_request_id id,
+ wifi_interface_handle iface,
+ unsigned num_rtt_config,
+ wifi_rtt_config rtt_config[],
+ wifi_rtt_event_handler handler) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+/* API to cancel RTT measurements */
+wifi_error wifi_rtt_range_cancel_stub(wifi_request_id id,
+ wifi_interface_handle iface,
+ unsigned num_devices, mac_addr addr[]) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+/* API to get RTT capability */
+wifi_error wifi_get_rtt_capabilities_stub(wifi_interface_handle iface,
+ wifi_rtt_capabilities* capabilities) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+/* API to enable RTT responder role */
+wifi_error wifi_enable_responder_stub(wifi_request_id id,
+ wifi_interface_handle iface,
+ wifi_channel_info channel_hint,
+ unsigned max_duration_seconds,
+ wifi_channel_info* channel_used) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+/* API to disable RTT responder role */
+wifi_error wifi_disable_responder_stub(wifi_request_id id,
+ wifi_interface_handle iface) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+/* API to get available channel for RTT responder role */
+wifi_error wifi_rtt_get_available_channel_stub(wifi_interface_handle iface,
+ wifi_channel_info* channel) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_set_nodfs_flag_stub(wifi_interface_handle iface, u32 nodfs) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_start_logging_stub(wifi_interface_handle iface,
+ u32 verbose_level, u32 flags,
+ u32 max_interval_sec, u32 min_data_size,
+ char* buffer_name) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_set_epno_list_stub(int id, wifi_interface_info* iface,
+ const wifi_epno_params* params,
+ wifi_epno_handler handler) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_reset_epno_list_stub(int id, wifi_interface_info* iface) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_set_country_code_stub(wifi_interface_handle iface,
+ const char* code) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_firmware_memory_dump_stub(
+ wifi_interface_handle iface, wifi_firmware_memory_dump_handler handler) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_set_log_handler_stub(wifi_request_id id,
+ wifi_interface_handle iface,
+ wifi_ring_buffer_data_handler handler) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_reset_log_handler_stub(wifi_request_id id,
+ wifi_interface_handle iface) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_set_alert_handler_stub(wifi_request_id id,
+ wifi_interface_handle iface,
+ wifi_alert_handler handler) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_reset_alert_handler_stub(wifi_request_id id,
+ wifi_interface_handle iface) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_firmware_version_stub(wifi_interface_handle iface,
+ char* buffer, int buffer_size) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_ring_buffers_status_stub(wifi_interface_handle iface,
+ u32* num_rings,
+ wifi_ring_buffer_status* status) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_logger_supported_feature_set_stub(
+ wifi_interface_handle iface, unsigned int* support) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_ring_data_stub(wifi_interface_handle iface,
+ char* ring_name) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_driver_version_stub(wifi_interface_handle iface,
+ char* buffer, int buffer_size) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_enable_tdls_stub(wifi_interface_handle iface, mac_addr addr,
+ wifi_tdls_params* params,
+ wifi_tdls_handler handler) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_disable_tdls_stub(wifi_interface_handle iface, mac_addr addr) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_tdls_status_stub(wifi_interface_handle iface, mac_addr addr,
+ wifi_tdls_status* status) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_tdls_capabilities_stub(
+ wifi_interface_handle iface, wifi_tdls_capabilities* capabilities) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_set_bssid_blacklist_stub(wifi_request_id id,
+ wifi_interface_handle iface,
+ wifi_bssid_params params) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_start_sending_offloaded_packet_stub(
+ wifi_request_id id, wifi_interface_handle iface, u8* ip_packet,
+ u16 ip_packet_len, u8* src_mac_addr, u8* dst_mac_addr, u32 period_msec) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_stop_sending_offloaded_packet_stub(
+ wifi_request_id id, wifi_interface_handle iface) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_wake_reason_stats_stub(
+ wifi_interface_handle iface,
+ WLAN_DRIVER_WAKE_REASON_CNT* wifi_wake_reason_cnt) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_configure_nd_offload_stub(wifi_interface_handle iface,
+ u8 enable) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_driver_memory_dump_stub(
+ wifi_interface_handle iface, wifi_driver_memory_dump_callbacks callbacks) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_start_pkt_fate_monitoring_stub(wifi_interface_handle iface) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_tx_pkt_fates_stub(wifi_interface_handle handle,
+ wifi_tx_report* tx_report_bufs,
+ size_t n_requested_fates,
+ size_t* n_provided_fates) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_rx_pkt_fates_stub(wifi_interface_handle handle,
+ wifi_rx_report* rx_report_bufs,
+ size_t n_requested_fates,
+ size_t* n_provided_fates) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+wifi_error wifi_nan_enable_request_stub(transaction_id id,
+ wifi_interface_handle iface,
+ NanEnableRequest* msg) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_nan_disable_request_stub(transaction_id id,
+ wifi_interface_handle iface) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_nan_publish_request_stub(transaction_id id,
+ wifi_interface_handle iface,
+ NanPublishRequest* msg) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_nan_publish_cancel_request_stub(transaction_id id,
+ wifi_interface_handle iface,
+ NanPublishCancelRequest* msg) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_nan_subscribe_request_stub(transaction_id id,
+ wifi_interface_handle iface,
+ NanSubscribeRequest* msg) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_nan_subscribe_cancel_request_stub(
+ transaction_id id, wifi_interface_handle iface,
+ NanSubscribeCancelRequest* msg) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_nan_transmit_followup_request_stub(
+ transaction_id id, wifi_interface_handle iface,
+ NanTransmitFollowupRequest* msg) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_nan_stats_request_stub(transaction_id id,
+ wifi_interface_handle iface,
+ NanStatsRequest* msg) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_nan_config_request_stub(transaction_id id,
+ wifi_interface_handle iface,
+ NanConfigRequest* msg) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_nan_tca_request_stub(transaction_id id,
+ wifi_interface_handle iface,
+ NanTCARequest* msg) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_nan_beacon_sdf_payload_request_stub(
+ transaction_id id, wifi_interface_handle iface,
+ NanBeaconSdfPayloadRequest* msg) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_nan_register_handler_stub(wifi_interface_handle iface,
+ NanCallbackHandler handlers) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_nan_get_version_stub(wifi_handle handle, NanVersion* version) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_nan_get_capabilities_stub(transaction_id id,
+ wifi_interface_handle iface) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_nan_data_interface_create_stub(transaction_id id,
+ wifi_interface_handle iface,
+ char* iface_name) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_nan_data_interface_delete_stub(transaction_id id,
+ wifi_interface_handle iface,
+ char* iface_name) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_nan_data_request_initiator_stub(
+ transaction_id id, wifi_interface_handle iface,
+ NanDataPathInitiatorRequest* msg) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_nan_data_indication_response_stub(
+ transaction_id id, wifi_interface_handle iface,
+ NanDataPathIndicationResponse* msg) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_nan_data_end_stub(transaction_id id,
+ wifi_interface_handle iface,
+ NanDataPathEndRequest* msg) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_get_packet_filter_capabilities_stub(
+ wifi_interface_handle handle, u32* version, u32* max_len) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+wifi_error wifi_set_packet_filter_stub(wifi_interface_handle handle,
+ const u8* program, u32 len) {
+ return WIFI_ERROR_NOT_SUPPORTED;
+}
+
+bool init_wifi_stub_hal_func_table(wifi_hal_fn* hal_fn) {
+ if (hal_fn == NULL) {
+ return false;
+ }
+ hal_fn->wifi_initialize = wifi_initialize_stub;
+ hal_fn->wifi_cleanup = wifi_cleanup_stub;
+ hal_fn->wifi_event_loop = wifi_event_loop_stub;
+ hal_fn->wifi_get_error_info = wifi_get_error_info_stub;
+ hal_fn->wifi_get_supported_feature_set = wifi_get_supported_feature_set_stub;
+ hal_fn->wifi_get_concurrency_matrix = wifi_get_concurrency_matrix_stub;
+ hal_fn->wifi_set_scanning_mac_oui = wifi_set_scanning_mac_oui_stub;
+ hal_fn->wifi_get_supported_channels = wifi_get_supported_channels_stub;
+ hal_fn->wifi_is_epr_supported = wifi_is_epr_supported_stub;
+ hal_fn->wifi_get_ifaces = wifi_get_ifaces_stub;
+ hal_fn->wifi_get_iface_name = wifi_get_iface_name_stub;
+ hal_fn->wifi_reset_iface_event_handler = wifi_reset_iface_event_handler_stub;
+ hal_fn->wifi_start_gscan = wifi_start_gscan_stub;
+ hal_fn->wifi_stop_gscan = wifi_stop_gscan_stub;
+ hal_fn->wifi_get_cached_gscan_results = wifi_get_cached_gscan_results_stub;
+ hal_fn->wifi_set_bssid_hotlist = wifi_set_bssid_hotlist_stub;
+ hal_fn->wifi_reset_bssid_hotlist = wifi_reset_bssid_hotlist_stub;
+ hal_fn->wifi_set_significant_change_handler =
+ wifi_set_significant_change_handler_stub;
+ hal_fn->wifi_reset_significant_change_handler =
+ wifi_reset_significant_change_handler_stub;
+ hal_fn->wifi_get_gscan_capabilities = wifi_get_gscan_capabilities_stub;
+ hal_fn->wifi_set_link_stats = wifi_set_link_stats_stub;
+ hal_fn->wifi_get_link_stats = wifi_get_link_stats_stub;
+ hal_fn->wifi_clear_link_stats = wifi_clear_link_stats_stub;
+ hal_fn->wifi_get_valid_channels = wifi_get_valid_channels_stub;
+ hal_fn->wifi_rtt_range_request = wifi_rtt_range_request_stub;
+ hal_fn->wifi_rtt_range_cancel = wifi_rtt_range_cancel_stub;
+ hal_fn->wifi_get_rtt_capabilities = wifi_get_rtt_capabilities_stub;
+ hal_fn->wifi_start_logging = wifi_start_logging_stub;
+ hal_fn->wifi_set_epno_list = wifi_set_epno_list_stub;
+ hal_fn->wifi_set_country_code = wifi_set_country_code_stub;
+ hal_fn->wifi_enable_tdls = wifi_enable_tdls_stub;
+ hal_fn->wifi_disable_tdls = wifi_disable_tdls_stub;
+ hal_fn->wifi_get_tdls_status = wifi_get_tdls_status_stub;
+ hal_fn->wifi_get_tdls_capabilities = wifi_get_tdls_capabilities_stub;
+ hal_fn->wifi_set_nodfs_flag = wifi_set_nodfs_flag_stub;
+ hal_fn->wifi_get_firmware_memory_dump = wifi_get_firmware_memory_dump_stub;
+ hal_fn->wifi_set_log_handler = wifi_set_log_handler_stub;
+ hal_fn->wifi_reset_log_handler = wifi_reset_log_handler_stub;
+ hal_fn->wifi_set_alert_handler = wifi_set_alert_handler_stub;
+ hal_fn->wifi_reset_alert_handler = wifi_reset_alert_handler_stub;
+ hal_fn->wifi_get_firmware_version = wifi_get_firmware_version_stub;
+ hal_fn->wifi_get_ring_buffers_status = wifi_get_ring_buffers_status_stub;
+ hal_fn->wifi_get_logger_supported_feature_set =
+ wifi_get_logger_supported_feature_set_stub;
+ hal_fn->wifi_get_ring_data = wifi_get_ring_data_stub;
+ hal_fn->wifi_get_driver_version = wifi_get_driver_version_stub;
+ hal_fn->wifi_set_bssid_blacklist = wifi_set_bssid_blacklist_stub;
+ hal_fn->wifi_start_sending_offloaded_packet =
+ wifi_start_sending_offloaded_packet_stub;
+ hal_fn->wifi_stop_sending_offloaded_packet =
+ wifi_stop_sending_offloaded_packet_stub;
+ hal_fn->wifi_get_wake_reason_stats = wifi_get_wake_reason_stats_stub;
+ hal_fn->wifi_configure_nd_offload = wifi_configure_nd_offload_stub;
+ hal_fn->wifi_get_driver_memory_dump = wifi_get_driver_memory_dump_stub;
+ hal_fn->wifi_start_pkt_fate_monitoring = wifi_start_pkt_fate_monitoring_stub;
+ hal_fn->wifi_get_tx_pkt_fates = wifi_get_tx_pkt_fates_stub;
+ hal_fn->wifi_get_rx_pkt_fates = wifi_get_rx_pkt_fates_stub;
+ hal_fn->wifi_nan_enable_request = wifi_nan_enable_request_stub;
+ hal_fn->wifi_nan_disable_request = wifi_nan_disable_request_stub;
+ hal_fn->wifi_nan_publish_request = wifi_nan_publish_request_stub;
+ hal_fn->wifi_nan_publish_cancel_request =
+ wifi_nan_publish_cancel_request_stub;
+ hal_fn->wifi_nan_subscribe_request = wifi_nan_subscribe_request_stub;
+ hal_fn->wifi_nan_subscribe_cancel_request =
+ wifi_nan_subscribe_cancel_request_stub;
+ hal_fn->wifi_nan_transmit_followup_request =
+ wifi_nan_transmit_followup_request_stub;
+ hal_fn->wifi_nan_stats_request = wifi_nan_stats_request_stub;
+ hal_fn->wifi_nan_config_request = wifi_nan_config_request_stub;
+ hal_fn->wifi_nan_tca_request = wifi_nan_tca_request_stub;
+ hal_fn->wifi_nan_beacon_sdf_payload_request =
+ wifi_nan_beacon_sdf_payload_request_stub;
+ hal_fn->wifi_nan_register_handler = wifi_nan_register_handler_stub;
+ hal_fn->wifi_nan_get_version = wifi_nan_get_version_stub;
+ hal_fn->wifi_nan_get_capabilities = wifi_nan_get_capabilities_stub;
+ hal_fn->wifi_nan_data_interface_create = wifi_nan_data_interface_create_stub;
+ hal_fn->wifi_nan_data_interface_delete = wifi_nan_data_interface_delete_stub;
+ hal_fn->wifi_nan_data_request_initiator =
+ wifi_nan_data_request_initiator_stub;
+ hal_fn->wifi_nan_data_indication_response =
+ wifi_nan_data_indication_response_stub;
+ hal_fn->wifi_nan_data_end = wifi_nan_data_end_stub;
+ hal_fn->wifi_get_packet_filter_capabilities =
+ wifi_get_packet_filter_capabilities_stub;
+ hal_fn->wifi_set_packet_filter = wifi_set_packet_filter_stub;
+
+ return true;
+}
+
+} // namespace
+
+bool HalTool::InitFunctionTable(wifi_hal_fn* hal_fn) {
+ if (!init_wifi_stub_hal_func_table(hal_fn)) {
+ ALOGE("Can not initialize the basic function pointer table");
+ return false;
+ }
+
+ if (init_wifi_vendor_hal_func_table(hal_fn) != WIFI_SUCCESS) {
+ ALOGE("Can not initialize the vendor function pointer table");
+ return false;
+ }
+
+ return true;
+}
+
+bool HalTool::CanGetValidChannels(wifi_hal_fn* hal_fn) {
+ return hal_fn &&
+ (hal_fn->wifi_get_valid_channels != wifi_get_valid_channels_stub);
+}
+
+} // namespace wifi_system
+} // namespace android
diff --git a/libwifi_system/hostapd_manager.cpp b/libwifi_system/hostapd_manager.cpp
new file mode 100644
index 0000000..889fd69
--- /dev/null
+++ b/libwifi_system/hostapd_manager.cpp
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+#include "wifi_system/hostapd_manager.h"
+
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/properties.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <private/android_filesystem_config.h>
+
+#include "wifi_system/wifi.h"
+
+using android::base::StringPrintf;
+using android::base::WriteStringToFile;
+using std::string;
+using std::vector;
+using std::stringstream;
+
+namespace android {
+namespace wifi_system {
+namespace {
+
+const int kDefaultApChannel = 6;
+const char kHostapdServiceName[] = "hostapd";
+const char kHostapdConfigFilePath[] = "/data/misc/wifi/hostapd.conf";
+
+string GeneratePsk(const vector<uint8_t>& ssid,
+ const vector<uint8_t>& passphrase) {
+ string result;
+ unsigned char psk[SHA256_DIGEST_LENGTH];
+
+ // Use the PKCS#5 PBKDF2 with 4096 iterations
+ if (PKCS5_PBKDF2_HMAC_SHA1(reinterpret_cast<const char*>(passphrase.data()),
+ passphrase.size(),
+ ssid.data(), ssid.size(),
+ 4096, sizeof(psk), psk) != 1) {
+ LOG(ERROR) << "Cannot generate PSK using PKCS#5 PBKDF2";
+ return result;
+ }
+
+ stringstream ss;
+ ss << std::hex;
+ ss << std::setfill('0');
+ for (int j = 0; j < SHA256_DIGEST_LENGTH; j++) {
+ ss << std::setw(2) << static_cast<unsigned int>(psk[j]);
+ }
+ result = ss.str();
+
+ return result;
+}
+
+} // namespace
+
+bool HostapdManager::StartHostapd() {
+ if (hostapd_is_running_) {
+ LOG(ERROR) << "SoftAP is already running";
+ return false;
+ }
+
+ if (ensure_entropy_file_exists() < 0) {
+ LOG(WARNING) << "Wi-Fi entropy file was not created";
+ }
+
+ if (property_set("ctl.start", kHostapdServiceName) != 0) {
+ LOG(ERROR) << "Failed to start SoftAP";
+ return false;
+ }
+
+ LOG(DEBUG) << "SoftAP started successfully";
+ hostapd_is_running_ = true;
+ return true;
+}
+
+bool HostapdManager::IsHostapdRunning() {
+ return hostapd_is_running_;
+}
+
+bool HostapdManager::StopHostapd() {
+ if (!hostapd_is_running_) {
+ LOG(DEBUG) << "SoftAP is not running";
+ return true; // Not really an error, hostapd is already stopped.
+ }
+
+ LOG(DEBUG) << "Stopping the SoftAP service...";
+
+ if (property_set("ctl.stop", kHostapdServiceName) < 0) {
+ LOG(ERROR) << "Failed to stop hostapd service!";
+ return false;
+ }
+
+ LOG(DEBUG) << "SoftAP stopped successfully";
+ hostapd_is_running_ = false;
+ return true;
+}
+
+bool HostapdManager::WriteHostapdConfig(const string& config) {
+ if (!WriteStringToFile(config, kHostapdConfigFilePath,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
+ AID_SYSTEM, AID_WIFI)) {
+ int error = errno;
+ LOG(ERROR) << "Cannot write hostapd config to \""
+ << kHostapdConfigFilePath << "\": " << strerror(error);
+ return false;
+ }
+ return true;
+}
+
+string HostapdManager::CreateHostapdConfig(
+ const string& interface_name,
+ const vector<uint8_t> ssid,
+ bool is_hidden,
+ int channel,
+ EncryptionType encryption_type,
+ const vector<uint8_t> passphrase) {
+ string result;
+
+ if (channel < 0) {
+ channel = kDefaultApChannel;
+ }
+
+ if (ssid.size() > 32) {
+ LOG(ERROR) << "SSIDs must be <= 32 bytes long";
+ return result;
+ }
+
+ stringstream ss;
+ ss << std::hex;
+ ss << std::setfill('0');
+ for (uint8_t b : ssid) {
+ ss << std::setw(2) << static_cast<unsigned int>(b);
+ }
+ const string ssid_as_string = ss.str();
+
+ string encryption_config;
+ if (encryption_type != EncryptionType::kOpen) {
+ string psk = GeneratePsk(ssid, passphrase);
+ if (psk.empty()) {
+ return result;
+ }
+ if (encryption_type == EncryptionType::kWpa) {
+ encryption_config = StringPrintf("wpa=3\n"
+ "wpa_pairwise=TKIP CCMP\n"
+ "wpa_psk=%s\n", psk.c_str());
+ } else if (encryption_type == EncryptionType::kWpa2) {
+ encryption_config = StringPrintf("wpa=2\n"
+ "rsn_pairwise=CCMP\n"
+ "wpa_psk=%s\n", psk.c_str());
+ } else {
+ using encryption_t = std::underlying_type<EncryptionType>::type;
+ LOG(ERROR) << "Unknown encryption type ("
+ << static_cast<encryption_t>(encryption_type)
+ << ")";
+ return result;
+ }
+ }
+
+ result = StringPrintf(
+ "interface=%s\n"
+ "driver=nl80211\n"
+ "ctrl_interface=/data/misc/wifi/hostapd\n"
+ // ssid2 signals to hostapd that the value is not a literal value
+ // for use as a SSID. In this case, we're giving it a hex string
+ // and hostapd needs to expect that.
+ "ssid2=%s\n"
+ "channel=%d\n"
+ "ieee80211n=1\n"
+ "hw_mode=%c\n"
+ "ignore_broadcast_ssid=%d\n"
+ "wowlan_triggers=any\n"
+ "%s",
+ interface_name.c_str(),
+ ssid_as_string.c_str(),
+ channel,
+ (channel <= 14) ? 'g' : 'a',
+ (is_hidden) ? 1 : 0,
+ encryption_config.c_str());
+ return result;
+}
+
+} // namespace wifi_system
+} // namespace android
diff --git a/libwifi_system/include/wifi_system/hal_tool.h b/libwifi_system/include/wifi_system/hal_tool.h
new file mode 100644
index 0000000..b25893e
--- /dev/null
+++ b/libwifi_system/include/wifi_system/hal_tool.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_WIFI_SYSTEM_HAL_TOOL_H
+#define ANDROID_WIFI_SYSTEM_HAL_TOOL_H
+
+#include <hardware_legacy/wifi_hal.h>
+
+namespace android {
+namespace wifi_system {
+
+// Utilities for interacting with the HAL.
+class HalTool {
+ public:
+ HalTool() = default;
+ virtual ~HalTool() = default;
+
+ virtual bool InitFunctionTable(wifi_hal_fn* hal_fn);
+
+ virtual bool CanGetValidChannels(wifi_hal_fn* hal_fn);
+}; // class HalTool
+
+} // namespace wifi_system
+} // namespace android
+
+#endif // ANDROID_WIFI_SYSTEM_HAL_TOOL_H
diff --git a/libwifi_system/include/wifi_system/hostapd_manager.h b/libwifi_system/include/wifi_system/hostapd_manager.h
new file mode 100644
index 0000000..2bffde4
--- /dev/null
+++ b/libwifi_system/include/wifi_system/hostapd_manager.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_WIFI_SYSTEM_HOSTAPD_MANAGER_H
+#define ANDROID_WIFI_SYSTEM_HOSTAPD_MANAGER_H
+
+#include <string>
+#include <vector>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace wifi_system {
+
+class HostapdManager {
+ public:
+ enum class EncryptionType {
+ kOpen,
+ kWpa,
+ kWpa2, // Strongly prefer this if at all possible.
+ };
+
+ HostapdManager() = default;
+ virtual ~HostapdManager() = default;
+
+ // Request that hostapd be started.
+ // Returns true on success.
+ virtual bool StartHostapd();
+
+ // Returns true if hostapd is currently running.
+ virtual bool IsHostapdRunning();
+
+ // Request that a running instance of hostapd be stopped.
+ // Returns true on success.
+ virtual bool StopHostapd();
+
+ // Create a string suitable for writing to the hostapd configuration file.
+ // |interface_name| is a network interface name (e.g. "wlan0").
+ // |ssid| is the SSID used by the configurated network.
+ // |is_hidden| is true iff hostapd should not broadcast the SSID.
+ // |channel| is the WiFi channel (e.g. 6) or <0 for a default value.
+ // |encryption_type| defines the encryption of the configured network.
+ // |passphrase| is ignored for kOpen networks.
+ //
+ // Returns an empty string on failure.
+ virtual std::string CreateHostapdConfig(
+ const std::string& interface_name,
+ const std::vector<uint8_t> ssid,
+ bool is_hidden,
+ int channel,
+ EncryptionType encryption,
+ const std::vector<uint8_t> passphrase);
+
+ // Write out a hostapd configuration file created via CreateHostapdConfig().
+ // Future instances of hostapd will use this new configuration.
+ // Returns true if the configuration file is successfully written.
+ virtual bool WriteHostapdConfig(const std::string& config_file);
+
+ private:
+ bool hostapd_is_running_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(HostapdManager);
+}; // class HostapdManager
+
+} // namespace wifi_system
+} // namespace android
+
+#endif // ANDROID_WIFI_SYSTEM_HOSTAPD_MANAGER_H
diff --git a/libwifi_system/include/wifi_system/interface_tool.h b/libwifi_system/include/wifi_system/interface_tool.h
new file mode 100644
index 0000000..2e58379
--- /dev/null
+++ b/libwifi_system/include/wifi_system/interface_tool.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_WIFI_SYSTEM_INTERFACE_TOOL_H
+#define ANDROID_WIFI_SYSTEM_INTERFACE_TOOL_H
+
+namespace android {
+namespace wifi_system {
+
+class InterfaceTool {
+ public:
+ InterfaceTool() = default;
+ virtual ~InterfaceTool() = default;
+
+ // Set the interface named by |if_name| up or down.
+ // Returns true on success, false otherwise.
+ virtual bool SetUpState(const char* if_name, bool request_up);
+
+ // A helpful alias for SetUpState() that assumes there is a single system
+ // WiFi interface. Prefer this form if you're hardcoding "wlan0" to help us
+ // identify all the places we are hardcoding the name of the wifi interface.
+ virtual bool SetWifiUpState(bool request_up);
+
+}; // class InterfaceTool
+
+} // namespace wifi_system
+} // namespace android
+
+#endif // ANDROID_WIFI_SYSTEM_INTERFACE_TOOL_H
diff --git a/libwifi_system/include/wifi_system/wifi.h b/libwifi_system/include/wifi_system/wifi.h
new file mode 100644
index 0000000..3e662a1
--- /dev/null
+++ b/libwifi_system/include/wifi_system/wifi.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_WIFI_SYSTEM_WIFI_H
+#define ANDROID_WIFI_SYSTEM_WIFI_H
+
+#include <cstddef>
+
+namespace android {
+namespace wifi_system {
+
+extern const char kWiFiEntropyFile[];
+
+/**
+ * Start supplicant.
+ *
+ * @return 0 on success, < 0 on failure.
+ */
+int wifi_start_supplicant(int p2pSupported);
+
+/**
+ * Stop supplicant.
+ *
+ * @return 0 on success, < 0 on failure.
+ */
+int wifi_stop_supplicant(int p2pSupported);
+
+/**
+ * Open a connection to supplicant
+ *
+ * @return 0 on success, < 0 on failure.
+ */
+int wifi_connect_to_supplicant();
+
+/**
+ * Close connection to supplicant
+ *
+ * @return 0 on success, < 0 on failure.
+ */
+void wifi_close_supplicant_connection();
+
+/**
+ * wifi_wait_for_event() performs a blocking call to
+ * get a Wi-Fi event and returns a string representing
+ * a Wi-Fi event when it occurs.
+ *
+ * @param buf is the buffer that receives the event
+ * @param len is the maximum length of the buffer
+ *
+ * @returns number of bytes in buffer, 0 if no
+ * event (for instance, no connection), and less than 0
+ * if there is an error.
+ */
+int wifi_wait_for_event(char* buf, size_t len);
+
+/**
+ * wifi_command() issues a command to the Wi-Fi driver.
+ *
+ * Android extends the standard commands listed at
+ * /link http://hostap.epitest.fi/wpa_supplicant/devel/ctrl_iface_page.html
+ * to include support for sending commands to the driver:
+ *
+ * See wifi/java/android/net/wifi/WifiNative.java for the details of
+ * driver commands that are supported
+ *
+ * @param command is the string command (preallocated with 32 bytes)
+ * @param commandlen is command buffer length
+ * @param reply is a buffer to receive a reply string
+ * @param reply_len on entry, this is the maximum length of
+ * the reply buffer. On exit, the number of
+ * bytes in the reply buffer.
+ *
+ * @return 0 if successful, < 0 if an error.
+ */
+int wifi_command(const char* command, char* reply, size_t* reply_len);
+
+/**
+ * Check and create if necessary initial entropy file
+ */
+int ensure_entropy_file_exists();
+
+} // namespace wifi_system
+} // namespace android
+
+#endif // ANDROID_WIFI_SYSTEM_WIFI_H
diff --git a/libwifi_system/interface_tool.cpp b/libwifi_system/interface_tool.cpp
new file mode 100644
index 0000000..ed0b9f7
--- /dev/null
+++ b/libwifi_system/interface_tool.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+#include "wifi_system/interface_tool.h"
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+/* We need linux/if.h for flags like IFF_UP. Sadly, it forward declares
+ struct sockaddr and must be included after sys/socket.h. */
+#include <linux/if.h>
+
+#include <android-base/unique_fd.h>
+#include <log/log.h>
+
+namespace android {
+namespace wifi_system {
+namespace {
+
+const char kWlan0InterfaceName[] = "wlan0";
+
+} // namespace
+
+bool InterfaceTool::SetUpState(const char* if_name, bool request_up) {
+ base::unique_fd sock(socket(PF_INET, SOCK_DGRAM, 0));
+ if (sock.get() < 0) {
+ ALOGE("Bad socket: %d, errno: %d\n", sock.get(), errno);
+ return false;
+ }
+
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ if (strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)) >=
+ sizeof(ifr.ifr_name)) {
+ ALOGE("Interface name is too long: %s\n", if_name);
+ return false;
+ }
+
+ if (TEMP_FAILURE_RETRY(ioctl(sock.get(), SIOCGIFFLAGS, &ifr)) != 0) {
+ ALOGE("Could not read interface %s errno: %d\n", if_name, errno);
+ return false;
+ }
+
+ const bool currently_up = ifr.ifr_flags & IFF_UP;
+ if (currently_up == request_up) {
+ return true;
+ }
+
+ if (request_up) {
+ ifr.ifr_flags |= IFF_UP;
+ } else {
+ ifr.ifr_flags &= ~IFF_UP;
+ }
+
+ if (TEMP_FAILURE_RETRY(ioctl(sock.get(), SIOCSIFFLAGS, &ifr)) != 0) {
+ ALOGE("Could not set interface %s flags: %d\n", if_name, errno);
+ return false;
+ }
+
+ return true;
+}
+
+bool InterfaceTool::SetWifiUpState(bool request_up) {
+ return SetUpState(kWlan0InterfaceName, request_up);
+}
+
+} // namespace wifi_system
+} // namespace android
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
new file mode 100644
index 0000000..312428f
--- /dev/null
+++ b/libwifi_system/testlib/include/wifi_system_test/mock_hal_tool.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_WIFI_SYSTEM_TEST_MOCK_HAL_TOOL_H
+#define ANDROID_WIFI_SYSTEM_TEST_MOCK_HAL_TOOL_H
+
+#include <wifi_system/hal_tool.h>
+
+namespace android {
+namespace wifi_system {
+
+class MockHalTool : public HalTool {
+ public:
+ ~MockHalTool() override = default;
+
+ MOCK_METHOD1(InitFunctionTable, bool(wifi_hal_fn*));
+ MOCK_METHOD1(CanGetValidChannels, bool(wifi_hal_fn*));
+
+}; // class MockHalTool
+
+} // namespace wifi_system
+} // namespace android
+
+#endif // ANDROID_WIFI_SYSTEM_TEST_MOCK_HAL_TOOL_H
diff --git a/libwifi_system/testlib/include/wifi_system_test/mock_interface_tool.h b/libwifi_system/testlib/include/wifi_system_test/mock_interface_tool.h
new file mode 100644
index 0000000..13e6780
--- /dev/null
+++ b/libwifi_system/testlib/include/wifi_system_test/mock_interface_tool.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_WIFI_SYSTEM_TEST_MOCK_INTERFACE_TOOL_H
+#define ANDROID_WIFI_SYSTEM_TEST_MOCK_INTERFACE_TOOL_H
+
+#include <wifi_system/interface_tool.h>
+
+namespace android {
+namespace wifi_system {
+
+class MockInterfaceTool : public InterfaceTool {
+ public:
+ ~MockInterfaceTool() override = default;
+
+ MOCK_METHOD2(SetUpState, bool(const char* if_name, bool request_up));
+ MOCK_METHOD1(SetWifiUpState, bool(bool request_up));
+
+}; // class MockInterfaceTool
+
+} // namespace wifi_system
+} // namespace android
+
+#endif // ANDROID_WIFI_SYSTEM_TEST_MOCK_INTERFACE_TOOL_H
diff --git a/libwifi_system/tests/hostapd_manager_unittest.cpp b/libwifi_system/tests/hostapd_manager_unittest.cpp
new file mode 100644
index 0000000..f8ebd99
--- /dev/null
+++ b/libwifi_system/tests/hostapd_manager_unittest.cpp
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+#include "wifi_system/hostapd_manager.h"
+
+using std::string;
+using std::vector;
+
+namespace android {
+namespace wifi_system {
+namespace {
+
+const char kTestInterfaceName[] = "foobar0";
+const char kTestSsidStr[] = "helloisitme";
+const char kTestPassphraseStr[] = "yourelookingfor";
+const int kTestChannel = 2;
+
+#define CONFIG_COMMON_PREFIX \
+ "interface=foobar0\n" \
+ "driver=nl80211\n" \
+ "ctrl_interface=/data/misc/wifi/hostapd\n" \
+ "ssid2=68656c6c6f" "6973" "6974" "6d65\n" \
+ "channel=2\n" \
+ "ieee80211n=1\n" \
+ "hw_mode=g\n"
+
+// If you generate your config file with both the test ssid
+// and the test passphrase, you'll get this line in the config.
+#define CONFIG_PSK_LINE \
+ "wpa_psk=dffa36815281e5a6eca1910f254717fa2528681335e3bbec5966d2aa9221a66e\n"
+
+#define CONFIG_WPA_SUFFIX \
+ "wpa=3\n" \
+ "wpa_pairwise=TKIP CCMP\n" \
+ CONFIG_PSK_LINE
+
+#define CONFIG_WPA2_SUFFIX \
+ "wpa=2\n" \
+ "rsn_pairwise=CCMP\n" \
+ CONFIG_PSK_LINE
+
+const char kExpectedOpenConfig[] =
+ CONFIG_COMMON_PREFIX
+ "ignore_broadcast_ssid=0\n"
+ "wowlan_triggers=any\n";
+
+const char kExpectedWpaConfig[] =
+ CONFIG_COMMON_PREFIX
+ "ignore_broadcast_ssid=0\n"
+ "wowlan_triggers=any\n"
+ CONFIG_WPA_SUFFIX;
+
+const char kExpectedWpa2Config[] =
+ CONFIG_COMMON_PREFIX
+ "ignore_broadcast_ssid=0\n"
+ "wowlan_triggers=any\n"
+ CONFIG_WPA2_SUFFIX;
+
+class HostapdManagerTest : public ::testing::Test {
+ protected:
+ string GetConfigForEncryptionType(
+ HostapdManager::EncryptionType encryption_type) {
+ return HostapdManager().CreateHostapdConfig(
+ kTestInterfaceName,
+ cstr2vector(kTestSsidStr),
+ false, // not hidden
+ kTestChannel,
+ encryption_type,
+ cstr2vector(kTestPassphraseStr));
+ }
+
+ vector<uint8_t> cstr2vector(const char* data) {
+ return vector<uint8_t>(data, data + strlen(data));
+ }
+}; // class HostapdManagerTest
+
+} // namespace
+
+TEST_F(HostapdManagerTest, GeneratesCorrectOpenConfig) {
+ string config = GetConfigForEncryptionType(
+ HostapdManager::EncryptionType::kOpen);
+ EXPECT_FALSE(config.empty());
+ EXPECT_EQ(kExpectedOpenConfig, config);
+}
+
+TEST_F(HostapdManagerTest, GeneratesCorrectWpaConfig) {
+ string config = GetConfigForEncryptionType(
+ HostapdManager::EncryptionType::kWpa);
+ EXPECT_FALSE(config.empty());
+ EXPECT_EQ(kExpectedWpaConfig, config);
+}
+
+TEST_F(HostapdManagerTest, GeneratesCorrectWpa2Config) {
+ string config = GetConfigForEncryptionType(
+ HostapdManager::EncryptionType::kWpa2);
+ EXPECT_FALSE(config.empty());
+ EXPECT_EQ(kExpectedWpa2Config, config);
+}
+
+TEST_F(HostapdManagerTest, RespectsHiddenSetting) {
+ string config = HostapdManager().CreateHostapdConfig(
+ kTestInterfaceName,
+ cstr2vector(kTestSsidStr),
+ true,
+ kTestChannel,
+ HostapdManager::EncryptionType::kOpen,
+ vector<uint8_t>());
+ EXPECT_FALSE(config.find("ignore_broadcast_ssid=1\n") == string::npos);
+ EXPECT_TRUE(config.find("ignore_broadcast_ssid=0\n") == string::npos);
+}
+
+TEST_F(HostapdManagerTest, CorrectlyInfersHwMode) {
+ string config = HostapdManager().CreateHostapdConfig(
+ kTestInterfaceName,
+ cstr2vector(kTestSsidStr),
+ true,
+ 44,
+ HostapdManager::EncryptionType::kOpen,
+ vector<uint8_t>());
+ EXPECT_FALSE(config.find("hw_mode=a\n") == string::npos);
+ EXPECT_TRUE(config.find("hw_mode=g\n") == string::npos);
+}
+
+
+} // namespace wifi_system
+} // namespace android
diff --git a/libwifi_system/tests/main.cpp b/libwifi_system/tests/main.cpp
new file mode 100644
index 0000000..51e8dad
--- /dev/null
+++ b/libwifi_system/tests/main.cpp
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ // Force ourselves to always log to stderr
+ android::base::InitLogging(argv, android::base::StderrLogger);
+ return RUN_ALL_TESTS();
+}
+
diff --git a/libwifi_system/wifi.cpp b/libwifi_system/wifi.cpp
new file mode 100644
index 0000000..3b08cf1
--- /dev/null
+++ b/libwifi_system/wifi.cpp
@@ -0,0 +1,566 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wifi_system/wifi.h"
+#define LOG_TAG "WifiHW"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <cutils/log.h>
+#include <cutils/memory.h>
+#include <cutils/misc.h>
+#include <cutils/properties.h>
+#include <private/android_filesystem_config.h>
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+#ifdef LIBWPA_CLIENT_EXISTS
+#include <libwpa_client/wpa_ctrl.h>
+#else
+#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING "
+struct wpa_ctrl {};
+void wpa_ctrl_cleanup(void) {}
+struct wpa_ctrl* wpa_ctrl_open(const char* ctrl_path) {
+ return NULL;
+}
+void wpa_ctrl_close(struct wpa_ctrl* ctrl) {}
+int wpa_ctrl_request(struct wpa_ctrl* ctrl, const char* cmd, size_t cmd_len,
+ char* reply, size_t* reply_len,
+ void (*msg_cb)(char* msg, size_t len)) {
+ return 0;
+}
+int wpa_ctrl_attach(struct wpa_ctrl* ctrl) { return 0; }
+int wpa_ctrl_detach(struct wpa_ctrl* ctrl) { return 0; }
+int wpa_ctrl_recv(struct wpa_ctrl* ctrl, char* reply, size_t* reply_len) {
+ return 0;
+}
+int wpa_ctrl_get_fd(struct wpa_ctrl* ctrl) { return 0; }
+#endif // defined LIBWPA_CLIENT_EXISTS
+
+namespace android {
+namespace wifi_system {
+namespace {
+
+/* socket pair used to exit from a blocking read */
+int exit_sockets[2];
+struct wpa_ctrl* ctrl_conn;
+struct wpa_ctrl* monitor_conn;
+
+static char primary_iface[PROPERTY_VALUE_MAX];
+// TODO: use new ANDROID_SOCKET mechanism, once support for multiple
+// sockets is in
+
+#define WIFI_TEST_INTERFACE "sta"
+
+#define WIFI_DRIVER_LOADER_DELAY 1000000
+
+const char IFACE_DIR[] = "/data/system/wpa_supplicant";
+const char SUPPLICANT_NAME[] = "wpa_supplicant";
+const char SUPP_PROP_NAME[] = "init.svc.wpa_supplicant";
+const char P2P_SUPPLICANT_NAME[] = "p2p_supplicant";
+const char P2P_PROP_NAME[] = "init.svc.p2p_supplicant";
+const char SUPP_CONFIG_TEMPLATE[] = "/system/etc/wifi/wpa_supplicant.conf";
+const char SUPP_CONFIG_FILE[] = "/data/misc/wifi/wpa_supplicant.conf";
+const char P2P_CONFIG_FILE[] = "/data/misc/wifi/p2p_supplicant.conf";
+
+const char IFNAME[] = "IFNAME=";
+#define IFNAMELEN (sizeof(IFNAME) - 1)
+const char WPA_EVENT_IGNORE[] = "CTRL-EVENT-IGNORE ";
+
+unsigned char dummy_key[21] = {0x02, 0x11, 0xbe, 0x33, 0x43, 0x35, 0x68,
+ 0x47, 0x84, 0x99, 0xa9, 0x2b, 0x1c, 0xd3,
+ 0xee, 0xff, 0xf1, 0xe2, 0xf3, 0xf4, 0xf5};
+
+/* Is either SUPPLICANT_NAME or P2P_SUPPLICANT_NAME */
+char supplicant_name[PROPERTY_VALUE_MAX];
+/* Is either SUPP_PROP_NAME or P2P_PROP_NAME */
+char supplicant_prop_name[PROPERTY_KEY_MAX];
+
+void wifi_close_sockets() {
+ if (ctrl_conn != NULL) {
+ wpa_ctrl_close(ctrl_conn);
+ ctrl_conn = NULL;
+ }
+
+ if (monitor_conn != NULL) {
+ wpa_ctrl_close(monitor_conn);
+ monitor_conn = NULL;
+ }
+
+ if (exit_sockets[0] >= 0) {
+ close(exit_sockets[0]);
+ exit_sockets[0] = -1;
+ }
+
+ if (exit_sockets[1] >= 0) {
+ close(exit_sockets[1]);
+ exit_sockets[1] = -1;
+ }
+}
+
+int ensure_config_file_exists(const char* config_file) {
+ char buf[2048];
+ int srcfd, destfd;
+ int nread;
+ int ret;
+
+ ret = access(config_file, R_OK | W_OK);
+ if ((ret == 0) || (errno == EACCES)) {
+ if ((ret != 0) &&
+ (chmod(config_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) != 0)) {
+ ALOGE("Cannot set RW to \"%s\": %s", config_file, strerror(errno));
+ return -1;
+ }
+ return 0;
+ } else if (errno != ENOENT) {
+ ALOGE("Cannot access \"%s\": %s", config_file, strerror(errno));
+ return -1;
+ }
+
+ srcfd = TEMP_FAILURE_RETRY(open(SUPP_CONFIG_TEMPLATE, O_RDONLY));
+ if (srcfd < 0) {
+ ALOGE("Cannot open \"%s\": %s", SUPP_CONFIG_TEMPLATE, strerror(errno));
+ return -1;
+ }
+
+ destfd = TEMP_FAILURE_RETRY(open(config_file, O_CREAT | O_RDWR, 0660));
+ if (destfd < 0) {
+ close(srcfd);
+ ALOGE("Cannot create \"%s\": %s", config_file, strerror(errno));
+ return -1;
+ }
+
+ while ((nread = TEMP_FAILURE_RETRY(read(srcfd, buf, sizeof(buf)))) != 0) {
+ if (nread < 0) {
+ ALOGE("Error reading \"%s\": %s", SUPP_CONFIG_TEMPLATE, strerror(errno));
+ close(srcfd);
+ close(destfd);
+ unlink(config_file);
+ return -1;
+ }
+ TEMP_FAILURE_RETRY(write(destfd, buf, nread));
+ }
+
+ close(destfd);
+ close(srcfd);
+
+ /* chmod is needed because open() didn't set permisions properly */
+ if (chmod(config_file, 0660) < 0) {
+ ALOGE("Error changing permissions of %s to 0660: %s", config_file,
+ strerror(errno));
+ unlink(config_file);
+ return -1;
+ }
+
+ if (chown(config_file, AID_SYSTEM, AID_WIFI) < 0) {
+ ALOGE("Error changing group ownership of %s to %d: %s", config_file,
+ AID_WIFI, strerror(errno));
+ unlink(config_file);
+ return -1;
+ }
+ return 0;
+}
+
+} // namespace
+
+const char kWiFiEntropyFile[] = "/data/misc/wifi/entropy.bin";
+
+int wifi_start_supplicant(int p2p_supported) {
+ char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
+ int count = 200; /* wait at most 20 seconds for completion */
+ const prop_info* pi;
+ unsigned serial = 0;
+
+ if (p2p_supported) {
+ strcpy(supplicant_name, P2P_SUPPLICANT_NAME);
+ strcpy(supplicant_prop_name, P2P_PROP_NAME);
+
+ /* Ensure p2p config file is created */
+ if (ensure_config_file_exists(P2P_CONFIG_FILE) < 0) {
+ ALOGE("Failed to create a p2p config file");
+ return -1;
+ }
+
+ } else {
+ strcpy(supplicant_name, SUPPLICANT_NAME);
+ strcpy(supplicant_prop_name, SUPP_PROP_NAME);
+ }
+
+ /* Check whether already running */
+ if (property_get(supplicant_prop_name, supp_status, NULL) &&
+ strcmp(supp_status, "running") == 0) {
+ return 0;
+ }
+
+ /* Before starting the daemon, make sure its config file exists */
+ if (ensure_config_file_exists(SUPP_CONFIG_FILE) < 0) {
+ ALOGE("Wi-Fi will not be enabled");
+ return -1;
+ }
+
+ if (ensure_entropy_file_exists() < 0) {
+ ALOGE("Wi-Fi entropy file was not created");
+ }
+
+ /* Clear out any stale socket files that might be left over. */
+ wpa_ctrl_cleanup();
+
+ /* Reset sockets used for exiting from hung state */
+ exit_sockets[0] = exit_sockets[1] = -1;
+
+ /*
+ * Get a reference to the status property, so we can distinguish
+ * the case where it goes stopped => running => stopped (i.e.,
+ * it start up, but fails right away) from the case in which
+ * it starts in the stopped state and never manages to start
+ * running at all.
+ */
+ pi = __system_property_find(supplicant_prop_name);
+ if (pi != NULL) {
+ serial = __system_property_serial(pi);
+ }
+ property_get("wifi.interface", primary_iface, WIFI_TEST_INTERFACE);
+
+ property_set("ctl.start", supplicant_name);
+ sched_yield();
+
+ while (count-- > 0) {
+ if (pi == NULL) {
+ pi = __system_property_find(supplicant_prop_name);
+ }
+ if (pi != NULL) {
+ /*
+ * property serial updated means that init process is scheduled
+ * after we sched_yield, further property status checking is based on this
+ */
+ if (__system_property_serial(pi) != serial) {
+ __system_property_read(pi, NULL, supp_status);
+ if (strcmp(supp_status, "running") == 0) {
+ return 0;
+ } else if (strcmp(supp_status, "stopped") == 0) {
+ return -1;
+ }
+ }
+ }
+ usleep(100000);
+ }
+ return -1;
+}
+
+int wifi_stop_supplicant(int p2p_supported) {
+ char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
+ int count = 50; /* wait at most 5 seconds for completion */
+
+ if (p2p_supported) {
+ strcpy(supplicant_name, P2P_SUPPLICANT_NAME);
+ strcpy(supplicant_prop_name, P2P_PROP_NAME);
+ } else {
+ strcpy(supplicant_name, SUPPLICANT_NAME);
+ strcpy(supplicant_prop_name, SUPP_PROP_NAME);
+ }
+
+ /* Check whether supplicant already stopped */
+ if (property_get(supplicant_prop_name, supp_status, NULL) &&
+ strcmp(supp_status, "stopped") == 0) {
+ return 0;
+ }
+
+ property_set("ctl.stop", supplicant_name);
+ sched_yield();
+
+ while (count-- > 0) {
+ if (property_get(supplicant_prop_name, supp_status, NULL)) {
+ if (strcmp(supp_status, "stopped") == 0) return 0;
+ }
+ usleep(100000);
+ }
+ ALOGE("Failed to stop supplicant");
+ return -1;
+}
+
+namespace {
+
+int wifi_connect_on_socket_path(const char* path) {
+ char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
+
+ /* Make sure supplicant is running */
+ if (!property_get(supplicant_prop_name, supp_status, NULL) ||
+ strcmp(supp_status, "running") != 0) {
+ ALOGE("Supplicant not running, cannot connect");
+ return -1;
+ }
+
+ ctrl_conn = wpa_ctrl_open(path);
+ if (ctrl_conn == NULL) {
+ ALOGE("Unable to open connection to supplicant on \"%s\": %s", path,
+ strerror(errno));
+ return -1;
+ }
+ monitor_conn = wpa_ctrl_open(path);
+ if (monitor_conn == NULL) {
+ wpa_ctrl_close(ctrl_conn);
+ ctrl_conn = NULL;
+ return -1;
+ }
+ if (wpa_ctrl_attach(monitor_conn) != 0) {
+ wpa_ctrl_close(monitor_conn);
+ wpa_ctrl_close(ctrl_conn);
+ ctrl_conn = monitor_conn = NULL;
+ return -1;
+ }
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, exit_sockets) == -1) {
+ wpa_ctrl_close(monitor_conn);
+ wpa_ctrl_close(ctrl_conn);
+ ctrl_conn = monitor_conn = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int wifi_send_command(const char* cmd, char* reply, size_t* reply_len) {
+ int ret;
+ if (ctrl_conn == NULL) {
+ ALOGV("Not connected to wpa_supplicant - \"%s\" command dropped.\n", cmd);
+ return -1;
+ }
+ ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), reply, reply_len, NULL);
+ if (ret == -2) {
+ ALOGD("'%s' command timed out.\n", cmd);
+ /* unblocks the monitor receive socket for termination */
+ TEMP_FAILURE_RETRY(write(exit_sockets[0], "T", 1));
+ return -2;
+ } else if (ret < 0 || strncmp(reply, "FAIL", 4) == 0) {
+ return -1;
+ }
+ if (strncmp(cmd, "PING", 4) == 0) {
+ reply[*reply_len] = '\0';
+ }
+ return 0;
+}
+
+int wifi_supplicant_connection_active() {
+ char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
+
+ if (property_get(supplicant_prop_name, supp_status, NULL)) {
+ if (strcmp(supp_status, "stopped") == 0) return -1;
+ }
+
+ return 0;
+}
+
+int wifi_ctrl_recv(char* reply, size_t* reply_len) {
+ int res;
+ int ctrlfd = wpa_ctrl_get_fd(monitor_conn);
+ struct pollfd rfds[2];
+
+ memset(rfds, 0, 2 * sizeof(struct pollfd));
+ rfds[0].fd = ctrlfd;
+ rfds[0].events |= POLLIN;
+ rfds[1].fd = exit_sockets[1];
+ rfds[1].events |= POLLIN;
+ do {
+ res = TEMP_FAILURE_RETRY(poll(rfds, 2, 30000));
+ if (res < 0) {
+ ALOGE("Error poll = %d", res);
+ return res;
+ } else if (res == 0) {
+ /* timed out, check if supplicant is active
+ * or not ..
+ */
+ res = wifi_supplicant_connection_active();
+ if (res < 0) return -2;
+ }
+ } while (res == 0);
+
+ if (rfds[0].revents & POLLIN) {
+ return wpa_ctrl_recv(monitor_conn, reply, reply_len);
+ }
+
+ /* it is not rfds[0], then it must be rfts[1] (i.e. the exit socket)
+ * or we timed out. In either case, this call has failed ..
+ */
+ return -2;
+}
+
+int wifi_wait_on_socket(char* buf, size_t buflen) {
+ size_t nread = buflen - 1;
+ int result;
+ char* match;
+ char* match2;
+
+ if (monitor_conn == NULL) {
+ return snprintf(buf, buflen, "IFNAME=%s %s - connection closed",
+ primary_iface, WPA_EVENT_TERMINATING);
+ }
+
+ result = wifi_ctrl_recv(buf, &nread);
+
+ /* Terminate reception on exit socket */
+ if (result == -2) {
+ return snprintf(buf, buflen, "IFNAME=%s %s - connection closed",
+ primary_iface, WPA_EVENT_TERMINATING);
+ }
+
+ if (result < 0) {
+ ALOGD("wifi_ctrl_recv failed: %s\n", strerror(errno));
+ return snprintf(buf, buflen, "IFNAME=%s %s - recv error", primary_iface,
+ WPA_EVENT_TERMINATING);
+ }
+ buf[nread] = '\0';
+ /* Check for EOF on the socket */
+ if (result == 0 && nread == 0) {
+ /* Fabricate an event to pass up */
+ ALOGD("Received EOF on supplicant socket\n");
+ return snprintf(buf, buflen, "IFNAME=%s %s - signal 0 received",
+ primary_iface, WPA_EVENT_TERMINATING);
+ }
+ /*
+ * Events strings are in the format
+ *
+ * IFNAME=iface <N>CTRL-EVENT-XXX
+ * or
+ * <N>CTRL-EVENT-XXX
+ *
+ * where N is the message level in numerical form (0=VERBOSE, 1=DEBUG,
+ * etc.) and XXX is the event name. The level information is not useful
+ * to us, so strip it off.
+ */
+
+ if (strncmp(buf, IFNAME, IFNAMELEN) == 0) {
+ match = strchr(buf, ' ');
+ if (match != NULL) {
+ if (match[1] == '<') {
+ match2 = strchr(match + 2, '>');
+ if (match2 != NULL) {
+ nread -= (match2 - match);
+ memmove(match + 1, match2 + 1, nread - (match - buf) + 1);
+ }
+ }
+ } else {
+ return snprintf(buf, buflen, "%s", WPA_EVENT_IGNORE);
+ }
+ } else if (buf[0] == '<') {
+ match = strchr(buf, '>');
+ if (match != NULL) {
+ nread -= (match + 1 - buf);
+ memmove(buf, match + 1, nread + 1);
+ ALOGV("supplicant generated event without interface - %s\n", buf);
+ }
+ } else {
+ /* let the event go as is! */
+ ALOGW(
+ "supplicant generated event without interface and without message "
+ "level - %s\n",
+ buf);
+ }
+
+ return nread;
+}
+
+} // namespace
+
+/* Establishes the control and monitor socket connections on the interface */
+int wifi_connect_to_supplicant() {
+ static char path[PATH_MAX];
+
+ if (access(IFACE_DIR, F_OK) == 0) {
+ snprintf(path, sizeof(path), "%s/%s", IFACE_DIR, primary_iface);
+ } else {
+ snprintf(path, sizeof(path), "@android:wpa_%s", primary_iface);
+ }
+ return wifi_connect_on_socket_path(path);
+}
+
+void wifi_close_supplicant_connection() {
+ char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
+ int count =
+ 50; /* wait at most 5 seconds to ensure init has stopped stupplicant */
+
+ wifi_close_sockets();
+
+ while (count-- > 0) {
+ if (property_get(supplicant_prop_name, supp_status, NULL)) {
+ if (strcmp(supp_status, "stopped") == 0) return;
+ }
+ usleep(100000);
+ }
+}
+
+int wifi_wait_for_event(char* buf, size_t buflen) {
+ return wifi_wait_on_socket(buf, buflen);
+}
+
+int wifi_command(const char* command, char* reply, size_t* reply_len) {
+ return wifi_send_command(command, reply, reply_len);
+}
+
+int ensure_entropy_file_exists() {
+ int ret;
+ int destfd;
+
+ ret = access(kWiFiEntropyFile, R_OK | W_OK);
+ if ((ret == 0) || (errno == EACCES)) {
+ if ((ret != 0) &&
+ (chmod(kWiFiEntropyFile, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) != 0)) {
+ ALOGE("Cannot set RW to \"%s\": %s", kWiFiEntropyFile, strerror(errno));
+ return -1;
+ }
+ return 0;
+ }
+ destfd = TEMP_FAILURE_RETRY(open(kWiFiEntropyFile, O_CREAT | O_RDWR, 0660));
+ if (destfd < 0) {
+ ALOGE("Cannot create \"%s\": %s", kWiFiEntropyFile, strerror(errno));
+ return -1;
+ }
+
+ if (TEMP_FAILURE_RETRY(write(destfd, dummy_key, sizeof(dummy_key))) !=
+ sizeof(dummy_key)) {
+ ALOGE("Error writing \"%s\": %s", kWiFiEntropyFile, strerror(errno));
+ close(destfd);
+ return -1;
+ }
+ close(destfd);
+
+ /* chmod is needed because open() didn't set permisions properly */
+ if (chmod(kWiFiEntropyFile, 0660) < 0) {
+ ALOGE("Error changing permissions of %s to 0660: %s", kWiFiEntropyFile,
+ strerror(errno));
+ unlink(kWiFiEntropyFile);
+ return -1;
+ }
+
+ if (chown(kWiFiEntropyFile, AID_SYSTEM, AID_WIFI) < 0) {
+ ALOGE("Error changing group ownership of %s to %d: %s", kWiFiEntropyFile,
+ AID_WIFI, strerror(errno));
+ unlink(kWiFiEntropyFile);
+ return -1;
+ }
+ return 0;
+}
+
+} // namespace wifi_system
+} // namespace android
diff --git a/service/Android.mk b/service/Android.mk
index 5c6c839..d949ea4 100644
--- a/service/Android.mk
+++ b/service/Android.mk
@@ -16,79 +16,15 @@
ifneq ($(TARGET_BUILD_PDK), true)
-# Make HAL stub library 1
-# ============================================================
-
-include $(CLEAR_VARS)
-
-LOCAL_REQUIRED_MODULES :=
-
-LOCAL_CFLAGS += -Wall -Werror -Wextra -Wno-unused-parameter -Wno-unused-function \
- -Wunused-variable -Winit-self -Wwrite-strings -Wshadow
-
-LOCAL_C_INCLUDES += \
- external/libnl-headers \
- $(call include-path-for, libhardware_legacy)/hardware_legacy
-
-LOCAL_SRC_FILES := \
- lib/wifi_hal.cpp
-
-LOCAL_MODULE := libwifi-hal
-
-include $(BUILD_STATIC_LIBRARY)
-
-# Make HAL stub library 2
-# ============================================================
-
-include $(CLEAR_VARS)
-
-LOCAL_REQUIRED_MODULES :=
-
-LOCAL_CFLAGS += -Wall -Werror -Wextra -Wno-unused-parameter -Wno-unused-function \
- -Wunused-variable -Winit-self -Wwrite-strings -Wshadow
-
-LOCAL_C_INCLUDES += \
- $(LOCAL_PATH)/jni \
- external/libnl-headers \
- $(call include-path-for, libhardware_legacy)/hardware_legacy
-
-LOCAL_SRC_FILES := \
- lib/wifi_hal_stub.cpp
-
-LOCAL_MODULE := libwifi-hal-stub
-
-include $(BUILD_STATIC_LIBRARY)
-
-# set correct hal library path
-# ============================================================
-LIB_WIFI_HAL := libwifi-hal
-
-ifeq ($(BOARD_WLAN_DEVICE), bcmdhd)
- LIB_WIFI_HAL := libwifi-hal-bcm
-else ifeq ($(BOARD_WLAN_DEVICE), qcwcn)
- LIB_WIFI_HAL := libwifi-hal-qcom
-else ifeq ($(BOARD_WLAN_DEVICE), mrvl)
- # this is commented because none of the nexus devices
- # that sport Marvell's wifi have support for HAL
- # LIB_WIFI_HAL := libwifi-hal-mrvl
-else ifeq ($(BOARD_WLAN_DEVICE), MediaTek)
- # support MTK WIFI HAL
- LIB_WIFI_HAL := libwifi-hal-mt66xx
-endif
-
# Make the JNI part
# ============================================================
include $(CLEAR_VARS)
-LOCAL_REQUIRED_MODULES := libhardware_legacy
-
LOCAL_CFLAGS += -Wall -Werror -Wextra -Wno-unused-parameter -Wno-unused-function \
-Wunused-variable -Winit-self -Wwrite-strings -Wshadow
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
- $(call include-path-for, libhardware)/hardware \
- $(call include-path-for, libhardware_legacy)/hardware_legacy \
libcore/include
LOCAL_SHARED_LIBRARIES += \
@@ -96,19 +32,15 @@
libnativehelper \
libcutils \
libutils \
- libhardware \
- libhardware_legacy \
- libnl \
- libdl
-
-LOCAL_STATIC_LIBRARIES += libwifi-hal-stub
-LOCAL_STATIC_LIBRARIES += $(LIB_WIFI_HAL)
+ libdl \
+ libwifi-hal \
+ libwifi-system
LOCAL_SRC_FILES := \
jni/com_android_server_wifi_WifiNative.cpp \
jni/jni_helper.cpp
-ifdef INCLUDE_NAN_FEATURE
+ifeq ($(BOARD_HAS_NAN), true)
LOCAL_SRC_FILES += \
jni/com_android_server_wifi_nan_WifiNanNative.cpp
endif
@@ -120,15 +52,19 @@
# Build the java code
# ============================================================
+wificond_aidl_path := system/connectivity/wificond/aidl
+wificond_aidl_rel_path := ../../../../../$(wificond_aidl_path)
+
include $(CLEAR_VARS)
-LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/java
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/java $(wificond_aidl_path)
LOCAL_SRC_FILES := $(call all-java-files-under, java) \
$(call all-Iaidl-files-under, java) \
+ $(call all-Iaidl-files-under, $(wificond_aidl_rel_path)) \
$(call all-logtags-files-under, java) \
$(call all-proto-files-under, proto)
-ifndef INCLUDE_NAN_FEATURE
+ifneq ($(BOARD_HAS_NAN), true)
LOCAL_SRC_FILES := $(filter-out $(call all-java-files-under, \
java/com/android/server/wifi/nan),$(LOCAL_SRC_FILES))
endif
diff --git a/service/java/com/android/server/wifi/Clock.java b/service/java/com/android/server/wifi/Clock.java
index df0df6d..bb2f21e 100644
--- a/service/java/com/android/server/wifi/Clock.java
+++ b/service/java/com/android/server/wifi/Clock.java
@@ -27,7 +27,7 @@
*
* @return Current time in milliseconds.
*/
- public long currentTimeMillis() {
+ public long getWallClockMillis() {
return System.currentTimeMillis();
}
@@ -36,26 +36,26 @@
*
* @return Current time since boot in milliseconds.
*/
- public long elapsedRealtime() {
+ public long getElapsedSinceBootMillis() {
return SystemClock.elapsedRealtime();
}
- /**
- * Returns the current timestamp of the most precise timer available on the local system, in
- * nanoseconds.
- *
- * @return Current time in nanoseconds.
- */
- public long nanoTime() {
- return System.nanoTime();
- }
-
- /**
+ /**
* Returns nanoseconds since boot, including time spent in sleep.
*
* @return Current time since boot in nanoseconds.
*/
- public long elapsedRealtimeNanos() {
+ public long getElapsedSinceBootNanos() {
return SystemClock.elapsedRealtimeNanos();
}
+
+ /**
+ * Returns milliseconds since boot, not counting time spent in deep sleep.
+ *
+ * @return Milliseconds of non-sleep uptime since boot.
+ */
+ public long getUptimeSinceBootMillis() {
+ return SystemClock.uptimeMillis();
+ }
+
}
diff --git a/service/java/com/android/server/wifi/FrameworkFacade.java b/service/java/com/android/server/wifi/FrameworkFacade.java
index cb03f7a..b273731 100644
--- a/service/java/com/android/server/wifi/FrameworkFacade.java
+++ b/service/java/com/android/server/wifi/FrameworkFacade.java
@@ -20,14 +20,13 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.net.ConnectivityManager;
+import android.content.pm.PackageManager;
import android.net.TrafficStats;
import android.net.ip.IpManager;
import android.net.wifi.IWifiScanner;
import android.net.wifi.WifiScanner;
import android.os.Handler;
import android.os.IBinder;
-import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -36,8 +35,6 @@
import android.security.KeyStore;
import android.telephony.CarrierConfigManager;
-import java.util.ArrayList;
-
/**
* This class allows overriding objects with mocks to write unit tests
*/
@@ -128,28 +125,6 @@
}
/**
- * Create a SoftApManager.
- * @param context current context
- * @param looper current thread looper
- * @param wifiNative reference to WifiNative
- * @param nmService reference to NetworkManagementService
- * @param cm reference to ConnectivityManager
- * @param countryCode Country code
- * @param allowed2GChannels list of allowed 2G channels
- * @param listener listener for SoftApManager
- * @return an instance of SoftApManager
- */
- public SoftApManager makeSoftApManager(
- Context context, Looper looper, WifiNative wifiNative,
- INetworkManagementService nmService, ConnectivityManager cm,
- String countryCode, ArrayList<Integer> allowed2GChannels,
- SoftApManager.Listener listener) {
- return new SoftApManager(
- looper, wifiNative, nmService, countryCode,
- allowed2GChannels, listener);
- }
-
- /**
* Checks whether the given uid has been granted the given permission.
* @param permName the permission to check
* @param uid The uid to check
diff --git a/service/java/com/android/server/wifi/NetworkUpdateResult.java b/service/java/com/android/server/wifi/NetworkUpdateResult.java
index 63cc33f..22c8e22 100644
--- a/service/java/com/android/server/wifi/NetworkUpdateResult.java
+++ b/service/java/com/android/server/wifi/NetworkUpdateResult.java
@@ -67,4 +67,9 @@
public void setIsNewNetwork(boolean isNew) {
isNewNetwork = isNew;
}
+
+ public boolean isSuccess() {
+ return netId != INVALID_NETWORK_ID;
+ }
+
}
diff --git a/service/java/com/android/server/wifi/README.txt b/service/java/com/android/server/wifi/README.txt
index 0d74da1..2ab4d8b 100644
--- a/service/java/com/android/server/wifi/README.txt
+++ b/service/java/com/android/server/wifi/README.txt
@@ -1,10 +1,26 @@
-This code has moved from
+Path history for this code:
-frameworks/base/services/java/com/android/server/wifi: gitk <SHA1 to be filled in later>
+commit date: 2013-12-18 to 2014-01-07
+commit hash: a07c419913bfae2a896fbc29e8f269ee08c4d910 (add)
+commit hash: 4a3f9cf099bbbe52dc0edb2a7e1d1c976bc335a3 (delete)
+dst: frameworks/opt/net/wifi/service
+src: frameworks/base/services/core/java/com/android/server/wifi
-Prior to that it was at
+commit date: 2013-12-19
+commit hash: 9158825f9c41869689d6b1786d7c7aa8bdd524ce (many more files)
+commit hash: 19c662b3df3b35756a92282bb6cc767e6407cb8a (a few files)
+dst: frameworks/base/services/core/java/com/android/server/wifi
+src: frameworks/base/services/java/com/android/server/wifi
-frameworks/base/wifi/java/android/net/wifi: gitk ffadfb9ffdced62db215319d3edc7717802088fb
+commit date: 2013-12-11
+commit hash: ffadfb9ffdced62db215319d3edc7717802088fb
+dst: frameworks/base/services/java/com/android/server/wifi
+src: frameworks/base/wifi/java/android/net/wifi
+
+commit date: 2008-10-21
+commit hash: 54b6cfa9a9e5b861a9930af873580d6dc20f773c
+dst: frameworks/base/wifi/java/android/net/wifi
+src: initial aosp import?
////////////////////////////////////////////////////////////////
diff --git a/service/java/com/android/server/wifi/ScanDetail.java b/service/java/com/android/server/wifi/ScanDetail.java
index dc87a5b..23e52db 100644
--- a/service/java/com/android/server/wifi/ScanDetail.java
+++ b/service/java/com/android/server/wifi/ScanDetail.java
@@ -83,40 +83,9 @@
mScanResult = scanResult;
mNetworkDetail = networkDetail;
mMatches = matches;
- mSeen = mScanResult.seen;
- }
-
- /**
- * Update the data stored in the scan result with the provided information.
- *
- * @param networkDetail NetworkDetail
- * @param level int
- * @param wssid WifiSsid
- * @param ssid String
- * @param flags String
- * @param freq int
- * @param tsf long
- */
- public void updateResults(NetworkDetail networkDetail, int level, WifiSsid wssid, String ssid,
- String flags, int freq, long tsf) {
- mScanResult.level = level;
- mScanResult.wifiSsid = wssid;
- // Keep existing API
- mScanResult.SSID = ssid;
- mScanResult.capabilities = flags;
- mScanResult.frequency = freq;
- mScanResult.timestamp = tsf;
- mSeen = System.currentTimeMillis();
- //mScanResult.seen = mSeen;
- mScanResult.channelWidth = networkDetail.getChannelWidth();
- mScanResult.centerFreq0 = networkDetail.getCenterfreq0();
- mScanResult.centerFreq1 = networkDetail.getCenterfreq1();
- if (networkDetail.is80211McResponderSupport()) {
- mScanResult.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
- }
- if (networkDetail.isInterworking()) {
- mScanResult.setFlag(ScanResult.FLAG_PASSPOINT_NETWORK);
- }
+ // Only inherit |mScanResult.seen| if it was previously set. This ensures that |mSeen|
+ // will always contain a valid timestamp.
+ mSeen = (mScanResult.seen == 0) ? System.currentTimeMillis() : mScanResult.seen;
}
/**
diff --git a/service/java/com/android/server/wifi/ScanDetailCache.java b/service/java/com/android/server/wifi/ScanDetailCache.java
index cb44e2a..ede9a6e 100644
--- a/service/java/com/android/server/wifi/ScanDetailCache.java
+++ b/service/java/com/android/server/wifi/ScanDetailCache.java
@@ -29,8 +29,8 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.Iterator;
-import java.util.concurrent.ConcurrentHashMap;
/**
* Maps BSSIDs to their individual ScanDetails for a given WifiConfiguration.
@@ -40,14 +40,29 @@
private static final String TAG = "ScanDetailCache";
private static final boolean DBG = false;
- private WifiConfiguration mConfig;
- private ConcurrentHashMap<String, ScanDetail> mMap;
- private ConcurrentHashMap<String, PasspointMatchInfo> mPasspointMatches;
+ private final WifiConfiguration mConfig;
+ private final int mMaxSize;
+ private final int mTrimSize;
+ private final HashMap<String, ScanDetail> mMap;
+ private final HashMap<String, PasspointMatchInfo> mPasspointMatches;
- ScanDetailCache(WifiConfiguration config) {
+ /**
+ * Scan Detail cache associated with each configured network.
+ *
+ * The cache size is trimmed down to |trimSize| once it crosses the provided |maxSize|.
+ * Since this operation is relatively expensive, ensure that |maxSize| and |trimSize| are not
+ * too close to each other. |trimSize| should always be <= |maxSize|.
+ *
+ * @param config WifiConfiguration object corresponding to the network.
+ * @param maxSize Max size desired for the cache.
+ * @param trimSize Size to trim the cache down to once it reaches |maxSize|.
+ */
+ ScanDetailCache(WifiConfiguration config, int maxSize, int trimSize) {
mConfig = config;
- mMap = new ConcurrentHashMap(16, 0.75f, 2);
- mPasspointMatches = new ConcurrentHashMap(16, 0.75f, 2);
+ mMaxSize = maxSize;
+ mTrimSize = trimSize;
+ mMap = new HashMap(16, 0.75f);
+ mPasspointMatches = new HashMap(16, 0.75f);
}
void put(ScanDetail scanDetail) {
@@ -55,6 +70,10 @@
}
void put(ScanDetail scanDetail, PasspointMatch match, HomeSP homeSp) {
+ // First check if we have reached |maxSize|. if yes, trim it down to |trimSize|.
+ if (mMap.size() >= mMaxSize) {
+ trim();
+ }
mMap.put(scanDetail.getBSSIDString(), scanDetail);
@@ -75,6 +94,7 @@
void remove(String bssid) {
mMap.remove(bssid);
+ mPasspointMatches.remove(bssid);
}
int size() {
@@ -94,13 +114,12 @@
}
/**
- * Method to reduce the cache to the given size by removing the oldest entries.
- *
- * @param num int target cache size
+ * Method to reduce the cache to |mTrimSize| size by removing the oldest entries.
+ * TODO: Investigate if this method can be further optimized.
*/
- public void trim(int num) {
+ private void trim() {
int currentSize = mMap.size();
- if (currentSize <= num) {
+ if (currentSize < mTrimSize) {
return; // Nothing to trim
}
ArrayList<ScanDetail> list = new ArrayList<ScanDetail>(mMap.values());
@@ -120,7 +139,7 @@
}
});
}
- for (int i = 0; i < currentSize - num; i++) {
+ for (int i = 0; i < currentSize - mTrimSize; i++) {
// Remove oldest results from scan cache
ScanDetail result = list.get(i);
mMap.remove(result.getBSSIDString());
@@ -288,7 +307,6 @@
}
-
@Override
public String toString() {
StringBuilder sbuf = new StringBuilder();
diff --git a/service/java/com/android/server/wifi/SoftApManager.java b/service/java/com/android/server/wifi/SoftApManager.java
index 2dfb754..266234b 100644
--- a/service/java/com/android/server/wifi/SoftApManager.java
+++ b/service/java/com/android/server/wifi/SoftApManager.java
@@ -20,13 +20,14 @@
import static com.android.server.wifi.util.ApConfigUtil.ERROR_NO_CHANNEL;
import static com.android.server.wifi.util.ApConfigUtil.SUCCESS;
-import android.content.Context;
-import android.net.ConnectivityManager;
+import android.net.wifi.IApInterface;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
+import android.os.IBinder.DeathRecipient;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
import android.util.Log;
import com.android.internal.util.State;
@@ -72,8 +73,9 @@
INetworkManagementService nmService,
String countryCode,
ArrayList<Integer> allowed2GChannels,
- Listener listener) {
- mStateMachine = new SoftApStateMachine(looper);
+ Listener listener,
+ IApInterface apInterface) {
+ mStateMachine = new SoftApStateMachine(looper, apInterface);
mNmService = nmService;
mWifiNative = wifiNative;
@@ -174,15 +176,29 @@
/* Commands for the state machine. */
public static final int CMD_START = 0;
public static final int CMD_STOP = 1;
+ public static final int CMD_AP_INTERFACE_BINDER_DEATH = 2;
+
+ private final IApInterface mApInterface;
private final State mIdleState = new IdleState();
private final State mStartedState = new StartedState();
- SoftApStateMachine(Looper looper) {
+ private ApInterfaceDeathRecipient mDeathRecipient;
+
+ private class ApInterfaceDeathRecipient implements DeathRecipient {
+ @Override
+ public void binderDied() {
+ SoftApStateMachine.this.sendMessage(CMD_AP_INTERFACE_BINDER_DEATH);
+ }
+ }
+
+
+ SoftApStateMachine(Looper looper, IApInterface apInterface) {
super(TAG, looper);
+ mApInterface = apInterface;
addState(mIdleState);
- addState(mStartedState, mIdleState);
+ addState(mStartedState);
setInitialState(mIdleState);
start();
@@ -190,11 +206,31 @@
private class IdleState extends State {
@Override
+ public void enter() {
+ if (mDeathRecipient != null) {
+ mApInterface.asBinder().unlinkToDeath(mDeathRecipient, 0);
+ mDeathRecipient = null;
+ }
+ }
+
+ @Override
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_START:
+ int result = SUCCESS;
updateApState(WifiManager.WIFI_AP_STATE_ENABLING, 0);
- int result = startSoftAp((WifiConfiguration) message.obj);
+ mDeathRecipient = new ApInterfaceDeathRecipient();
+ try {
+ mApInterface.asBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ // The remote has already died.
+ result = ERROR_GENERIC;
+ break;
+ }
+
+ if (result == SUCCESS) {
+ result = startSoftAp((WifiConfiguration) message.obj);
+ }
if (result == SUCCESS) {
updateApState(WifiManager.WIFI_AP_STATE_ENABLED, 0);
transitionTo(mStartedState);
@@ -204,12 +240,17 @@
reason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;
}
updateApState(WifiManager.WIFI_AP_STATE_FAILED, reason);
+ if (mDeathRecipient != null) {
+ mApInterface.asBinder().unlinkToDeath(mDeathRecipient, 0);
+ mDeathRecipient = null;
+ }
}
break;
default:
/* Ignore all other commands. */
break;
}
+
return HANDLED;
}
}
@@ -221,10 +262,16 @@
case CMD_START:
/* Already started, ignore this command. */
break;
+ case CMD_AP_INTERFACE_BINDER_DEATH:
case CMD_STOP:
updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 0);
stopSoftAp();
- updateApState(WifiManager.WIFI_AP_STATE_DISABLED, 0);
+ if (message.what == CMD_AP_INTERFACE_BINDER_DEATH) {
+ updateApState(WifiManager.WIFI_AP_STATE_FAILED,
+ WifiManager.SAP_START_FAILURE_GENERAL);
+ } else {
+ updateApState(WifiManager.WIFI_AP_STATE_DISABLED, 0);
+ }
transitionTo(mIdleState);
break;
default:
diff --git a/service/java/com/android/server/wifi/WifiBackupRestore.java b/service/java/com/android/server/wifi/WifiBackupRestore.java
new file mode 100644
index 0000000..c83e063
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiBackupRestore.java
@@ -0,0 +1,749 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi;
+
+import android.net.IpConfiguration;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+import com.android.server.net.IpConfigStore;
+import com.android.server.wifi.util.XmlUtil;
+import com.android.server.wifi.util.XmlUtil.IpConfigurationXmlUtil;
+import com.android.server.wifi.util.XmlUtil.WifiConfigurationXmlUtil;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.CharArrayReader;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Class used to backup/restore data using the SettingsBackupAgent.
+ * There are 2 symmetric API's exposed here:
+ * 1. retrieveBackupDataFromConfigurations: Retrieve the configuration data to be backed up.
+ * 2. retrieveConfigurationsFromBackupData: Restore the configuration using the provided data.
+ * The byte stream to be backed up is XML encoded and versioned to migrate the data easily across
+ * revisions.
+ */
+public class WifiBackupRestore {
+ private static final String TAG = "WifiBackupRestore";
+
+ /**
+ * Current backup data version. This will be incremented for any additions.
+ */
+ private static final int CURRENT_BACKUP_DATA_VERSION = 1;
+
+ /** This list of older versions will be used to restore data from older backups. */
+ /**
+ * First version of the backup data format.
+ */
+ private static final int INITIAL_BACKUP_DATA_VERSION = 1;
+
+ /**
+ * List of XML section header tags in the backed up data
+ */
+ private static final String XML_TAG_DOCUMENT_HEADER = "WifiBackupData";
+ private static final String XML_TAG_VERSION = "Version";
+ private static final String XML_TAG_SECTION_HEADER_NETWORK_LIST = "NetworkList";
+ private static final String XML_TAG_SECTION_HEADER_NETWORK = "Network";
+ private static final String XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION = "WifiConfiguration";
+ private static final String XML_TAG_SECTION_HEADER_IP_CONFIGURATION = "IpConfiguration";
+
+ /**
+ * Regex to mask out passwords in backup data dump.
+ */
+ private static final String PSK_MASK_LINE_MATCH_PATTERN =
+ "<.*" + WifiConfigurationXmlUtil.XML_TAG_PRE_SHARED_KEY + ".*>.*<.*>";
+ private static final String PSK_MASK_SEARCH_PATTERN =
+ "(<.*" + WifiConfigurationXmlUtil.XML_TAG_PRE_SHARED_KEY + ".*>)(.*)(<.*>)";
+ private static final String PSK_MASK_REPLACE_PATTERN = "$1*$3";
+
+ private static final String WEP_KEYS_MASK_LINE_START_MATCH_PATTERN =
+ "<string-array.*" + WifiConfigurationXmlUtil.XML_TAG_WEP_KEYS + ".*num=\"[0-9]\">";
+ private static final String WEP_KEYS_MASK_LINE_END_MATCH_PATTERN = "</string-array>";
+ private static final String WEP_KEYS_MASK_SEARCH_PATTERN = "(<.*=)(.*)(/>)";
+ private static final String WEP_KEYS_MASK_REPLACE_PATTERN = "$1*$3";
+
+ /**
+ * Verbose logging flag.
+ */
+ private boolean mVerboseLoggingEnabled = false;
+
+ /**
+ * Store the dump of the backup/restore data for debugging. This is only stored when verbose
+ * logging is enabled in developer options.
+ */
+ private byte[] mDebugLastBackupDataRetrieved;
+ private byte[] mDebugLastBackupDataRestored;
+ private byte[] mDebugLastSupplicantBackupDataRestored;
+
+ /**
+ * Retrieve an XML byte stream representing the data that needs to be backed up from the
+ * provided configurations.
+ *
+ * @param configurations list of currently saved networks that needs to be backed up.
+ * @return Raw byte stream of XML that needs to be backed up.
+ */
+ public byte[] retrieveBackupDataFromConfigurations(List<WifiConfiguration> configurations) {
+ if (configurations == null) {
+ Log.e(TAG, "Invalid configuration list received");
+ return null;
+ }
+
+ try {
+ final XmlSerializer out = new FastXmlSerializer();
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+
+ // Start writing the XML stream.
+ XmlUtil.writeDocumentStart(out, XML_TAG_DOCUMENT_HEADER);
+
+ XmlUtil.writeNextValue(out, XML_TAG_VERSION, CURRENT_BACKUP_DATA_VERSION);
+
+ writeNetworkConfigurationsToXml(out, configurations);
+
+ XmlUtil.writeDocumentEnd(out, XML_TAG_DOCUMENT_HEADER);
+
+ byte[] data = outputStream.toByteArray();
+
+ if (mVerboseLoggingEnabled) {
+ mDebugLastBackupDataRetrieved = data;
+ }
+
+ return data;
+ } catch (XmlPullParserException e) {
+ Log.e(TAG, "Error retrieving the backup data: " + e);
+ } catch (IOException e) {
+ Log.e(TAG, "Error retrieving the backup data: " + e);
+ }
+ return null;
+ }
+
+ /**
+ * Write the list of configurations to the XML stream.
+ */
+ private void writeNetworkConfigurationsToXml(
+ XmlSerializer out, List<WifiConfiguration> configurations)
+ throws XmlPullParserException, IOException {
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_NETWORK_LIST);
+ for (WifiConfiguration configuration : configurations) {
+ // We don't want to backup/restore enterprise/passpoint configurations.
+ if (configuration.isEnterprise() || configuration.isPasspoint()) {
+ Log.d(TAG, "Skipping enterprise network for backup: " + configuration.configKey());
+ continue;
+ }
+ // Write this configuration data now.
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_NETWORK);
+ writeNetworkConfigurationToXml(out, configuration);
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_NETWORK);
+ }
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_NETWORK_LIST);
+ }
+
+ /**
+ * Write the configuration data elements from the provided Configuration to the XML stream.
+ * Uses XmlUtils to write the values of each element.
+ */
+ private void writeNetworkConfigurationToXml(XmlSerializer out, WifiConfiguration configuration)
+ throws XmlPullParserException, IOException {
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION);
+ WifiConfigurationXmlUtil.writeToXmlForBackup(out, configuration);
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION);
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_IP_CONFIGURATION);
+ IpConfigurationXmlUtil.writeToXml(out, configuration.getIpConfiguration());
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_IP_CONFIGURATION);
+ }
+
+ /**
+ * Parse out the configurations from the back up data.
+ *
+ * @param data raw byte stream representing the XML data.
+ * @return list of networks retrieved from the backed up data.
+ */
+ public List<WifiConfiguration> retrieveConfigurationsFromBackupData(byte[] data) {
+ if (data == null || data.length == 0) {
+ Log.e(TAG, "Invalid backup data received");
+ return null;
+ }
+
+ try {
+ if (mVerboseLoggingEnabled) {
+ mDebugLastBackupDataRestored = data;
+ }
+
+ final XmlPullParser in = Xml.newPullParser();
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
+ in.setInput(inputStream, StandardCharsets.UTF_8.name());
+
+ // Start parsing the XML stream.
+ XmlUtil.gotoDocumentStart(in, XML_TAG_DOCUMENT_HEADER);
+ int rootTagDepth = in.getDepth();
+
+ int version = (int) XmlUtil.readNextValueWithName(in, XML_TAG_VERSION);
+ if (version < INITIAL_BACKUP_DATA_VERSION || version > CURRENT_BACKUP_DATA_VERSION) {
+ Log.e(TAG, "Invalid version of data: " + version);
+ return null;
+ }
+
+ return parseNetworkConfigurationsFromXml(in, rootTagDepth, version);
+ } catch (XmlPullParserException e) {
+ Log.e(TAG, "Error parsing the backup data: " + e);
+ } catch (IOException e) {
+ Log.e(TAG, "Error parsing the backup data: " + e);
+ }
+ return null;
+ }
+
+ /**
+ * Parses the list of configurations from the provided XML stream.
+ *
+ * @param in XmlPullParser instance pointing to the XML stream.
+ * @param outerTagDepth depth of the outer tag in the XML document.
+ * @param dataVersion version number parsed from incoming data.
+ * @return List<WifiConfiguration> object if parsing is successful, null otherwise.
+ */
+ private List<WifiConfiguration> parseNetworkConfigurationsFromXml(
+ XmlPullParser in, int outerTagDepth, int dataVersion)
+ throws XmlPullParserException, IOException {
+ // Find the configuration list section.
+ XmlUtil.gotoNextSectionWithName(in, XML_TAG_SECTION_HEADER_NETWORK_LIST, outerTagDepth);
+ // Find all the configurations within the configuration list section.
+ int networkListTagDepth = outerTagDepth + 1;
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ while (XmlUtil.gotoNextSectionWithNameOrEnd(
+ in, XML_TAG_SECTION_HEADER_NETWORK, networkListTagDepth)) {
+ WifiConfiguration configuration =
+ parseNetworkConfigurationFromXml(in, dataVersion, networkListTagDepth);
+ if (configuration != null) {
+ Log.v(TAG, "Parsed Configuration: " + configuration.configKey());
+ configurations.add(configuration);
+ }
+ }
+ return configurations;
+ }
+
+ /**
+ * Helper method to parse the WifiConfiguration object and validate the configKey parsed.
+ */
+ private WifiConfiguration parseWifiConfigurationFromXmlAndValidateConfigKey(
+ XmlPullParser in, int outerTagDepth)
+ throws XmlPullParserException, IOException {
+ Pair<String, WifiConfiguration> parsedConfig =
+ WifiConfigurationXmlUtil.parseFromXml(in, outerTagDepth);
+ if (parsedConfig == null || parsedConfig.first == null || parsedConfig.second == null) {
+ return null;
+ }
+ String configKeyParsed = parsedConfig.first;
+ WifiConfiguration configuration = parsedConfig.second;
+ String configKeyCalculated = configuration.configKey();
+ if (!configKeyParsed.equals(configKeyCalculated)) {
+ String configKeyMismatchLog =
+ "Configuration key does not match. Retrieved: " + configKeyParsed
+ + ", Calculated: " + configKeyCalculated;
+ if (configuration.shared) {
+ Log.e(TAG, configKeyMismatchLog);
+ return null;
+ } else {
+ // ConfigKey mismatches are expected for private networks because the
+ // UID is not preserved across backup/restore.
+ Log.w(TAG, configKeyMismatchLog);
+ }
+ }
+ return configuration;
+ }
+
+ /**
+ * Parses the configuration data elements from the provided XML stream to a Configuration.
+ *
+ * @param in XmlPullParser instance pointing to the XML stream.
+ * @param outerTagDepth depth of the outer tag in the XML document.
+ * @param dataVersion version number parsed from incoming data.
+ * @return WifiConfiguration object if parsing is successful, null otherwise.
+ */
+ private WifiConfiguration parseNetworkConfigurationFromXml(XmlPullParser in, int dataVersion,
+ int outerTagDepth)
+ throws XmlPullParserException, IOException {
+ // Any version migration needs to be handled here in future.
+ if (dataVersion == INITIAL_BACKUP_DATA_VERSION) {
+ WifiConfiguration configuration = null;
+ int networkTagDepth = outerTagDepth + 1;
+ // Retrieve WifiConfiguration object first.
+ XmlUtil.gotoNextSectionWithName(
+ in, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION, networkTagDepth);
+ int configTagDepth = networkTagDepth + 1;
+ configuration = parseWifiConfigurationFromXmlAndValidateConfigKey(in, configTagDepth);
+ if (configuration == null) {
+ return null;
+ }
+ // Now retrieve any IP configuration info.
+ XmlUtil.gotoNextSectionWithName(
+ in, XML_TAG_SECTION_HEADER_IP_CONFIGURATION, networkTagDepth);
+ IpConfiguration ipConfiguration =
+ IpConfigurationXmlUtil.parseFromXml(in, configTagDepth);
+ configuration.setIpConfiguration(ipConfiguration);
+ return configuration;
+ }
+ return null;
+ }
+
+ /**
+ * Create log dump of the backup data in XML format with the preShared & WEP key masked.
+ *
+ * PSK keys are written in the following format in XML:
+ * <string name="PreSharedKey">WifiBackupRestorePsk</string>
+ *
+ * WEP Keys are written in following format in XML:
+ * <string-array name="WEPKeys" num="4">
+ * <item value="WifiBackupRestoreWep1" />
+ * <item value="WifiBackupRestoreWep2" />
+ * <item value="WifiBackupRestoreWep3" />
+ * <item value="WifiBackupRestoreWep3" />
+ * </string-array>
+ */
+ private String createLogFromBackupData(byte[] data) {
+ StringBuilder sb = new StringBuilder();
+ try {
+ String xmlString = new String(data, StandardCharsets.UTF_8.name());
+ boolean wepKeysLine = false;
+ for (String line : xmlString.split("\n")) {
+ if (line.matches(PSK_MASK_LINE_MATCH_PATTERN)) {
+ line = line.replaceAll(PSK_MASK_SEARCH_PATTERN, PSK_MASK_REPLACE_PATTERN);
+ }
+ if (line.matches(WEP_KEYS_MASK_LINE_START_MATCH_PATTERN)) {
+ wepKeysLine = true;
+ } else if (line.matches(WEP_KEYS_MASK_LINE_END_MATCH_PATTERN)) {
+ wepKeysLine = false;
+ } else if (wepKeysLine) {
+ line = line.replaceAll(
+ WEP_KEYS_MASK_SEARCH_PATTERN, WEP_KEYS_MASK_REPLACE_PATTERN);
+ }
+ sb.append(line).append("\n");
+ }
+ } catch (UnsupportedEncodingException e) {
+ return "";
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Restore state from the older supplicant back up data.
+ * The old backup data was essentially a backup of wpa_supplicant.conf & ipconfig.txt file.
+ *
+ * @param supplicantData Raw byte stream of wpa_supplicant.conf
+ * @param ipConfigData Raw byte stream of ipconfig.txt
+ * @return list of networks retrieved from the backed up data.
+ */
+ public List<WifiConfiguration> retrieveConfigurationsFromSupplicantBackupData(
+ byte[] supplicantData, byte[] ipConfigData) {
+ if (supplicantData == null || supplicantData.length == 0) {
+ Log.e(TAG, "Invalid supplicant backup data received");
+ return null;
+ }
+
+ if (mVerboseLoggingEnabled) {
+ mDebugLastSupplicantBackupDataRestored = supplicantData;
+ }
+
+ SupplicantBackupMigration.SupplicantNetworks supplicantNetworks =
+ new SupplicantBackupMigration.SupplicantNetworks();
+ // Incorporate the networks present in the backup data.
+ char[] restoredAsChars = new char[supplicantData.length];
+ for (int i = 0; i < supplicantData.length; i++) {
+ restoredAsChars[i] = (char) supplicantData[i];
+ }
+
+ BufferedReader in = new BufferedReader(new CharArrayReader(restoredAsChars));
+ supplicantNetworks.readNetworksFromStream(in);
+
+ // Retrieve corresponding WifiConfiguration objects.
+ List<WifiConfiguration> configurations = supplicantNetworks.retrieveWifiConfigurations();
+
+ // Now retrieve all the IpConfiguration objects and set in the corresponding
+ // WifiConfiguration objects if ipconfig data is present.
+ if (ipConfigData != null && ipConfigData.length != 0) {
+ SparseArray<IpConfiguration> networks =
+ IpConfigStore.readIpAndProxyConfigurations(
+ new ByteArrayInputStream(ipConfigData));
+ if (networks != null) {
+ for (int i = 0; i < networks.size(); i++) {
+ int id = networks.keyAt(i);
+ for (WifiConfiguration configuration : configurations) {
+ // This is a dangerous lookup, but that's how it is currently written.
+ if (configuration.configKey().hashCode() == id) {
+ configuration.setIpConfiguration(networks.valueAt(i));
+ }
+ }
+ }
+ } else {
+ Log.e(TAG, "Failed to parse ipconfig data");
+ }
+ } else {
+ Log.e(TAG, "Invalid ipconfig backup data received");
+ }
+ return configurations;
+ }
+
+ /**
+ * Enable verbose logging.
+ *
+ * @param verbose verbosity level.
+ */
+ public void enableVerboseLogging(int verbose) {
+ mVerboseLoggingEnabled = (verbose > 0);
+ if (!mVerboseLoggingEnabled) {
+ mDebugLastBackupDataRetrieved = null;
+ mDebugLastBackupDataRestored = null;
+ mDebugLastSupplicantBackupDataRestored = null;
+ }
+ }
+
+ /**
+ * Dump out the last backup/restore data if verbose logging is enabled.
+ *
+ * @param fd unused
+ * @param pw PrintWriter for writing dump to
+ * @param args unused
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("Dump of WifiBackupRestore");
+ if (mDebugLastBackupDataRetrieved != null) {
+ pw.println("Last backup data retrieved: "
+ + createLogFromBackupData(mDebugLastBackupDataRetrieved));
+ }
+ if (mDebugLastBackupDataRestored != null) {
+ pw.println("Last backup data restored: "
+ + createLogFromBackupData(mDebugLastBackupDataRestored));
+ }
+ if (mDebugLastSupplicantBackupDataRestored != null) {
+ pw.println("Last old backup data restored: "
+ + SupplicantBackupMigration.createLogFromBackupData(
+ mDebugLastSupplicantBackupDataRestored));
+ }
+ }
+
+ /**
+ * These sub classes contain the logic to parse older backups and restore wifi state from it.
+ * Most of the code here has been migrated over from BackupSettingsAgent.
+ * This is kind of ugly text parsing, but it is needed to support the migration of this data.
+ */
+ public static class SupplicantBackupMigration {
+ /**
+ * List of keys to look out for in wpa_supplicant.conf parsing.
+ * These key values are declared in different parts of the wifi codebase today.
+ */
+ public static final String SUPPLICANT_KEY_SSID = WifiConfiguration.ssidVarName;
+ public static final String SUPPLICANT_KEY_HIDDEN = WifiConfiguration.hiddenSSIDVarName;
+ public static final String SUPPLICANT_KEY_KEY_MGMT = WifiConfiguration.KeyMgmt.varName;
+ public static final String SUPPLICANT_KEY_CLIENT_CERT =
+ WifiEnterpriseConfig.CLIENT_CERT_KEY;
+ public static final String SUPPLICANT_KEY_CA_CERT = WifiEnterpriseConfig.CA_CERT_KEY;
+ public static final String SUPPLICANT_KEY_CA_PATH = WifiEnterpriseConfig.CA_PATH_KEY;
+ public static final String SUPPLICANT_KEY_EAP = WifiEnterpriseConfig.EAP_KEY;
+ public static final String SUPPLICANT_KEY_PSK = WifiConfiguration.pskVarName;
+ public static final String SUPPLICANT_KEY_WEP_KEY0 = WifiConfiguration.wepKeyVarNames[0];
+ public static final String SUPPLICANT_KEY_WEP_KEY1 = WifiConfiguration.wepKeyVarNames[1];
+ public static final String SUPPLICANT_KEY_WEP_KEY2 = WifiConfiguration.wepKeyVarNames[2];
+ public static final String SUPPLICANT_KEY_WEP_KEY3 = WifiConfiguration.wepKeyVarNames[3];
+ public static final String SUPPLICANT_KEY_WEP_KEY_IDX =
+ WifiConfiguration.wepTxKeyIdxVarName;
+ public static final String SUPPLICANT_KEY_ID_STR = WifiSupplicantControl.ID_STRING_VAR_NAME;
+
+ /**
+ * Regex to mask out passwords in backup data dump.
+ */
+ private static final String PSK_MASK_LINE_MATCH_PATTERN =
+ ".*" + SUPPLICANT_KEY_PSK + ".*=.*";
+ private static final String PSK_MASK_SEARCH_PATTERN =
+ "(.*" + SUPPLICANT_KEY_PSK + ".*=)(.*)";
+ private static final String PSK_MASK_REPLACE_PATTERN = "$1*";
+
+ private static final String WEP_KEYS_MASK_LINE_MATCH_PATTERN =
+ ".*" + SUPPLICANT_KEY_WEP_KEY0.replace("0", "") + ".*=.*";
+ private static final String WEP_KEYS_MASK_SEARCH_PATTERN =
+ "(.*" + SUPPLICANT_KEY_WEP_KEY0.replace("0", "") + ".*=)(.*)";
+ private static final String WEP_KEYS_MASK_REPLACE_PATTERN = "$1*";
+
+ /**
+ * Create log dump of the backup data in wpa_supplicant.conf format with the preShared &
+ * WEP key masked.
+ *
+ * PSK keys are written in the following format in wpa_supplicant.conf:
+ * psk=WifiBackupRestorePsk
+ *
+ * WEP Keys are written in following format in wpa_supplicant.conf:
+ * wep_keys0=WifiBackupRestoreWep0
+ * wep_keys1=WifiBackupRestoreWep1
+ * wep_keys2=WifiBackupRestoreWep2
+ * wep_keys3=WifiBackupRestoreWep3
+ */
+ public static String createLogFromBackupData(byte[] data) {
+ StringBuilder sb = new StringBuilder();
+ try {
+ String supplicantConfString = new String(data, StandardCharsets.UTF_8.name());
+ for (String line : supplicantConfString.split("\n")) {
+ if (line.matches(PSK_MASK_LINE_MATCH_PATTERN)) {
+ line = line.replaceAll(PSK_MASK_SEARCH_PATTERN, PSK_MASK_REPLACE_PATTERN);
+ }
+ if (line.matches(WEP_KEYS_MASK_LINE_MATCH_PATTERN)) {
+ line = line.replaceAll(
+ WEP_KEYS_MASK_SEARCH_PATTERN, WEP_KEYS_MASK_REPLACE_PATTERN);
+ }
+ sb.append(line).append("\n");
+ }
+ } catch (UnsupportedEncodingException e) {
+ return "";
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Class for capturing a network definition from the wifi supplicant config file.
+ */
+ static class SupplicantNetwork {
+ private String mParsedSSIDLine;
+ private String mParsedHiddenLine;
+ private String mParsedKeyMgmtLine;
+ private String mParsedPskLine;
+ private String[] mParsedWepKeyLines = new String[4];
+ private String mParsedWepTxKeyIdxLine;
+ private String mParsedIdStrLine;
+ public boolean certUsed = false;
+ public boolean isEap = false;
+
+ /**
+ * Read lines from wpa_supplicant.conf stream for this network.
+ */
+ public static SupplicantNetwork readNetworkFromStream(BufferedReader in) {
+ final SupplicantNetwork n = new SupplicantNetwork();
+ String line;
+ try {
+ while (in.ready()) {
+ line = in.readLine();
+ if (line == null || line.startsWith("}")) {
+ break;
+ }
+ n.parseLine(line);
+ }
+ } catch (IOException e) {
+ return null;
+ }
+ return n;
+ }
+
+ /**
+ * Parse a line from wpa_supplicant.conf stream for this network.
+ */
+ void parseLine(String line) {
+ // Can't rely on particular whitespace patterns so strip leading/trailing.
+ line = line.trim();
+ if (line.isEmpty()) return; // only whitespace; drop the line.
+
+ // Now parse the network block within wpa_supplicant.conf and store the important
+ // lines for procesing later.
+ if (line.startsWith(SUPPLICANT_KEY_SSID)) {
+ mParsedSSIDLine = line;
+ } else if (line.startsWith(SUPPLICANT_KEY_HIDDEN)) {
+ mParsedHiddenLine = line;
+ } else if (line.startsWith(SUPPLICANT_KEY_KEY_MGMT)) {
+ mParsedKeyMgmtLine = line;
+ if (line.contains("EAP")) {
+ isEap = true;
+ }
+ } else if (line.startsWith(SUPPLICANT_KEY_CLIENT_CERT)) {
+ certUsed = true;
+ } else if (line.startsWith(SUPPLICANT_KEY_CA_CERT)) {
+ certUsed = true;
+ } else if (line.startsWith(SUPPLICANT_KEY_CA_PATH)) {
+ certUsed = true;
+ } else if (line.startsWith(SUPPLICANT_KEY_EAP)) {
+ isEap = true;
+ } else if (line.startsWith(SUPPLICANT_KEY_PSK)) {
+ mParsedPskLine = line;
+ } else if (line.startsWith(SUPPLICANT_KEY_WEP_KEY0)) {
+ mParsedWepKeyLines[0] = line;
+ } else if (line.startsWith(SUPPLICANT_KEY_WEP_KEY1)) {
+ mParsedWepKeyLines[1] = line;
+ } else if (line.startsWith(SUPPLICANT_KEY_WEP_KEY2)) {
+ mParsedWepKeyLines[2] = line;
+ } else if (line.startsWith(SUPPLICANT_KEY_WEP_KEY3)) {
+ mParsedWepKeyLines[3] = line;
+ } else if (line.startsWith(SUPPLICANT_KEY_WEP_KEY_IDX)) {
+ mParsedWepTxKeyIdxLine = line;
+ } else if (line.startsWith(SUPPLICANT_KEY_ID_STR)) {
+ mParsedIdStrLine = line;
+ }
+ }
+
+ /**
+ * Create WifiConfiguration object from the parsed data for this network.
+ */
+ public WifiConfiguration createWifiConfiguration() {
+ if (mParsedSSIDLine == null) {
+ // No SSID => malformed network definition
+ return null;
+ }
+ WifiConfiguration configuration = new WifiConfiguration();
+ configuration.SSID = mParsedSSIDLine.substring(mParsedSSIDLine.indexOf('=') + 1);
+
+ if (mParsedHiddenLine != null) {
+ // Can't use Boolean.valueOf() because it works only for true/false.
+ configuration.hiddenSSID =
+ Integer.parseInt(mParsedHiddenLine.substring(
+ mParsedHiddenLine.indexOf('=') + 1)) != 0;
+ }
+ if (mParsedKeyMgmtLine == null) {
+ // no key_mgmt line specified; this is defined as equivalent to
+ // "WPA-PSK WPA-EAP".
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+ } else {
+ // Need to parse the mParsedKeyMgmtLine line
+ final String bareKeyMgmt =
+ mParsedKeyMgmtLine.substring(mParsedKeyMgmtLine.indexOf('=') + 1);
+ String[] typeStrings = bareKeyMgmt.split("\\s+");
+
+ // Parse out all the key management regimes permitted for this network.
+ // The literal strings here are the standard values permitted in
+ // wpa_supplicant.conf.
+ for (int i = 0; i < typeStrings.length; i++) {
+ final String ktype = typeStrings[i];
+ if (ktype.equals("NONE")) {
+ configuration.allowedKeyManagement.set(
+ WifiConfiguration.KeyMgmt.NONE);
+ } else if (ktype.equals("WPA-PSK")) {
+ configuration.allowedKeyManagement.set(
+ WifiConfiguration.KeyMgmt.WPA_PSK);
+ } else if (ktype.equals("WPA-EAP")) {
+ configuration.allowedKeyManagement.set(
+ WifiConfiguration.KeyMgmt.WPA_EAP);
+ } else if (ktype.equals("IEEE8021X")) {
+ configuration.allowedKeyManagement.set(
+ WifiConfiguration.KeyMgmt.IEEE8021X);
+ }
+ }
+ }
+ if (mParsedPskLine != null) {
+ configuration.preSharedKey =
+ mParsedPskLine.substring(mParsedPskLine.indexOf('=') + 1);
+ }
+ if (mParsedWepKeyLines[0] != null) {
+ configuration.wepKeys[0] =
+ mParsedWepKeyLines[0].substring(mParsedWepKeyLines[0].indexOf('=') + 1);
+ }
+ if (mParsedWepKeyLines[1] != null) {
+ configuration.wepKeys[1] =
+ mParsedWepKeyLines[1].substring(mParsedWepKeyLines[1].indexOf('=') + 1);
+ }
+ if (mParsedWepKeyLines[2] != null) {
+ configuration.wepKeys[2] =
+ mParsedWepKeyLines[2].substring(mParsedWepKeyLines[2].indexOf('=') + 1);
+ }
+ if (mParsedWepKeyLines[3] != null) {
+ configuration.wepKeys[3] =
+ mParsedWepKeyLines[3].substring(mParsedWepKeyLines[3].indexOf('=') + 1);
+ }
+ if (mParsedWepTxKeyIdxLine != null) {
+ configuration.wepTxKeyIndex =
+ Integer.valueOf(mParsedWepTxKeyIdxLine.substring(
+ mParsedWepTxKeyIdxLine.indexOf('=') + 1));
+ }
+ if (mParsedIdStrLine != null) {
+ String idString =
+ mParsedIdStrLine.substring(mParsedIdStrLine.indexOf('=') + 1);
+ Map<String, String> extras = WifiNative.parseNetworkExtra(idString);
+ String configKey = extras.get(WifiSupplicantControl.ID_STRING_KEY_CONFIG_KEY);
+ if (!configKey.equals(configuration.configKey())) {
+ // ConfigKey mismatches are expected for private networks because the
+ // UID is not preserved across backup/restore.
+ Log.w(TAG, "Configuration key does not match. Retrieved: " + configKey
+ + ", Calculated: " + configuration.configKey());
+ }
+ }
+ return configuration;
+ }
+ }
+
+ /**
+ * Ingest multiple wifi config fragments from wpa_supplicant.conf, looking for network={}
+ * blocks and eliminating duplicates
+ */
+ static class SupplicantNetworks {
+ final ArrayList<SupplicantNetwork> mNetworks = new ArrayList<>(8);
+
+ /**
+ * Parse the wpa_supplicant.conf file stream and add networks.
+ */
+ public void readNetworksFromStream(BufferedReader in) {
+ try {
+ String line;
+ while (in.ready()) {
+ line = in.readLine();
+ if (line != null) {
+ if (line.startsWith("network")) {
+ SupplicantNetwork net = SupplicantNetwork.readNetworkFromStream(in);
+ // Networks that use certificates for authentication can't be
+ // restored because the certificates they need don't get restored
+ // (because they are stored in keystore, and can't be restored).
+ // Similarly, omit EAP network definitions to avoid propagating
+ // controlled enterprise network definitions.
+ if (net.isEap || net.certUsed) {
+ Log.d(TAG, "Skipping enterprise network for restore: "
+ + net.mParsedSSIDLine + " / " + net.mParsedKeyMgmtLine);
+ continue;
+ }
+ mNetworks.add(net);
+ }
+ }
+ }
+ } catch (IOException e) {
+ // whatever happened, we're done now
+ }
+ }
+
+ /**
+ * Retrieve a list of WifiConfiguration objects parsed from wpa_supplicant.conf
+ */
+ public List<WifiConfiguration> retrieveWifiConfigurations() {
+ ArrayList<WifiConfiguration> wifiConfigurations = new ArrayList<>();
+ for (SupplicantNetwork net : mNetworks) {
+ WifiConfiguration wifiConfiguration = net.createWifiConfiguration();
+ if (wifiConfiguration != null) {
+ Log.v(TAG, "Parsed Configuration: " + wifiConfiguration.configKey());
+ wifiConfigurations.add(wifiConfiguration);
+ }
+ }
+ return wifiConfigurations;
+ }
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java
index c1a334a..1e288ef 100644
--- a/service/java/com/android/server/wifi/WifiConfigManager.java
+++ b/service/java/com/android/server/wifi/WifiConfigManager.java
@@ -45,7 +45,6 @@
import android.net.wifi.WpsResult;
import android.os.Environment;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -151,8 +150,6 @@
*
*/
public class WifiConfigManager {
- private static boolean sVDBG = false;
- private static boolean sVVDBG = false;
public static final String TAG = "WifiConfigManager";
public static final int MAX_TX_PACKET_FOR_FULL_SCANS = 8;
public static final int MAX_RX_PACKET_FOR_FULL_SCANS = 16;
@@ -185,6 +182,11 @@
private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
/**
+ * Maximum age of scan results that can be used for averaging out RSSI value.
+ */
+ private static final int SCAN_RESULT_MAXIMUM_AGE_MS = 40000;
+
+ /**
* The threshold for each kind of error. If a network continuously encounter the same error more
* than the threshold times, this network will be disabled. -1 means unavailable.
*/
@@ -195,10 +197,12 @@
5, // threshold for DISABLED_AUTHENTICATION_FAILURE
5, // threshold for DISABLED_DHCP_FAILURE
5, // threshold for DISABLED_DNS_FAILURE
+ 1, // threshold for DISABLED_WPS_START
6, // threshold for DISABLED_TLS_VERSION_MISMATCH
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_WIFI_MANAGER
+ 1 // threshold for DISABLED_BY_USER_SWITCH
};
/**
@@ -211,10 +215,12 @@
5, // threshold for DISABLED_AUTHENTICATION_FAILURE
5, // threshold for DISABLED_DHCP_FAILURE
5, // threshold for DISABLED_DNS_FAILURE
+ 0, // threshold for DISABLED_WPS_START
Integer.MAX_VALUE, // threshold for DISABLED_TLS_VERSION
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_WIFI_MANAGER
+ Integer.MAX_VALUE // threshold for DISABLED_BY_USER_SWITCH
};
public final AtomicBoolean mEnableAutoJoinWhenAssociated = new AtomicBoolean();
@@ -222,7 +228,6 @@
public final AtomicBoolean mEnableRssiPollWhenAssociated = new AtomicBoolean(true);
public final AtomicInteger mThresholdSaturatedRssi5 = new AtomicInteger();
public final AtomicInteger mThresholdQualifiedRssi24 = new AtomicInteger();
- public final AtomicInteger mEnableVerboseLogging = new AtomicInteger(0);
public final AtomicInteger mAlwaysEnableScansWhileAssociated = new AtomicInteger(0);
public final AtomicInteger mMaxNumActiveChannelsForPartialScans = new AtomicInteger();
@@ -255,6 +260,10 @@
* The SSIDs are encoded in a String as per definition of WifiConfiguration.SSID field.
*/
public Set<String> mDeletedEphemeralSSIDs = new HashSet<String>();
+ /**
+ * List of blacklisted BSSIDs.
+ */
+ private final HashSet<String> mBssidBlacklist = new HashSet<String>();
/* configured networks with network id as the key */
private final ConfigurationMap mConfiguredNetworks;
@@ -262,7 +271,8 @@
private final LocalLog mLocalLog;
private final KeyStore mKeyStore;
private final WifiNetworkHistory mWifiNetworkHistory;
- private final WifiConfigStore mWifiConfigStore;
+ private final WifiSupplicantControl mWifiSupplicantControl;
+ private final WifiKeyStore mWifiKeyStore;
private final AnqpCache mAnqpCache;
private final SupplicantBridge mSupplicantBridge;
private final SupplicantBridgeCallbacks mSupplicantBridgeCallbacks;
@@ -270,15 +280,15 @@
private final boolean mEnableOsuQueries;
private final SIMAccessor mSIMAccessor;
private final UserManager mUserManager;
- private final Object mActiveScanDetailLock = new Object();
+ private final BackupManagerProxy mBackupManagerProxy;
+ private boolean mVerboseLoggingEnabled = false;
private Context mContext;
private FrameworkFacade mFacade;
private Clock mClock;
private IpConfigStore mIpconfigStore;
private DelayedDiskWrite mWriter;
private boolean mOnlyLinkSameCredentialConfigurations;
- private boolean mShowNetworks = false;
private int mCurrentUserId = UserHandle.USER_SYSTEM;
/* Stores a map of NetworkId to ScanCache */
@@ -304,7 +314,8 @@
*/
private HashSet<String> mLostConfigsDbg = new HashSet<String>();
- private ScanDetail mActiveScanDetail; // ScanDetail associated with active network
+ // NetworkDetail associated with the last selected Passpoint network.
+ private NetworkDetail mSelectedPasspointNetwork;
private class SupplicantBridgeCallbacks implements SupplicantBridge.SupplicantBridgeCallbacks {
@Override
@@ -339,12 +350,7 @@
mClock = clock;
mKeyStore = keyStore;
mUserManager = userManager;
-
- if (mShowNetworks) {
- mLocalLog = wifiNative.getLocalLog();
- } else {
- mLocalLog = null;
- }
+ mLocalLog = wifiNative.getLocalLog();
mOnlyLinkSameCredentialConfigurations = mContext.getResources().getBoolean(
R.bool.config_wifi_only_link_same_credential_configurations);
@@ -396,28 +402,27 @@
mSIMAccessor = new SIMAccessor(mContext);
mWriter = new DelayedDiskWrite();
mIpconfigStore = new IpConfigStore(mWriter);
- mWifiNetworkHistory = new WifiNetworkHistory(context, mLocalLog, mWriter);
- mWifiConfigStore =
- new WifiConfigStore(context, wifiNative, mKeyStore, mLocalLog, mShowNetworks, true);
+ mWifiNetworkHistory = new WifiNetworkHistory(mContext, mLocalLog, mWriter);
+ mWifiSupplicantControl = new WifiSupplicantControl(mContext, wifiNative, mLocalLog);
+ mWifiKeyStore = new WifiKeyStore(keyStore);
+ mBackupManagerProxy = new BackupManagerProxy();
}
public void trimANQPCache(boolean all) {
mAnqpCache.clear(all, DBG);
}
+ /**
+ * Enable verbose logging.
+ */
void enableVerboseLogging(int verbose) {
- mEnableVerboseLogging.set(verbose);
if (verbose > 0) {
- sVDBG = true;
- mShowNetworks = true;
+ mVerboseLoggingEnabled = true;
} else {
- sVDBG = false;
+ mVerboseLoggingEnabled = false;
}
- if (verbose > 1) {
- sVVDBG = true;
- } else {
- sVVDBG = false;
- }
+ mWifiSupplicantControl.enableVerboseLogging(mVerboseLoggingEnabled);
+ mWifiKeyStore.enableVerboseLogging(mVerboseLoggingEnabled);
}
/**
@@ -543,50 +548,6 @@
}
/**
- * Fetch the list of currently saved networks (i.e. all configured networks, excluding
- * ephemeral networks) that were recently seen.
- *
- * @param scanResultAgeMs The maximum age (in ms) of scan results for which we calculate the
- * RSSI values
- * @param copy If true, the returned list will contain copies of the configurations for the
- * saved networks. Otherwise, the returned list will contain references to these
- * configurations.
- * @return List of networks
- */
- List<WifiConfiguration> getRecentSavedNetworks(int scanResultAgeMs, boolean copy) {
- List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
-
- for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
- if (config.ephemeral) {
- // Do not enumerate and return this configuration to anyone (e.g. WiFi Picker);
- // treat it as unknown instead. This configuration can still be retrieved
- // directly by its key or networkId.
- continue;
- }
-
- // Calculate the RSSI for scan results that are more recent than scanResultAgeMs.
- ScanDetailCache cache = getScanDetailCache(config);
- if (cache == null) {
- continue;
- }
- config.setVisibility(cache.getVisibility(scanResultAgeMs));
- if (config.visibility == null) {
- continue;
- }
- if (config.visibility.rssi5 == WifiConfiguration.INVALID_RSSI
- && config.visibility.rssi24 == WifiConfiguration.INVALID_RSSI) {
- continue;
- }
- if (copy) {
- networks.add(new WifiConfiguration(config));
- } else {
- networks.add(config);
- }
- }
- return networks;
- }
-
- /**
* Update the configuration and BSSID with latest RSSI value.
*/
void updateConfiguration(WifiInfo info) {
@@ -603,9 +564,8 @@
result.level = info.getRssi();
// Average the RSSI value
- result.averageRssi(previousRssi, previousSeen,
- WifiQualifiedNetworkSelector.SCAN_RESULT_MAXIMUNM_AGE);
- if (sVDBG) {
+ result.averageRssi(previousRssi, previousSeen, SCAN_RESULT_MAXIMUM_AGE_MS);
+ if (mVerboseLoggingEnabled) {
logd("updateConfiguration freq=" + result.frequency
+ " BSSID=" + result.BSSID
+ " RSSI=" + result.level
@@ -655,11 +615,11 @@
}
private boolean setNetworkPriorityNative(WifiConfiguration config, int priority) {
- return mWifiConfigStore.setNetworkPriority(config, priority);
+ return mWifiSupplicantControl.setNetworkPriority(config, priority);
}
private boolean setSSIDNative(WifiConfiguration config, String ssid) {
- return mWifiConfigStore.setNetworkSSID(config, ssid);
+ return mWifiSupplicantControl.setNetworkSSID(config, ssid);
}
public boolean updateLastConnectUid(WifiConfiguration config, int uid) {
@@ -686,7 +646,7 @@
* @return false if the network id is invalid
*/
boolean selectNetwork(WifiConfiguration config, boolean updatePriorities, int uid) {
- if (sVDBG) localLogNetwork("selectNetwork", config.networkId);
+ if (mVerboseLoggingEnabled) localLogNetwork("selectNetwork", config.networkId);
if (config.networkId == INVALID_NETWORK_ID) return false;
if (!WifiConfigurationUtil.isVisibleToAnyProfile(config,
mUserManager.getProfiles(mCurrentUserId))) {
@@ -712,6 +672,7 @@
setNetworkPriorityNative(config, ++mLastPriority);
}
+ NetworkDetail selectedPasspointNetwork = null;
if (config.isPasspoint()) {
// Set the SSID for the underlying WPA supplicant network entry corresponding to this
// Passpoint profile to the SSID of the BSS selected by QNS. |config.SSID| is set by
@@ -720,9 +681,18 @@
logd("Setting SSID for WPA supplicant network " + config.networkId + " to "
+ config.SSID);
setSSIDNative(config, config.SSID);
+ String selectedPasspointNetworkBssid = mWifiSupplicantControl.getNetworkBSSID(config);
+ if (selectedPasspointNetworkBssid != null) {
+ ScanDetail result =
+ getScanDetailCache(config).getScanDetail(selectedPasspointNetworkBssid);
+ if (result != null) {
+ selectedPasspointNetwork = result.getNetworkDetail();
+ }
+ }
}
+ mSelectedPasspointNetwork = selectedPasspointNetwork;
- mWifiConfigStore.enableHS20(config.isPasspoint());
+ mWifiSupplicantControl.enableHS20(config.isPasspoint());
if (updatePriorities) {
saveConfig();
@@ -759,8 +729,10 @@
return new NetworkUpdateResult(INVALID_NETWORK_ID);
}
- if (sVDBG) localLogNetwork("WifiConfigManager: saveNetwork netId", config.networkId);
- if (sVDBG) {
+ if (mVerboseLoggingEnabled) {
+ localLogNetwork("WifiConfigManager: saveNetwork netId", config.networkId);
+ }
+ if (mVerboseLoggingEnabled) {
logd("WifiConfigManager saveNetwork,"
+ " size=" + Integer.toString(mConfiguredNetworks.sizeForAllUsers())
+ " (for all users)"
@@ -770,7 +742,7 @@
}
if (mDeletedEphemeralSSIDs.remove(config.SSID)) {
- if (sVDBG) {
+ if (mVerboseLoggingEnabled) {
logd("WifiConfigManager: removed from ephemeral blacklist: " + config.SSID);
}
// NOTE: This will be flushed to disk as part of the addOrUpdateNetworkNative call
@@ -781,18 +753,22 @@
NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid);
int netId = result.getNetworkId();
- if (sVDBG) localLogNetwork("WifiConfigManager: saveNetwork got it back netId=", netId);
+ if (mVerboseLoggingEnabled) {
+ localLogNetwork("WifiConfigManager: saveNetwork got it back netId=", netId);
+ }
conf = mConfiguredNetworks.getForCurrentUser(netId);
if (conf != null) {
if (!conf.getNetworkSelectionStatus().isNetworkEnabled()) {
- if (sVDBG) localLog("WifiConfigManager: re-enabling: " + conf.SSID);
+ if (mVerboseLoggingEnabled) {
+ localLog("WifiConfigManager: re-enabling: " + conf.SSID);
+ }
// reenable autojoin, since new information has been provided
updateNetworkSelectionStatus(netId,
WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
}
- if (sVDBG) {
+ if (mVerboseLoggingEnabled) {
logd("WifiConfigManager: saveNetwork got config back netId="
+ Integer.toString(netId)
+ " uid=" + Integer.toString(config.creatorUid));
@@ -810,7 +786,7 @@
void noteRoamingFailure(WifiConfiguration config, int reason) {
if (config == null) return;
- config.lastRoamingFailure = mClock.currentTimeMillis();
+ config.lastRoamingFailure = mClock.getWallClockMillis();
config.roamingFailureBlackListTimeMilli =
2 * (config.roamingFailureBlackListTimeMilli + 1000);
if (config.roamingFailureBlackListTimeMilli > mNetworkSwitchingBlackListPeriodMs) {
@@ -820,7 +796,7 @@
}
void saveWifiConfigBSSID(WifiConfiguration config, String bssid) {
- mWifiConfigStore.setNetworkBSSID(config, bssid);
+ mWifiSupplicantControl.setNetworkBSSID(config, bssid);
}
@@ -836,6 +812,7 @@
WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
break;
case DISCONNECTED:
+ mSelectedPasspointNetwork = null;
//If network is already disabled, keep the status
if (config.status == Status.CURRENT) {
config.status = Status.ENABLED;
@@ -885,7 +862,7 @@
* @return {@code true} if it succeeds, {@code false} otherwise
*/
boolean forgetNetwork(int netId) {
- if (mShowNetworks) localLogNetwork("forgetNetwork", netId);
+ if (mVerboseLoggingEnabled) localLogNetwork("forgetNetwork", netId);
if (!removeNetwork(netId)) {
loge("Failed to forget network " + netId);
return false;
@@ -910,7 +887,7 @@
return WifiConfiguration.INVALID_NETWORK_ID;
}
- if (mShowNetworks) localLogNetwork("addOrUpdateNetwork id=", config.networkId);
+ if (mVerboseLoggingEnabled) localLogNetwork("addOrUpdateNetwork id=", config.networkId);
if (config.isPasspoint()) {
/* create a temporary SSID with providerFriendlyName */
Long csum = getChecksum(config.FQDN);
@@ -955,11 +932,8 @@
}
public int matchProviderWithCurrentNetwork(String fqdn) {
- ScanDetail scanDetail = null;
- synchronized (mActiveScanDetailLock) {
- scanDetail = mActiveScanDetail;
- }
- if (scanDetail == null) {
+ NetworkDetail selectedPasspointNetwork = mSelectedPasspointNetwork;
+ if (selectedPasspointNetwork == null) {
return PasspointMatch.None.ordinal();
}
HomeSP homeSP = mMOManager.getHomeSP(fqdn);
@@ -967,12 +941,15 @@
return PasspointMatch.None.ordinal();
}
- ANQPData anqpData = mAnqpCache.getEntry(scanDetail.getNetworkDetail());
+ Map<Constants.ANQPElementType, ANQPElement> anqpElements
+ = selectedPasspointNetwork.getANQPElements();
- Map<Constants.ANQPElementType, ANQPElement> anqpElements =
- anqpData != null ? anqpData.getANQPElements() : null;
+ if (anqpElements == null || anqpElements.isEmpty()) {
+ ANQPData anqpData = mAnqpCache.getEntry(selectedPasspointNetwork);
+ anqpElements = anqpData != null ? anqpData.getANQPElements() : null;
+ }
- return homeSP.match(scanDetail.getNetworkDetail(), anqpElements, mSIMAccessor).ordinal();
+ return homeSP.match(selectedPasspointNetwork, anqpElements, mSIMAccessor).ordinal();
}
/**
@@ -1149,7 +1126,7 @@
* @return {@code true} if it succeeds, {@code false} otherwise
*/
boolean removeNetwork(int netId) {
- if (mShowNetworks) localLogNetwork("removeNetwork", netId);
+ if (mVerboseLoggingEnabled) localLogNetwork("removeNetwork", netId);
WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
if (!removeConfigAndSendBroadcastIfNeeded(config)) {
return false;
@@ -1170,7 +1147,7 @@
if (config == null) {
return false;
}
- if (!mWifiConfigStore.removeNetwork(config)) {
+ if (!removeNetworkNative(config)) {
loge("Failed to remove network " + config.networkId);
return false;
}
@@ -1187,7 +1164,7 @@
return false;
}
String key = config.configKey();
- if (sVDBG) {
+ if (mVerboseLoggingEnabled) {
logd("removeNetwork " + " key=" + key + " config.id=" + config.networkId);
}
writeIpAndProxyConfigurations();
@@ -1238,7 +1215,7 @@
if (app.uid != config.creatorUid || !app.packageName.equals(config.creatorName)) {
continue;
}
- if (mShowNetworks) {
+ if (mVerboseLoggingEnabled) {
localLog("Removing network " + config.SSID
+ ", application \"" + app.packageName + "\" uninstalled"
+ " from user " + UserHandle.getUserId(app.uid));
@@ -1261,7 +1238,7 @@
continue;
}
success &= removeNetwork(config.networkId);
- if (mShowNetworks) {
+ if (mVerboseLoggingEnabled) {
localLog("Removing network " + config.SSID
+ ", user " + userId + " removed");
}
@@ -1283,54 +1260,61 @@
if (config == null) {
return false;
}
-
updateNetworkSelectionStatus(
config, WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
setLatestUserSelectedConfiguration(config);
boolean ret = true;
if (disableOthers) {
ret = selectNetworkWithoutBroadcast(config.networkId);
- if (sVDBG) {
- localLogNetwork("enableNetwork(disableOthers=true, uid=" + uid + ") ",
- config.networkId);
+ if (ret) {
+ if (mVerboseLoggingEnabled) {
+ localLogNetwork("enableNetwork(disableOthers=true, uid=" + uid + ") ",
+ config.networkId);
+ }
+ markAllNetworksDisabledExcept(config.networkId,
+ WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER);
+ updateLastConnectUid(config, uid);
+ writeKnownNetworkHistory();
+ sendConfiguredNetworksChangedBroadcast();
}
- updateLastConnectUid(config, uid);
- writeKnownNetworkHistory();
- sendConfiguredNetworksChangedBroadcast();
} else {
- if (sVDBG) localLogNetwork("enableNetwork(disableOthers=false) ", config.networkId);
+ if (mVerboseLoggingEnabled) {
+ localLogNetwork("enableNetwork(disableOthers=false) ", config.networkId);
+ }
sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_CONFIG_CHANGE);
}
return ret;
}
boolean selectNetworkWithoutBroadcast(int netId) {
- return mWifiConfigStore.selectNetwork(
- mConfiguredNetworks.getForCurrentUser(netId),
- mConfiguredNetworks.valuesForCurrentUser());
+ return mWifiSupplicantControl.selectNetwork(mConfiguredNetworks.getForCurrentUser(netId));
}
/**
* Disable a network in wpa_supplicant.
*/
boolean disableNetworkNative(WifiConfiguration config) {
- return mWifiConfigStore.disableNetwork(config);
+ return mWifiSupplicantControl.disableNetwork(config);
}
/**
* Disable all networks in wpa_supplicant.
*/
void disableAllNetworksNative() {
- mWifiConfigStore.disableAllNetworks(mConfiguredNetworks.valuesForCurrentUser());
+ mWifiSupplicantControl.disableAllNetworks(mConfiguredNetworks.valuesForCurrentUser());
}
- /**
- * Disable a network. Note that there is no saveConfig operation.
- * @param netId network to be disabled
- * @return {@code true} if it succeeds, {@code false} otherwise
- */
- boolean disableNetwork(int netId) {
- return mWifiConfigStore.disableNetwork(mConfiguredNetworks.getForCurrentUser(netId));
+ /* Mark all networks except specified netId as disabled */
+ private void markAllNetworksDisabledExcept(int netId, int disableReason) {
+ for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
+ if (config != null && config.networkId != netId) {
+ updateNetworkSelectionStatus(config, disableReason);
+ }
+ }
+ }
+
+ private void markAllNetworksDisabled(int disableReason) {
+ markAllNetworksDisabledExcept(WifiConfiguration.INVALID_NETWORK_ID, disableReason);
}
/**
@@ -1381,11 +1365,13 @@
}
/**
- * Check the config. If it is temporarily disabled, check the disable time is expired or not, If
- * expired, enabled it again for qualified network selection.
+ * Attempt to re-enable a network for qualified network selection, if this network was either:
+ * a) Previously temporarily disabled, but its disable timeout has expired, or
+ * b) Previously disabled because of a user switch, but is now visible to the current
+ * user.
* @param networkId the id of the network to be checked for possible unblock (due to timeout)
- * @return true if network status has been changed
- * false network status is not changed
+ * @return true if the network identified by {@param networkId} was re-enabled for qualified
+ * network selection, false otherwise.
*/
public boolean tryEnableQualifiedNetwork(int networkId) {
WifiConfiguration config = getWifiConfiguration(networkId);
@@ -1397,25 +1383,33 @@
}
/**
- * Check the config. If it is temporarily disabled, check the disable is expired or not, If
- * expired, enabled it again for qualified network selection.
- * @param config network to be checked for possible unblock (due to timeout)
- * @return true if network status has been changed
- * false network status is not changed
+ * Attempt to re-enable a network for qualified network selection, if this network was either:
+ * a) Previously temporarily disabled, but its disable timeout has expired, or
+ * b) Previously disabled because of a user switch, but is now visible to the current
+ * user.
+ * @param config configuration for the network to be re-enabled for network selection. The
+ * network corresponding to the config must be visible to the current user.
+ * @return true if the network identified by {@param config} was re-enabled for qualified
+ * network selection, false otherwise.
*/
private boolean tryEnableQualifiedNetwork(WifiConfiguration config) {
WifiConfiguration.NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
if (networkStatus.isNetworkTemporaryDisabled()) {
//time difference in minutes
long timeDifference =
- (mClock.elapsedRealtime() - networkStatus.getDisableTime()) / 1000 / 60;
+ (mClock.getElapsedSinceBootMillis() - networkStatus.getDisableTime()) / 1000 / 60;
if (timeDifference < 0 || timeDifference
>= NETWORK_SELECTION_DISABLE_TIMEOUT[
networkStatus.getNetworkSelectionDisableReason()]) {
updateNetworkSelectionStatus(config.networkId,
- networkStatus.NETWORK_SELECTION_ENABLE);
+ WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
return true;
}
+ } else if (networkStatus.isDisabledByReason(
+ WifiConfiguration.NetworkSelectionStatus.DISABLED_DUE_TO_USER_SWITCH)) {
+ updateNetworkSelectionStatus(config.networkId,
+ WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
+ return true;
}
return false;
}
@@ -1436,15 +1430,14 @@
WifiConfiguration.NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
if (reason < 0 || reason >= WifiConfiguration.NetworkSelectionStatus
.NETWORK_SELECTION_DISABLED_MAX) {
- localLog("Invalid Network disable reason:" + reason);
+ localLog("Invalid Network disable reason: " + reason);
return false;
}
if (reason == WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
if (networkStatus.isNetworkEnabled()) {
if (DBG) {
- localLog("Need not change Qualified network Selection status since"
- + " already enabled");
+ localLog("Do nothing. Network is already enabled!");
}
return false;
}
@@ -1455,17 +1448,13 @@
WifiConfiguration.NetworkSelectionStatus
.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
networkStatus.clearDisableReasonCounter();
- String disableTime = DateFormat.getDateTimeInstance().format(new Date());
- if (DBG) {
- localLog("Re-enable network: " + config.SSID + " at " + disableTime);
- }
+ config.status = Status.ENABLED;
sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_CONFIG_CHANGE);
} else {
//disable the network
if (networkStatus.isNetworkPermanentlyDisabled()) {
- //alreay permanent disable
if (DBG) {
- localLog("Do nothing. Alreay permanent disabled! "
+ localLog("Do nothing. Network is already permanently disabled! Disable reason: "
+ WifiConfiguration.NetworkSelectionStatus
.getNetworkDisableReasonString(reason));
}
@@ -1473,38 +1462,32 @@
} else if (networkStatus.isNetworkTemporaryDisabled()
&& reason < WifiConfiguration.NetworkSelectionStatus
.DISABLED_TLS_VERSION_MISMATCH) {
- //alreay temporarily disable
if (DBG) {
- localLog("Do nothing. Already temporarily disabled! "
+ localLog("Do nothing. Network is already temporarily disabled! Disable reason: "
+ WifiConfiguration.NetworkSelectionStatus
.getNetworkDisableReasonString(reason));
}
return false;
}
-
- if (networkStatus.isNetworkEnabled()) {
- disableNetworkNative(config);
- sendConfiguredNetworksChangedBroadcast(config,
- WifiManager.CHANGE_REASON_CONFIG_CHANGE);
- localLog("Disable network " + config.SSID + " reason:"
- + WifiConfiguration.NetworkSelectionStatus
- .getNetworkDisableReasonString(reason));
- }
+ disableNetworkNative(config);
if (reason < WifiConfiguration.NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) {
networkStatus.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus
.NETWORK_SELECTION_TEMPORARY_DISABLED);
- networkStatus.setDisableTime(mClock.elapsedRealtime());
+ networkStatus.setDisableTime(mClock.getElapsedSinceBootMillis());
} else {
networkStatus.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus
.NETWORK_SELECTION_PERMANENTLY_DISABLED);
+ config.status = Status.DISABLED;
+ sendConfiguredNetworksChangedBroadcast(
+ config, WifiManager.CHANGE_REASON_CONFIG_CHANGE);
}
networkStatus.setNetworkSelectionDisableReason(reason);
- if (DBG) {
- String disableTime = DateFormat.getDateTimeInstance().format(new Date());
- localLog("Network:" + config.SSID + "Configure new status:"
- + networkStatus.getNetworkStatusString() + " with reason:"
- + networkStatus.getNetworkDisableReasonString() + " at: " + disableTime);
- }
+ }
+ if (DBG) {
+ String time = DateFormat.getDateTimeInstance().format(new Date());
+ localLog("Network: " + config.SSID + " Configure new status: "
+ + networkStatus.getNetworkStatusString() + " with reason: "
+ + networkStatus.getNetworkDisableReasonString() + " at: " + time);
}
return true;
}
@@ -1514,7 +1497,7 @@
* @return {@code true} if it succeeds, {@code false} otherwise
*/
boolean saveConfig() {
- return mWifiConfigStore.saveConfig();
+ return mWifiSupplicantControl.saveConfig();
}
/**
@@ -1524,8 +1507,12 @@
* @return Wps result containing status and pin
*/
WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) {
- return mWifiConfigStore.startWpsWithPinFromAccessPoint(
- config, mConfiguredNetworks.valuesForCurrentUser());
+ WpsResult result = mWifiSupplicantControl.startWpsWithPinFromAccessPoint(config);
+ /* WPS leaves all networks disabled */
+ if (result.status == WpsResult.Status.SUCCESS) {
+ markAllNetworksDisabled(WifiConfiguration.NetworkSelectionStatus.DISABLED_WPS_START);
+ }
+ return result;
}
/**
@@ -1534,8 +1521,12 @@
* @return WpsResult indicating status and pin
*/
WpsResult startWpsWithPinFromDevice(WpsInfo config) {
- return mWifiConfigStore.startWpsWithPinFromDevice(
- config, mConfiguredNetworks.valuesForCurrentUser());
+ WpsResult result = mWifiSupplicantControl.startWpsWithPinFromDevice(config);
+ /* WPS leaves all networks disabled */
+ if (result.status == WpsResult.Status.SUCCESS) {
+ markAllNetworksDisabled(WifiConfiguration.NetworkSelectionStatus.DISABLED_WPS_START);
+ }
+ return result;
}
/**
@@ -1544,8 +1535,12 @@
* @return WpsResult indicating status and pin
*/
WpsResult startWpsPbc(WpsInfo config) {
- return mWifiConfigStore.startWpsPbc(
- config, mConfiguredNetworks.valuesForCurrentUser());
+ WpsResult result = mWifiSupplicantControl.startWpsPbc(config);
+ /* WPS leaves all networks disabled */
+ if (result.status == WpsResult.Status.SUCCESS) {
+ markAllNetworksDisabled(WifiConfiguration.NetworkSelectionStatus.DISABLED_WPS_START);
+ }
+ return result;
}
/**
@@ -1647,7 +1642,7 @@
final Map<String, WifiConfiguration> configs = new HashMap<>();
final SparseArray<Map<String, String>> networkExtras = new SparseArray<>();
- mLastPriority = mWifiConfigStore.loadNetworks(configs, networkExtras);
+ mLastPriority = mWifiSupplicantControl.loadNetworks(configs, networkExtras);
readNetworkHistory(configs);
readPasspointConfig(configs, networkExtras);
@@ -1658,15 +1653,16 @@
// 2) mConfiguredNetworks caches a Passpoint network's FQDN the moment the network is added.
// Thus, we had to load the FQDNs first.
mConfiguredNetworks.clear();
+ mScanDetailCaches.clear();
for (Map.Entry<String, WifiConfiguration> entry : configs.entrySet()) {
final String configKey = entry.getKey();
final WifiConfiguration config = entry.getValue();
if (!configKey.equals(config.configKey())) {
- if (mShowNetworks) {
+ if (mVerboseLoggingEnabled) {
log("Ignoring network " + config.networkId + " because the configKey loaded "
+ "from wpa_supplicant.conf is not valid.");
}
- mWifiConfigStore.removeNetwork(config);
+ removeNetworkNative(config);
continue;
}
mConfiguredNetworks.put(config);
@@ -1676,7 +1672,7 @@
sendConfiguredNetworksChangedBroadcast();
- if (mShowNetworks) {
+ if (mVerboseLoggingEnabled) {
localLog("loadConfiguredNetworks loaded " + mConfiguredNetworks.sizeForAllUsers()
+ " networks (for all users)");
}
@@ -1684,8 +1680,8 @@
if (mConfiguredNetworks.sizeForAllUsers() == 0) {
// no networks? Lets log if the file contents
logKernelTime();
- logContents(WifiConfigStore.SUPPLICANT_CONFIG_FILE);
- logContents(WifiConfigStore.SUPPLICANT_CONFIG_FILE_BACKUP);
+ logContents(WifiSupplicantControl.SUPPLICANT_CONFIG_FILE);
+ logContents(WifiSupplicantControl.SUPPLICANT_CONFIG_FILE_BACKUP);
logContents(WifiNetworkHistory.NETWORK_HISTORY_CONFIG_FILE);
}
}
@@ -1717,15 +1713,16 @@
}
private Map<String, String> readNetworkVariablesFromSupplicantFile(String key) {
- return mWifiConfigStore.readNetworkVariablesFromSupplicantFile(key);
+ return mWifiSupplicantControl.readNetworkVariablesFromSupplicantFile(key);
}
private String readNetworkVariableFromSupplicantFile(String configKey, String key) {
- long start = SystemClock.elapsedRealtimeNanos();
- Map<String, String> data = mWifiConfigStore.readNetworkVariablesFromSupplicantFile(key);
- long end = SystemClock.elapsedRealtimeNanos();
+ long start = mClock.getElapsedSinceBootNanos();
+ Map<String, String> data =
+ mWifiSupplicantControl.readNetworkVariablesFromSupplicantFile(key);
+ long end = mClock.getElapsedSinceBootNanos();
- if (sVDBG) {
+ if (mVerboseLoggingEnabled) {
localLog("readNetworkVariableFromSupplicantFile configKey=[" + configKey + "] key="
+ key + " duration=" + (long) (end - start));
}
@@ -1743,7 +1740,7 @@
if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP)
&& config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
- if (needsSoftwareBackedKeyStore(config.enterpriseConfig)) {
+ if (mWifiKeyStore.needsSoftwareBackedKeyStore(config.enterpriseConfig)) {
return true;
}
}
@@ -1773,7 +1770,8 @@
continue;
}
final String configFqdn =
- networkExtras.get(config.networkId).get(WifiConfigStore.ID_STRING_KEY_FQDN);
+ networkExtras.get(config.networkId)
+ .get(WifiSupplicantControl.ID_STRING_KEY_FQDN);
if (configFqdn != null && configFqdn.equals(fqdn)) {
Log.d(TAG, "Matched " + configFqdn + " with " + config.networkId);
++matchedConfigs;
@@ -1838,7 +1836,7 @@
}
public void setAndEnableLastSelectedConfiguration(int netId) {
- if (sVDBG) {
+ if (mVerboseLoggingEnabled) {
logd("setLastSelectedConfiguration " + Integer.toString(netId));
}
if (netId == WifiConfiguration.INVALID_NETWORK_ID) {
@@ -1851,10 +1849,10 @@
mLastSelectedTimeStamp = -1;
} else {
mLastSelectedConfiguration = selected.configKey();
- mLastSelectedTimeStamp = mClock.elapsedRealtime();
+ mLastSelectedTimeStamp = mClock.getElapsedSinceBootMillis();
updateNetworkSelectionStatus(netId,
WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
- if (sVDBG) {
+ if (mVerboseLoggingEnabled) {
logd("setLastSelectedConfiguration now: " + mLastSelectedConfiguration);
}
}
@@ -1864,7 +1862,7 @@
public void setLatestUserSelectedConfiguration(WifiConfiguration network) {
if (network != null) {
mLastSelectedConfiguration = network.configKey();
- mLastSelectedTimeStamp = mClock.elapsedRealtime();
+ mLastSelectedTimeStamp = mClock.getElapsedSinceBootMillis();
}
}
@@ -1923,7 +1921,9 @@
* refer to an existing configuration.
*/
- if (sVDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid());
+ if (mVerboseLoggingEnabled) {
+ localLog("addOrUpdateNetworkNative " + config.getPrintableSsid());
+ }
if (config.isPasspoint() && !mMOManager.isEnabled()) {
Log.e(TAG, "Passpoint is not enabled");
return new NetworkUpdateResult(INVALID_NETWORK_ID);
@@ -1955,9 +1955,19 @@
// HasEverConnected to be set to false.
WifiConfiguration originalConfig = new WifiConfiguration(currentConfig);
- if (!mWifiConfigStore.addOrUpdateNetwork(config, currentConfig)) {
+ if (!mWifiSupplicantControl.addOrUpdateNetwork(config)) {
return new NetworkUpdateResult(INVALID_NETWORK_ID);
}
+ // Update the keys for enterprise networks and save the enterprise parameters to
+ // wpa_supplicant.
+ if (config.enterpriseConfig != null
+ && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
+ if (!(mWifiKeyStore.updateNetworkKeys(config, currentConfig)
+ && mWifiSupplicantControl.saveEnterpriseConfiguration(config))) {
+ removeNetworkNative(config);
+ return new NetworkUpdateResult(INVALID_NETWORK_ID);
+ }
+ }
int netId = config.networkId;
String savedConfigKey = config.configKey();
@@ -2044,21 +2054,18 @@
StringBuilder sb = new StringBuilder();
sb.append("time=");
Calendar c = Calendar.getInstance();
- c.setTimeInMillis(mClock.currentTimeMillis());
+ c.setTimeInMillis(mClock.getWallClockMillis());
sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
if (newNetwork) {
+ // New networks start out disabled according to public API.
+ updateNetworkSelectionStatus(currentConfig,
+ WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER);
currentConfig.creationTime = sb.toString();
} else {
currentConfig.updateTime = sb.toString();
}
- if (currentConfig.status == WifiConfiguration.Status.ENABLED) {
- // Make sure autojoin remain in sync with user modifying the configuration
- updateNetworkSelectionStatus(currentConfig.networkId,
- WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
- }
-
if (currentConfig.configKey().equals(getLastSelectedConfiguration())
&& currentConfig.ephemeral) {
// Make the config non-ephemeral since the user just explicitly clicked it.
@@ -2069,15 +2076,17 @@
}
}
- if (sVDBG) log("will read network variables netId=" + Integer.toString(netId));
+ if (mVerboseLoggingEnabled) {
+ log("will read network variables netId=" + Integer.toString(netId));
+ }
readNetworkVariables(currentConfig);
// When we read back the config from wpa_supplicant, some of the default values are set
// which could change the configKey.
if (!savedConfigKey.equals(currentConfig.configKey())) {
- if (!mWifiConfigStore.saveNetworkMetadata(currentConfig)) {
+ if (!mWifiSupplicantControl.saveNetworkMetadata(currentConfig)) {
loge("Failed to set network metadata. Removing config " + config.networkId);
- mWifiConfigStore.removeNetwork(config);
+ removeNetworkNative(config);
return new NetworkUpdateResult(INVALID_NETWORK_ID);
}
}
@@ -2113,10 +2122,25 @@
saveConfig();
writeKnownNetworkHistory();
-
+ // Stage the backup of the SettingsProvider package which backs this up.
+ mBackupManagerProxy.notifyDataChanged();
return result;
}
+ private boolean removeNetworkNative(WifiConfiguration config) {
+ if (!mWifiSupplicantControl.removeNetwork(config)) {
+ return false;
+ }
+ // Remove any associated keys.
+ if (config.enterpriseConfig != null
+ && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
+ mWifiKeyStore.removeKeys(config.enterpriseConfig);
+ }
+ // Stage the backup of the SettingsProvider package which backs this up.
+ mBackupManagerProxy.notifyDataChanged();
+ return true;
+ }
+
private boolean wasBitSetUpdated(BitSet originalBitSet, BitSet currentBitSet) {
if (originalBitSet != null && currentBitSet != null) {
// both configs have values set, check if they are different
@@ -2249,7 +2273,9 @@
if (config == null) return null;
ScanDetailCache cache = mScanDetailCaches.get(config.networkId);
if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
- cache = new ScanDetailCache(config);
+ cache =
+ new ScanDetailCache(
+ config, MAX_NUM_SCAN_CACHE_ENTRIES + 64, MAX_NUM_SCAN_CACHE_ENTRIES);
mScanDetailCaches.put(config.networkId, cache);
}
return cache;
@@ -2301,7 +2327,7 @@
if (config.defaultGwMacAddress != null && link.defaultGwMacAddress != null) {
// If both default GW are known, link only if they are equal
if (config.defaultGwMacAddress.equals(link.defaultGwMacAddress)) {
- if (sVDBG) {
+ if (mVerboseLoggingEnabled) {
logd("linkConfiguration link due to same gw " + link.SSID
+ " and " + config.SSID + " GW " + config.defaultGwMacAddress);
}
@@ -2317,7 +2343,7 @@
for (String abssid : getScanDetailCache(config).keySet()) {
for (String bbssid : linkedScanDetailCache.keySet()) {
- if (sVVDBG) {
+ if (mVerboseLoggingEnabled) {
logd("linkConfiguration try to link due to DBDC BSSID match "
+ link.SSID + " and " + config.SSID + " bssida " + abssid
+ " bssidb " + bbssid);
@@ -2346,7 +2372,7 @@
}
if (doLink) {
- if (sVDBG) {
+ if (mVerboseLoggingEnabled) {
logd("linkConfiguration: will link " + link.configKey()
+ " and " + config.configKey());
}
@@ -2365,7 +2391,7 @@
} else {
if (link.linkedConfigurations != null
&& (link.linkedConfigurations.get(config.configKey()) != null)) {
- if (sVDBG) {
+ if (mVerboseLoggingEnabled) {
logd("linkConfiguration: un-link " + config.configKey()
+ " from " + link.configKey());
}
@@ -2373,7 +2399,7 @@
}
if (config.linkedConfigurations != null
&& (config.linkedConfigurations.get(link.configKey()) != null)) {
- if (sVDBG) {
+ if (mVerboseLoggingEnabled) {
logd("linkConfiguration: un-link " + link.configKey()
+ " from " + config.configKey());
}
@@ -2387,7 +2413,7 @@
if (config == null) {
return null;
}
- long now_ms = mClock.currentTimeMillis();
+ long now_ms = mClock.getWallClockMillis();
HashSet<Integer> channels = new HashSet<Integer>();
@@ -2396,7 +2422,7 @@
return null;
}
- if (sVDBG) {
+ if (mVerboseLoggingEnabled) {
StringBuilder dbg = new StringBuilder();
dbg.append("makeChannelList age=" + Integer.toString(age)
+ " for " + config.configKey()
@@ -2418,7 +2444,7 @@
if (numChannels > mMaxNumActiveChannelsForPartialScans.get()) {
break;
}
- if (sVDBG) {
+ if (mVerboseLoggingEnabled) {
boolean test = (now_ms - result.seen) < age;
logd("has " + result.BSSID + " freq=" + Integer.toString(result.frequency)
+ " age=" + Long.toString(now_ms - result.seen) + " ?=" + test);
@@ -2442,7 +2468,7 @@
}
for (ScanDetail scanDetail : getScanDetailCache(linked).values()) {
ScanResult result = scanDetail.getScanResult();
- if (sVDBG) {
+ if (mVerboseLoggingEnabled) {
logd("has link: " + result.BSSID
+ " freq=" + Integer.toString(result.frequency)
+ " age=" + Long.toString(now_ms - result.seen));
@@ -2629,30 +2655,6 @@
scanResult.untrusted = true;
}
- if (scanDetailCache.size() > (MAX_NUM_SCAN_CACHE_ENTRIES + 64)) {
- long now_dbg = 0;
- if (sVVDBG) {
- logd(" Will trim config " + config.configKey()
- + " size " + scanDetailCache.size());
-
- for (ScanDetail sd : scanDetailCache.values()) {
- logd(" " + sd.getBSSIDString() + " " + sd.getSeen());
- }
- now_dbg = SystemClock.elapsedRealtimeNanos();
- }
- // Trim the scan result cache to MAX_NUM_SCAN_CACHE_ENTRIES entries max
- // Since this operation is expensive, make sure it is not performed
- // until the cache has grown significantly above the trim treshold
- scanDetailCache.trim(MAX_NUM_SCAN_CACHE_ENTRIES);
- if (sVVDBG) {
- long diff = SystemClock.elapsedRealtimeNanos() - now_dbg;
- logd(" Finished trimming config, time(ns) " + diff);
- for (ScanDetail sd : scanDetailCache.values()) {
- logd(" " + sd.getBSSIDString() + " " + sd.getSeen());
- }
- }
- }
-
// Add the scan result to this WifiConfiguration
if (passpointMatch != null) {
scanDetailCache.put(scanDetail, passpointMatch, getHomeSPForConfig(config));
@@ -2796,7 +2798,8 @@
final List<WifiConfiguration> hiddenConfigurations =
mConfiguredNetworks.handleUserSwitch(mCurrentUserId);
for (WifiConfiguration network : hiddenConfigurations) {
- disableNetworkNative(network);
+ updateNetworkSelectionStatus(
+ network, WifiConfiguration.NetworkSelectionStatus.DISABLED_DUE_TO_USER_SWITCH);
}
enableAllNetworks();
@@ -2895,7 +2898,7 @@
}
if (ipChanged || proxyChanged || isNewNetwork) {
- if (sVDBG) {
+ if (mVerboseLoggingEnabled) {
logd("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> "
+ newConfig.SSID + " path: " + IP_CONFIG_FILE);
}
@@ -2911,7 +2914,7 @@
* @param config the {@link WifiConfiguration} object to be filled in.
*/
private void readNetworkVariables(WifiConfiguration config) {
- mWifiConfigStore.readNetworkVariables(config);
+ mWifiSupplicantControl.readNetworkVariables(config);
}
/* return the allowed key management based on a scan result */
@@ -2922,7 +2925,7 @@
config.SSID = "\"" + result.SSID + "\"";
- if (sVDBG) {
+ if (mVerboseLoggingEnabled) {
logd("WifiConfiguration from scan results "
+ config.SSID + " cap " + result.capabilities);
}
@@ -3051,68 +3054,11 @@
}
}
- static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) {
- String client = config.getClientCertificateAlias();
- if (!TextUtils.isEmpty(client)) {
- // a valid client certificate is configured
-
- // BUGBUG: keyStore.get() never returns certBytes; because it is not
- // taking WIFI_UID as a parameter. It always looks for certificate
- // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that
- // all certificates need software keystore until we get the get() API
- // fixed.
-
- return true;
- }
-
- /*
- try {
-
- if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials
- .USER_CERTIFICATE + client);
-
- CertificateFactory factory = CertificateFactory.getInstance("X.509");
- if (factory == null) {
- Slog.e(TAG, "Error getting certificate factory");
- return;
- }
-
- byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client);
- if (certBytes != null) {
- Certificate cert = (X509Certificate) factory.generateCertificate(
- new ByteArrayInputStream(certBytes));
-
- if (cert != null) {
- mNeedsSoftwareKeystore = hasHardwareBackedKey(cert);
-
- if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials
- .USER_CERTIFICATE + client);
- if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" :
- "does not need" ) + " software key store");
- } else {
- Slog.d(TAG, "could not generate certificate");
- }
- } else {
- Slog.e(TAG, "Could not load client certificate " + Credentials
- .USER_CERTIFICATE + client);
- mNeedsSoftwareKeystore = true;
- }
-
- } catch(CertificateException e) {
- Slog.e(TAG, "Could not read certificates");
- mCaCert = null;
- mClientCertificate = null;
- }
- */
-
- return false;
- }
-
/**
* Resets all sim networks from the network list.
*/
public void resetSimNetworks() {
- mWifiConfigStore.resetSimNetworks(mConfiguredNetworks.valuesForCurrentUser());
+ mWifiSupplicantControl.resetSimNetworks(mConfiguredNetworks.valuesForCurrentUser());
}
boolean isNetworkConfigured(WifiConfiguration config) {
@@ -3242,7 +3188,7 @@
}
}
// Record last time Connectivity Service switched us away from WiFi and onto Cell
- mLastUnwantedNetworkDisconnectTimestamp = mClock.currentTimeMillis();
+ mLastUnwantedNetworkDisconnectTimestamp = mClock.getWallClockMillis();
}
int getMaxDhcpRetries() {
@@ -3251,16 +3197,36 @@
DEFAULT_MAX_DHCP_RETRIES);
}
+ /**
+ * Clear BSSID blacklist in wpa_supplicant & HAL.
+ */
void clearBssidBlacklist() {
- mWifiConfigStore.clearBssidBlacklist();
+ mBssidBlacklist.clear();
+ mWifiSupplicantControl.clearBssidBlacklist();
}
+ /**
+ * Add a BSSID to the blacklist.
+ *
+ * @param bssid to be added.
+ */
void blackListBssid(String bssid) {
- mWifiConfigStore.blackListBssid(bssid);
+ if (TextUtils.isEmpty(bssid)) {
+ return;
+ }
+ mBssidBlacklist.add(bssid);
+ mWifiSupplicantControl.blackListBssid(bssid,
+ mBssidBlacklist.toArray(new String[mBssidBlacklist.size()]));
}
+ /**
+ * Checks if the provided bssid is blacklisted or not.
+ *
+ * @param bssid bssid to be checked.
+ * @return true if present, false otherwise.
+ */
public boolean isBssidBlacklisted(String bssid) {
- return mWifiConfigStore.isBssidBlacklisted(bssid);
+ return mBssidBlacklist.contains(bssid);
}
public boolean getEnableAutoJoinWhenAssociated() {
@@ -3271,12 +3237,6 @@
mEnableAutoJoinWhenAssociated.set(enabled);
}
- public void setActiveScanDetail(ScanDetail activeScanDetail) {
- synchronized (mActiveScanDetailLock) {
- mActiveScanDetail = activeScanDetail;
- }
- }
-
/**
* Check if the provided ephemeral network was deleted by the user or not.
* @param ssid ssid of the network
diff --git a/service/java/com/android/server/wifi/WifiConfigManagerNew.java b/service/java/com/android/server/wifi/WifiConfigManagerNew.java
new file mode 100644
index 0000000..41e66bd
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiConfigManagerNew.java
@@ -0,0 +1,1341 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi;
+
+import android.app.ActivityManager;
+import android.app.admin.DeviceAdminInfo;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.IpConfiguration;
+import android.net.ProxyInfo;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.WifiManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.wifi.util.ScanResultUtil;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This class provides the APIs to manage configured Wi-Fi networks.
+ * It deals with the following:
+ * - Maintaining a list of configured networks for quick access.
+ * - Persisting the configurations to store when required.
+ * - Supporting WifiManager Public API calls:
+ * > addOrUpdateNetwork()
+ * > removeNetwork()
+ * > enableNetwork()
+ * > disableNetwork()
+ * - Handle user switching on multi-user devices.
+ *
+ * All network configurations retrieved from this class are copies of the original configuration
+ * stored in the internal database. So, any updates to the retrieved configuration object are
+ * meaningless and will not be reflected in the original database.
+ * This is done on purpose to ensure that only WifiConfigManager can modify configurations stored
+ * in the internal database. Any configuration updates should be triggered with appropriate helper
+ * methods of this class using the configuration's unique networkId.
+ *
+ * NOTE: These API's are not thread safe and should only be used from WifiStateMachine thread.
+ */
+public class WifiConfigManagerNew {
+ /**
+ * String used to mask passwords to public interface.
+ */
+ @VisibleForTesting
+ public static final String PASSWORD_MASK = "*";
+ /**
+ * Network Selection disable reason thresholds. These numbers are used to debounce network
+ * failures before we disable them.
+ * These are indexed using the disable reason constants defined in
+ * {@link android.net.wifi.WifiConfiguration.NetworkSelectionStatus}.
+ */
+ @VisibleForTesting
+ public static final int[] NETWORK_SELECTION_DISABLE_THRESHOLD = {
+ -1, // threshold for NETWORK_SELECTION_ENABLE
+ 1, // threshold for DISABLED_BAD_LINK
+ 5, // threshold for DISABLED_ASSOCIATION_REJECTION
+ 5, // threshold for DISABLED_AUTHENTICATION_FAILURE
+ 5, // threshold for DISABLED_DHCP_FAILURE
+ 5, // threshold for DISABLED_DNS_FAILURE
+ 1, // threshold for DISABLED_WPS_START
+ 6, // threshold for DISABLED_TLS_VERSION_MISMATCH
+ 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
+ };
+ /**
+ * Network Selection disable timeout for each kind of error. After the timeout milliseconds,
+ * enable the network again.
+ * These are indexed using the disable reason constants defined in
+ * {@link android.net.wifi.WifiConfiguration.NetworkSelectionStatus}.
+ */
+ @VisibleForTesting
+ public static final int[] NETWORK_SELECTION_DISABLE_TIMEOUT_MS = {
+ Integer.MAX_VALUE, // threshold for NETWORK_SELECTION_ENABLE
+ 15 * 60 * 1000, // threshold for DISABLED_BAD_LINK
+ 5 * 60 * 1000, // threshold for DISABLED_ASSOCIATION_REJECTION
+ 5 * 60 * 1000, // threshold for DISABLED_AUTHENTICATION_FAILURE
+ 5 * 60 * 1000, // threshold for DISABLED_DHCP_FAILURE
+ 5 * 60 * 1000, // threshold for DISABLED_DNS_FAILURE
+ 0 * 60 * 1000, // threshold for DISABLED_WPS_START
+ Integer.MAX_VALUE, // threshold for DISABLED_TLS_VERSION
+ 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
+ };
+ /**
+ * Max size of scan details to cache in {@link #mScanDetailCaches}.
+ */
+ @VisibleForTesting
+ public static final int SCAN_CACHE_ENTRIES_MAX_SIZE = 192;
+ /**
+ * Once the size of the scan details in the cache {@link #mScanDetailCaches} exceeds
+ * {@link #SCAN_CACHE_ENTRIES_MAX_SIZE}, trim it down to this value so that we have some
+ * buffer time before the next eviction.
+ */
+ @VisibleForTesting
+ public static final int SCAN_CACHE_ENTRIES_TRIM_SIZE = 128;
+ /**
+ * Flags to be passed in for |canModifyNetwork| to decide if the change is minor and can
+ * bypass the lockdown checks.
+ */
+ private static final boolean ALLOW_LOCKDOWN_CHECK_BYPASS = true;
+ private static final boolean DISALLOW_LOCKDOWN_CHECK_BYPASS = false;
+ /**
+ * Log tag for this class.
+ */
+ private static final String TAG = "WifiConfigManagerNew";
+ /**
+ * List of external dependencies for WifiConfigManager.
+ */
+ private final Context mContext;
+ private final FrameworkFacade mFacade;
+ private final Clock mClock;
+ private final UserManager mUserManager;
+ private final BackupManagerProxy mBackupManagerProxy;
+ private final WifiConfigStoreNew mWifiConfigStore;
+ private final WifiKeyStore mWifiKeyStore;
+ /**
+ * Local log used for debugging any WifiConfigManager issues.
+ */
+ private final LocalLog mLocalLog =
+ new LocalLog(ActivityManager.isLowRamDeviceStatic() ? 128 : 256);
+ /**
+ * Map of configured networks with network id as the key.
+ */
+ private final ConfigurationMap mConfiguredNetworks;
+ /**
+ * Stores a map of NetworkId to ScanDetailCache.
+ */
+ private final ConcurrentHashMap<Integer, ScanDetailCache> mScanDetailCaches;
+ /**
+ * Framework keeps a list of ephemeral SSIDs that where deleted by user,
+ * so as, framework knows not to autoconnect again those SSIDs based on scorer input.
+ * The list is never cleared up.
+ * The SSIDs are encoded in a String as per definition of WifiConfiguration.SSID field.
+ */
+ private final Set<String> mDeletedEphemeralSSIDs;
+
+ /**
+ * Verbose logging flag. Toggled by developer options.
+ */
+ private boolean mVerboseLoggingEnabled = false;
+ /**
+ * Current logged in user ID.
+ */
+ private int mCurrentUserId = UserHandle.USER_SYSTEM;
+ /**
+ * This is keeping track of the last network ID assigned. Any new networks will be assigned
+ * |mLastNetworkId + 1| as network ID.
+ */
+ private int mLastNetworkId;
+
+ /**
+ * Create new instance of WifiConfigManager.
+ */
+ WifiConfigManagerNew(
+ Context context, FrameworkFacade facade, Clock clock, UserManager userManager,
+ WifiKeyStore wifiKeyStore, WifiConfigStoreNew wifiConfigStore) {
+ mContext = context;
+ mFacade = facade;
+ mClock = clock;
+ mUserManager = userManager;
+ mBackupManagerProxy = new BackupManagerProxy();
+ mWifiConfigStore = wifiConfigStore;
+ mWifiKeyStore = wifiKeyStore;
+
+ mConfiguredNetworks = new ConfigurationMap(userManager);
+ mScanDetailCaches = new ConcurrentHashMap<>(16, 0.75f, 2);
+ mDeletedEphemeralSSIDs = new HashSet<String>();
+ }
+
+ /**
+ * Construct the string to be put in the |creationTime| & |updateTime| elements of
+ * WifiConfiguration from the provided wall clock millis.
+ *
+ * @param wallClockMillis Time in milliseconds to be converted to string.
+ */
+ @VisibleForTesting
+ public static String createDebugTimeStampString(long wallClockMillis) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("time=");
+ Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(wallClockMillis);
+ sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
+ return sb.toString();
+ }
+
+ /**
+ * Enable/disable verbose logging in WifiConfigManager & its helper classes.
+ */
+ public void enableVerboseLogging(int verbose) {
+ if (verbose > 0) {
+ mVerboseLoggingEnabled = true;
+ } else {
+ mVerboseLoggingEnabled = false;
+ }
+ mWifiConfigStore.enableVerboseLogging(mVerboseLoggingEnabled);
+ mWifiKeyStore.enableVerboseLogging(mVerboseLoggingEnabled);
+ }
+
+ /**
+ * Helper method to mask all passwords/keys from the provided WifiConfiguration object. This
+ * is needed when the network configurations are being requested via the public WifiManager
+ * API's.
+ * This currently masks the following elements: psk, wepKeys & enterprise config password.
+ */
+ private void maskPasswordsInWifiConfiguration(WifiConfiguration configuration) {
+ if (!TextUtils.isEmpty(configuration.preSharedKey)) {
+ configuration.preSharedKey = PASSWORD_MASK;
+ }
+ if (configuration.wepKeys != null) {
+ for (int i = 0; i < configuration.wepKeys.length; i++) {
+ if (!TextUtils.isEmpty(configuration.wepKeys[i])) {
+ configuration.wepKeys[i] = PASSWORD_MASK;
+ }
+ }
+ }
+ if (!TextUtils.isEmpty(configuration.enterpriseConfig.getPassword())) {
+ configuration.enterpriseConfig.setPassword(PASSWORD_MASK);
+ }
+ }
+
+ /**
+ * Fetch the list of currently configured networks maintained in WifiConfigManager.
+ *
+ * This retrieves a copy of the internal configurations maintained by WifiConfigManager and
+ * should be used for any public interfaces.
+ *
+ * @param savedOnly Retrieve only saved networks.
+ * @param maskPasswords Mask passwords or not.
+ * @return List of WifiConfiguration objects representing the networks.
+ */
+ private List<WifiConfiguration> getConfiguredNetworks(
+ boolean savedOnly, boolean maskPasswords) {
+ List<WifiConfiguration> networks = new ArrayList<>();
+ for (WifiConfiguration config : getInternalConfiguredNetworks()) {
+ if (savedOnly && config.ephemeral) {
+ continue;
+ }
+ WifiConfiguration newConfig = new WifiConfiguration(config);
+ if (maskPasswords) {
+ maskPasswordsInWifiConfiguration(newConfig);
+ }
+ networks.add(newConfig);
+ }
+ return networks;
+ }
+
+ /**
+ * Retrieves the list of all configured networks with passwords masked.
+ *
+ * @return List of WifiConfiguration objects representing the networks.
+ */
+ public List<WifiConfiguration> getConfiguredNetworks() {
+ return getConfiguredNetworks(false, true);
+ }
+
+ /**
+ * Retrieves the list of all configured networks with the passwords in plaintext.
+ *
+ * WARNING: Don't use this to pass network configurations to external apps. Should only be
+ * sent to system apps/wifi stack, when there is a need for passwords in plaintext.
+ * TODO: Need to understand the current use case of this API.
+ *
+ * @return List of WifiConfiguration objects representing the networks.
+ */
+ public List<WifiConfiguration> getConfiguredNetworksWithPasswords() {
+ return getConfiguredNetworks(false, false);
+ }
+
+ /**
+ * Retrieves the list of all configured networks with the passwords masked.
+ *
+ * @return List of WifiConfiguration objects representing the networks.
+ */
+ public List<WifiConfiguration> getSavedNetworks() {
+ return getConfiguredNetworks(true, true);
+ }
+
+ /**
+ * Retrieves the configured network corresponding to the provided networkId with password
+ * masked.
+ *
+ * @param networkId networkId of the requested network.
+ * @return WifiConfiguration object if found, null otherwise.
+ */
+ public WifiConfiguration getConfiguredNetwork(int networkId) {
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ return null;
+ }
+ // Create a new configuration object with the passwords masked to send out to the external
+ // world.
+ WifiConfiguration network = new WifiConfiguration(config);
+ maskPasswordsInWifiConfiguration(network);
+ return network;
+ }
+
+ /**
+ * Retrieves the configured network corresponding to the provided networkId with password
+ * in plaintext.
+ *
+ * WARNING: Don't use this to pass network configurations to external apps. Should only be
+ * sent to system apps/wifi stack, when there is a need for passwords in plaintext.
+ *
+ * @param networkId networkId of the requested network.
+ * @return WifiConfiguration object if found, null otherwise.
+ */
+ public WifiConfiguration getConfiguredNetworkWithPassword(int networkId) {
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ return null;
+ }
+ // Create a new configuration object without the passwords masked to send out to the
+ // external world.
+ WifiConfiguration network = new WifiConfiguration(config);
+ return network;
+ }
+
+ /**
+ * Helper method to retrieve all the internal WifiConfiguration objects corresponding to all
+ * the networks in our database.
+ */
+ private Collection<WifiConfiguration> getInternalConfiguredNetworks() {
+ return mConfiguredNetworks.valuesForCurrentUser();
+ }
+
+ /**
+ * Helper method to retrieve the internal WifiConfiguration object corresponding to the
+ * provided configuration in our database.
+ * This first attempts to find the network using the provided network ID in configuration,
+ * else it attempts to find a matching configuration using the configKey.
+ */
+ private WifiConfiguration getInternalConfiguredNetwork(WifiConfiguration config) {
+ WifiConfiguration internalConfig = mConfiguredNetworks.getForCurrentUser(config.networkId);
+ if (internalConfig != null) {
+ return internalConfig;
+ }
+ return mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey());
+ }
+
+ /**
+ * Helper method to retrieve the internal WifiConfiguration object corresponding to the
+ * provided network ID in our database.
+ */
+ private WifiConfiguration getInternalConfiguredNetwork(int networkId) {
+ return mConfiguredNetworks.getForCurrentUser(networkId);
+ }
+
+ /**
+ * Helper method to check if the network is already configured internally or not.
+ */
+ private boolean isNetworkConfiguredInternally(WifiConfiguration config) {
+ return getInternalConfiguredNetwork(config) != null;
+ }
+
+ /**
+ * Helper method to check if the network is already configured internally or not.
+ */
+ private boolean isNetworkConfiguredInternally(int networkId) {
+ return getInternalConfiguredNetwork(networkId) != null;
+ }
+
+ /**
+ * Method to send out the configured networks change broadcast when a single network
+ * configuration is changed.
+ *
+ * @param network WifiConfiguration corresponding to the network that was changed.
+ * @param reason The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED,
+ * WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE.
+ */
+ private void sendConfiguredNetworkChangedBroadcast(
+ WifiConfiguration network, int reason) {
+ Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
+ // Create a new WifiConfiguration with passwords masked before we send it out.
+ WifiConfiguration broadcastNetwork = new WifiConfiguration(network);
+ maskPasswordsInWifiConfiguration(broadcastNetwork);
+ intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, broadcastNetwork);
+ intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ /**
+ * Method to send out the configured networks change broadcast when multiple network
+ * configurations are changed.
+ */
+ private void sendConfiguredNetworksChangedBroadcast() {
+ Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ /**
+ * Checks if the app has the permission to override Wi-Fi network configuration or not.
+ *
+ * @param uid uid of the app.
+ * @return true if the app does have the permission, false otherwise.
+ */
+ private boolean checkConfigOverridePermission(int uid) {
+ try {
+ int permission =
+ mFacade.checkUidPermission(
+ android.Manifest.permission.OVERRIDE_WIFI_CONFIG, uid);
+ return (permission == PackageManager.PERMISSION_GRANTED);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error checking for permission " + e);
+ return false;
+ }
+ }
+
+ /**
+ * Checks if |uid| has permission to modify the provided configuration.
+ *
+ * @param config WifiConfiguration object corresponding to the network to be modified.
+ * @param uid UID of the app requesting the modification.
+ * @param ignoreLockdown Ignore the configuration lockdown checks for debug data updates.
+ */
+ private boolean canModifyNetwork(WifiConfiguration config, int uid, boolean ignoreLockdown) {
+ final DevicePolicyManagerInternal dpmi = LocalServices.getService(
+ DevicePolicyManagerInternal.class);
+
+ final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
+ DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+ // If |uid| corresponds to the device owner, allow all modifications.
+ if (isUidDeviceOwner) {
+ return true;
+ }
+
+ final boolean isCreator = (config.creatorUid == uid);
+
+ // Check if the |uid| is either the creator of the network or holds the
+ // |OVERRIDE_CONFIG_WIFI| permission if the caller asks us to bypass the lockdown checks.
+ if (ignoreLockdown) {
+ return isCreator || checkConfigOverridePermission(uid);
+ }
+
+ // Check if device has DPM capability. If it has and |dpmi| is still null, then we
+ // treat this case with suspicion and bail out.
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)
+ && dpmi == null) {
+ Log.w(TAG, "Error retrieving DPMI service.");
+ return false;
+ }
+
+ // WiFi config lockdown related logic. At this point we know uid is NOT a Device Owner.
+
+ final boolean isConfigEligibleForLockdown = dpmi != null && dpmi.isActiveAdminWithPolicy(
+ config.creatorUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ if (!isConfigEligibleForLockdown) {
+ return isCreator || checkConfigOverridePermission(uid);
+ }
+
+ final ContentResolver resolver = mContext.getContentResolver();
+ final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
+ Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
+ return !isLockdownFeatureEnabled && checkConfigOverridePermission(uid);
+ }
+
+ /**
+ * Copy over public elements from an external WifiConfiguration object to the internal
+ * configuration object if element has been set in the provided external WifiConfiguration.
+ * The only exception is the hidden |IpConfiguration| parameters, these need to be copied over
+ * for every update.
+ *
+ * This method updates all elements that are common to both network addition & update.
+ * The following fields of {@link WifiConfiguration} are not copied from external configs:
+ * > networkId - These are allocated by Wi-Fi stack internally for any new configurations.
+ * > status - The status needs to be explicitly updated using
+ * {@link WifiManager#enableNetwork(int, boolean)} or
+ * {@link WifiManager#disableNetwork(int)}.
+ *
+ * @param externalConfig WifiConfiguration object provided from the external API.
+ * @param internalConfig WifiConfiguration object in our internal map.
+ */
+ private void mergeWithInternalWifiConfiguration(
+ WifiConfiguration externalConfig, WifiConfiguration internalConfig) {
+ if (externalConfig.SSID != null) {
+ internalConfig.SSID = externalConfig.SSID;
+ }
+ if (externalConfig.BSSID != null) {
+ internalConfig.BSSID = externalConfig.BSSID;
+ }
+ internalConfig.hiddenSSID = externalConfig.hiddenSSID;
+ if (externalConfig.preSharedKey != null) {
+ internalConfig.preSharedKey = externalConfig.preSharedKey;
+ }
+ // Modify only wep keys are present in the provided configuration. This is a little tricky
+ // because there is no easy way to tell if the app is actually trying to null out the
+ // existing keys or not.
+ if (externalConfig.wepKeys != null) {
+ boolean hasWepKey = false;
+ for (int i = 0; i < internalConfig.wepKeys.length; i++) {
+ if (externalConfig.wepKeys[i] != null) {
+ internalConfig.wepKeys[i] = externalConfig.wepKeys[i];
+ hasWepKey = true;
+ }
+ }
+ if (hasWepKey) {
+ internalConfig.wepTxKeyIndex = externalConfig.wepTxKeyIndex;
+ }
+ }
+ if (externalConfig.FQDN != null) {
+ internalConfig.FQDN = externalConfig.FQDN;
+ }
+ if (externalConfig.providerFriendlyName != null) {
+ internalConfig.providerFriendlyName = externalConfig.providerFriendlyName;
+ }
+ if (externalConfig.roamingConsortiumIds != null) {
+ internalConfig.roamingConsortiumIds = externalConfig.roamingConsortiumIds;
+ }
+
+ // Copy over all the auth/protocol/key mgmt parameters if set.
+ if (externalConfig.allowedAuthAlgorithms != null
+ && !externalConfig.allowedAuthAlgorithms.isEmpty()) {
+ internalConfig.allowedAuthAlgorithms = externalConfig.allowedAuthAlgorithms;
+ }
+ if (externalConfig.allowedProtocols != null
+ && !externalConfig.allowedProtocols.isEmpty()) {
+ internalConfig.allowedProtocols = externalConfig.allowedProtocols;
+ }
+ if (externalConfig.allowedKeyManagement != null
+ && !externalConfig.allowedKeyManagement.isEmpty()) {
+ internalConfig.allowedKeyManagement = externalConfig.allowedKeyManagement;
+ }
+ if (externalConfig.allowedPairwiseCiphers != null
+ && !externalConfig.allowedPairwiseCiphers.isEmpty()) {
+ internalConfig.allowedPairwiseCiphers = externalConfig.allowedPairwiseCiphers;
+ }
+ if (externalConfig.allowedGroupCiphers != null
+ && !externalConfig.allowedGroupCiphers.isEmpty()) {
+ internalConfig.allowedGroupCiphers = externalConfig.allowedGroupCiphers;
+ }
+
+ // Copy over the |IpConfiguration| parameters if set.
+ if (externalConfig.getIpConfiguration() != null) {
+ if (externalConfig.getIpAssignment() != IpConfiguration.IpAssignment.UNASSIGNED) {
+ internalConfig.setIpAssignment(externalConfig.getIpAssignment());
+ internalConfig.setStaticIpConfiguration(externalConfig.getStaticIpConfiguration());
+ }
+ if (externalConfig.getProxySettings() != IpConfiguration.ProxySettings.UNASSIGNED) {
+ internalConfig.setProxySettings(externalConfig.getProxySettings());
+ internalConfig.setHttpProxy(externalConfig.getHttpProxy());
+ }
+ }
+
+ // Copy over the |WifiEnterpriseConfig| parameters if set.
+ if (externalConfig.enterpriseConfig != null) {
+ internalConfig.enterpriseConfig =
+ new WifiEnterpriseConfig(externalConfig.enterpriseConfig);
+ }
+ }
+
+ /**
+ * Set all the exposed defaults in the newly created WifiConfiguration object.
+ * These fields have a default value advertised in our public documentation. The only exception
+ * is the hidden |IpConfiguration| parameters, these have a default value even though they're
+ * hidden.
+ *
+ * @param configuration provided WifiConfiguration object.
+ */
+ private void setDefaultsInWifiConfiguration(WifiConfiguration configuration) {
+ configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
+ configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
+
+ configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
+ configuration.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
+
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+
+ configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+ configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
+
+ configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
+ configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
+ configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
+ configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
+
+ configuration.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
+ configuration.setProxySettings(IpConfiguration.ProxySettings.NONE);
+ }
+
+ /**
+ * Create a new WifiConfiguration object by copying over parameters from the provided
+ * external configuration and set defaults for the appropriate parameters.
+ *
+ * @param config provided external WifiConfiguration object.
+ * @return New WifiConfiguration object with parameters merged from the provided external
+ * configuration.
+ */
+ private WifiConfiguration createNewInternalWifiConfigurationFromExternal(
+ WifiConfiguration config, int uid) {
+ WifiConfiguration newConfig = new WifiConfiguration();
+
+ // First allocate a new network ID for the configuration.
+ newConfig.networkId = mLastNetworkId++;
+
+ // First set defaults in the new configuration created.
+ setDefaultsInWifiConfiguration(newConfig);
+
+ // Copy over all the public elements from the provided configuration.
+ mergeWithInternalWifiConfiguration(config, newConfig);
+
+ // Copy over the hidden configuration parameters. These are the only parameters used by
+ // system apps to indicate some property about the network being added.
+ // These are only copied over for network additions and ignored for network updates.
+ newConfig.requirePMF = config.requirePMF;
+ newConfig.noInternetAccessExpected = config.noInternetAccessExpected;
+ newConfig.ephemeral = config.ephemeral;
+ newConfig.meteredHint = config.meteredHint;
+ newConfig.useExternalScores = config.useExternalScores;
+ newConfig.shared = config.shared;
+
+ // Add debug information for network addition.
+ newConfig.creatorUid = newConfig.lastUpdateUid = uid;
+ newConfig.creatorName = newConfig.lastUpdateName =
+ mContext.getPackageManager().getNameForUid(uid);
+ newConfig.creationTime = newConfig.updateTime =
+ createDebugTimeStampString(mClock.getWallClockMillis());
+
+ return newConfig;
+ }
+
+ /**
+ * Merges the provided external WifiConfiguration object with the existing internal
+ * WifiConfiguration object.
+ *
+ * @param config provided external WifiConfiguration object.
+ * @return Existing WifiConfiguration object with parameters merged from the provided
+ * configuration.
+ */
+ private WifiConfiguration updateExistingInternalWifiConfigurationFromExternal(
+ WifiConfiguration config, int uid) {
+ WifiConfiguration existingConfig = getInternalConfiguredNetwork(config);
+
+ // Copy over all the public elements from the provided configuration.
+ mergeWithInternalWifiConfiguration(config, existingConfig);
+
+ // Add debug information for network update.
+ existingConfig.lastUpdateUid = uid;
+ existingConfig.lastUpdateName = mContext.getPackageManager().getNameForUid(uid);
+ existingConfig.updateTime = createDebugTimeStampString(mClock.getWallClockMillis());
+
+ return existingConfig;
+ }
+
+ /**
+ * Compare existing and new IpConfiguration and return if IP assignment has changed or not.
+ *
+ * @param existingConfig Existing config corresponding to the network already stored in our
+ * database.
+ * @param newConfig New updated config object in our database.
+ * @return true if IP assignment have changed, false otherwise.
+ */
+ private boolean hasIpChanged(WifiConfiguration existingConfig, WifiConfiguration newConfig) {
+ switch (newConfig.getIpAssignment()) {
+ case STATIC:
+ if (existingConfig.getIpAssignment() != newConfig.getIpAssignment()) {
+ return true;
+ } else {
+ return !Objects.equals(
+ existingConfig.getStaticIpConfiguration(),
+ newConfig.getStaticIpConfiguration());
+ }
+ case DHCP:
+ return (existingConfig.getIpAssignment() != newConfig.getIpAssignment());
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Compare existing and new IpConfiguration and return if Proxy settings has changed or not.
+ *
+ * @param existingConfig Existing config corresponding to the network already stored in our
+ * database.
+ * @param newConfig New updated config object in our database.
+ * @return true if proxy settings have changed, false otherwise.
+ */
+ private boolean hasProxyChanged(WifiConfiguration existingConfig, WifiConfiguration newConfig) {
+ switch (newConfig.getProxySettings()) {
+ case STATIC:
+ case PAC:
+ ProxyInfo newHttpProxy = newConfig.getHttpProxy();
+ ProxyInfo currentHttpProxy = existingConfig.getHttpProxy();
+ if (newHttpProxy != null) {
+ return !newHttpProxy.equals(currentHttpProxy);
+ } else {
+ return (currentHttpProxy != null);
+ }
+ case NONE:
+ return (existingConfig.getProxySettings() != newConfig.getProxySettings());
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Add a network or update a network configuration to our database.
+ * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
+ * network configuration. Otherwise, the networkId should refer to an existing configuration.
+ *
+ * @param config provided WifiConfiguration object.
+ * @param uid UID of the app requesting the network addition/deletion.
+ * @return NetworkUpdateResult object representing status of the update.
+ */
+ private NetworkUpdateResult addOrUpdateNetworkInternal(WifiConfiguration config, int uid) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Adding/Updating network " + config.getPrintableSsid());
+ }
+ boolean newNetwork;
+ WifiConfiguration existingInternalConfig;
+ WifiConfiguration newInternalConfig;
+
+ if (!isNetworkConfiguredInternally(config)) {
+ newNetwork = true;
+ existingInternalConfig = null;
+ newInternalConfig = createNewInternalWifiConfigurationFromExternal(config, uid);
+ } else {
+ newNetwork = false;
+ existingInternalConfig =
+ new WifiConfiguration(getInternalConfiguredNetwork(config));
+ // 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 "
+ + config.configKey());
+ return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
+ }
+ newInternalConfig = updateExistingInternalWifiConfigurationFromExternal(config, uid);
+ }
+
+ // Update the keys for enterprise networks.
+ if (config.enterpriseConfig != null
+ && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
+ if (!(mWifiKeyStore.updateNetworkKeys(config, existingInternalConfig))) {
+ return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
+ }
+ }
+
+ // Add it our internal map.
+ mConfiguredNetworks.put(newInternalConfig);
+
+ // Stage the backup of the SettingsProvider package which backs this up.
+ mBackupManagerProxy.notifyDataChanged();
+
+ // This is needed to inform IpManager about any IP configuration changes.
+ boolean hasIpChanged =
+ newNetwork || hasIpChanged(existingInternalConfig, newInternalConfig);
+ boolean hasProxyChanged =
+ newNetwork || hasProxyChanged(existingInternalConfig, newInternalConfig);
+ NetworkUpdateResult result = new NetworkUpdateResult(hasIpChanged, hasProxyChanged);
+ result.setIsNewNetwork(newNetwork);
+ result.setNetworkId(newInternalConfig.networkId);
+
+ localLog("addOrUpdateNetworkInternal: added/updated config."
+ + " netId=" + newInternalConfig.networkId
+ + " configKey=" + newInternalConfig.configKey()
+ + " uid=" + Integer.toString(newInternalConfig.creatorUid)
+ + " name=" + newInternalConfig.creatorName);
+ return result;
+ }
+
+ /**
+ * Add a network or update a network configuration to our database.
+ * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
+ * network configuration. Otherwise, the networkId should refer to an existing configuration.
+ *
+ * @param config provided WifiConfiguration object.
+ * @param uid UID of the app requesting the network addition/deletion.
+ * @return NetworkUpdateResult object representing status of the update.
+ */
+ public NetworkUpdateResult addOrUpdateNetwork(WifiConfiguration config, int uid) {
+ if (config == null) {
+ Log.e(TAG, "Cannot add/update network with null config");
+ return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
+ }
+ NetworkUpdateResult result = addOrUpdateNetworkInternal(config, uid);
+ if (!result.isSuccess()) {
+ Log.e(TAG, "Failed to add/update network " + config.getPrintableSsid());
+ return result;
+ }
+ WifiConfiguration newConfig = getInternalConfiguredNetwork(result.getNetworkId());
+ sendConfiguredNetworkChangedBroadcast(
+ newConfig,
+ result.isNewNetwork()
+ ? WifiManager.CHANGE_REASON_ADDED
+ : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
+ // External modification, persist it immediately.
+ saveToStore(true);
+ return result;
+ }
+
+ /**
+ * Removes the specified network configuration from our database.
+ *
+ * @param config provided WifiConfiguration object.
+ * @return true if successful, false otherwise.
+ */
+ private boolean removeNetworkInternal(WifiConfiguration config) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Removing network " + config.getPrintableSsid());
+ }
+ // Remove any associated enterprise keys.
+ if (config.enterpriseConfig != null
+ && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
+ mWifiKeyStore.removeKeys(config.enterpriseConfig);
+ }
+
+ mConfiguredNetworks.remove(config.networkId);
+ mScanDetailCaches.remove(config.networkId);
+ // Stage the backup of the SettingsProvider package which backs this up.
+ mBackupManagerProxy.notifyDataChanged();
+
+ localLog("removeNetworkInternal: removed config."
+ + " netId=" + config.networkId
+ + " configKey=" + config.configKey());
+ return true;
+ }
+
+ /**
+ * Removes the specified network configuration from our database.
+ *
+ * @param networkId network ID of the provided network.
+ * @return true if successful, false otherwise.
+ */
+ public boolean removeNetwork(int networkId) {
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ Log.e(TAG, "Cannot find network with networkId " + networkId);
+ return false;
+ }
+ if (!removeNetworkInternal(config)) {
+ Log.e(TAG, "Failed to remove network " + config.getPrintableSsid());
+ return false;
+ }
+ sendConfiguredNetworkChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
+ // External modification, persist it immediately.
+ saveToStore(true);
+ return true;
+ }
+
+ /**
+ * Helper method to mark a network enabled for network selection.
+ */
+ private void setNetworkSelectionEnabled(NetworkSelectionStatus status) {
+ status.setNetworkSelectionStatus(
+ NetworkSelectionStatus.NETWORK_SELECTION_ENABLED);
+ status.setDisableTime(
+ NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
+ status.setNetworkSelectionDisableReason(NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
+
+ // Clear out all the disable reason counters.
+ status.clearDisableReasonCounter();
+ }
+
+ /**
+ * Helper method to mark a network temporarily disabled for network selection.
+ */
+ private void setNetworkSelectionTemporarilyDisabled(
+ NetworkSelectionStatus status, int disableReason) {
+ status.setNetworkSelectionStatus(
+ NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED);
+ // Only need a valid time filled in for temporarily disabled networks.
+ status.setDisableTime(mClock.getElapsedSinceBootMillis());
+ status.setNetworkSelectionDisableReason(disableReason);
+ }
+
+ /**
+ * Helper method to mark a network permanently disabled for network selection.
+ */
+ private void setNetworkSelectionPermanentlyDisabled(
+ NetworkSelectionStatus status, int disableReason) {
+ status.setNetworkSelectionStatus(
+ NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
+ status.setDisableTime(
+ NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
+ status.setNetworkSelectionDisableReason(disableReason);
+ }
+
+ /**
+ * Helper method to set the publicly exposed status for the network and send out the network
+ * status change broadcast.
+ */
+ private void setNetworkStatus(WifiConfiguration config, int status) {
+ config.status = status;
+ sendConfiguredNetworkChangedBroadcast(config, WifiManager.CHANGE_REASON_CONFIG_CHANGE);
+ }
+
+ /**
+ * Sets a network's status (both internal and public) according to the update reason and
+ * its current state.
+ *
+ * This updates the network's {@link WifiConfiguration#mNetworkSelectionStatus} field and the
+ * public {@link WifiConfiguration#status} field if the network is either enabled or
+ * permanently disabled.
+ *
+ * @param config network to be updated.
+ * @param reason reason code for update.
+ * @return true if the input configuration has been updated, false otherwise.
+ */
+ private boolean setNetworkSelectionStatus(WifiConfiguration config, int reason) {
+ NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
+ if (reason < 0 || reason >= NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX) {
+ Log.e(TAG, "Invalid Network disable reason " + reason);
+ return false;
+ }
+ if (reason == NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
+ setNetworkSelectionEnabled(networkStatus);
+ setNetworkStatus(config, WifiConfiguration.Status.ENABLED);
+ } else if (reason < NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) {
+ setNetworkSelectionTemporarilyDisabled(networkStatus, reason);
+ } else {
+ setNetworkSelectionPermanentlyDisabled(networkStatus, reason);
+ setNetworkStatus(config, WifiConfiguration.Status.DISABLED);
+ }
+ localLog("setNetworkSelectionStatus: configKey=" + config.configKey()
+ + " networkStatus=" + networkStatus.getNetworkStatusString() + " disableReason="
+ + networkStatus.getNetworkDisableReasonString() + " at="
+ + createDebugTimeStampString(mClock.getWallClockMillis()));
+ return true;
+ }
+
+ /**
+ * Update a network's status (both internal and public) according to the update reason and
+ * its current state.
+ *
+ * @param config network to be updated.
+ * @param reason reason code for update.
+ * @return true if the input configuration has been updated, false otherwise.
+ */
+ private boolean updateNetworkSelectionStatus(WifiConfiguration config, int reason) {
+ NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
+ if (reason != NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
+ networkStatus.incrementDisableReasonCounter(reason);
+ // For network disable reasons, we should only update the status if we cross the
+ // threshold.
+ int disableReasonCounter = networkStatus.getDisableReasonCounter(reason);
+ int disableReasonThreshold = NETWORK_SELECTION_DISABLE_THRESHOLD[reason];
+ if (disableReasonCounter < disableReasonThreshold) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Disable counter for network " + config.getPrintableSsid()
+ + " for reason "
+ + NetworkSelectionStatus.getNetworkDisableReasonString(reason) + " is "
+ + networkStatus.getDisableReasonCounter(reason) + " and threshold is "
+ + disableReasonThreshold);
+ }
+ return true;
+ }
+ }
+ return setNetworkSelectionStatus(config, reason);
+ }
+
+ /**
+ * Update a network's status (both internal and public) according to the update reason and
+ * its current state.
+ *
+ * Each network has 2 status:
+ * 1. NetworkSelectionStatus: This is internal selection status of the network. This is used
+ * for temporarily disabling a network for QNS.
+ * 2. Status: This is the exposed status for a network. This is mostly set by
+ * the public API's {@link WifiManager#enableNetwork(int, boolean)} &
+ * {@link WifiManager#disableNetwork(int)}.
+ *
+ * @param networkId network ID of the network that needs the update.
+ * @param reason reason to update the network.
+ * @return true if the input configuration has been updated, false otherwise.
+ */
+ public boolean updateNetworkSelectionStatus(int networkId, int reason) {
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ Log.e(TAG, "Cannot find network with networkId " + networkId);
+ return false;
+ }
+ return updateNetworkSelectionStatus(config, reason);
+ }
+
+ /**
+ * Attempt to re-enable a network for network selection, if this network was either:
+ * a) Previously temporarily disabled, but its disable timeout has expired, or
+ * b) Previously disabled because of a user switch, but is now visible to the current
+ * user.
+ *
+ * @param config configuration for the network to be re-enabled for network selection. The
+ * network corresponding to the config must be visible to the current user.
+ * @return true if the network identified by {@param config} was re-enabled for qualified
+ * network selection, false otherwise.
+ */
+ private boolean tryEnableNetwork(WifiConfiguration config) {
+ NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
+ if (networkStatus.isNetworkTemporaryDisabled()) {
+ long timeDifferenceMs =
+ mClock.getElapsedSinceBootMillis() - networkStatus.getDisableTime();
+ int disableReason = networkStatus.getNetworkSelectionDisableReason();
+ long disableTimeoutMs = NETWORK_SELECTION_DISABLE_TIMEOUT_MS[disableReason];
+ if (timeDifferenceMs >= disableTimeoutMs) {
+ return updateNetworkSelectionStatus(
+ config, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
+ }
+ } else if (networkStatus.isDisabledByReason(
+ NetworkSelectionStatus.DISABLED_DUE_TO_USER_SWITCH)) {
+ return updateNetworkSelectionStatus(
+ config, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
+ }
+ return false;
+ }
+
+ /**
+ * Attempt to re-enable a network for network selection, if this network was either:
+ * a) Previously temporarily disabled, but its disable timeout has expired, or
+ * b) Previously disabled because of a user switch, but is now visible to the current
+ * user.
+ *
+ * @param networkId the id of the network to be checked for possible unblock (due to timeout)
+ * @return true if the network identified by {@param networkId} was re-enabled for qualified
+ * network selection, false otherwise.
+ */
+ public boolean tryEnableNetwork(int networkId) {
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ Log.e(TAG, "Cannot find network with networkId " + networkId);
+ return false;
+ }
+ return tryEnableNetwork(config);
+ }
+
+ /**
+ * Enable a network using the public {@link WifiManager#enableNetwork(int, boolean)} API.
+ *
+ * @param networkId network ID of the network that needs the update.
+ * @param uid uid of the app requesting the update.
+ * @return {@code true} if it succeeds, {@code false} otherwise
+ */
+ public boolean enableNetwork(int networkId, int uid) {
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ Log.e(TAG, "Cannot find network with networkId " + networkId);
+ return false;
+ }
+ if (!canModifyNetwork(config, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
+ Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
+ + config.configKey());
+ return false;
+ }
+ return updateNetworkSelectionStatus(
+ networkId, WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
+ }
+
+ /**
+ * Disable a network using the public {@link WifiManager#disableNetwork(int)} API.
+ *
+ * @param networkId network ID of the network that needs the update.
+ * @param uid uid of the app requesting the update.
+ * @return {@code true} if it succeeds, {@code false} otherwise
+ */
+ public boolean disableNetwork(int networkId, int uid) {
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ Log.e(TAG, "Cannot find network with networkId " + networkId);
+ return false;
+ }
+ if (!canModifyNetwork(config, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
+ Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
+ + config.configKey());
+ return false;
+ }
+ return updateNetworkSelectionStatus(
+ networkId, NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER);
+ }
+
+ /**
+ * Checks if the |uid| has the necessary permission to override wifi config and updates the last
+ * connected UID for the provided configuration.
+ *
+ * @param networkId network ID corresponding to the network.
+ * @param uid uid of the app requesting the connection.
+ * @return {@code true} if |uid| has the necessary permission to trigger connection to the
+ * network, {@code false} otherwise.
+ */
+ public boolean checkAndUpdateLastConnectUid(int networkId, int uid) {
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ Log.e(TAG, "Cannot find network with networkId " + networkId);
+ return false;
+ }
+ if (!canModifyNetwork(config, uid, ALLOW_LOCKDOWN_CHECK_BYPASS)) {
+ Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
+ + config.configKey());
+ return false;
+ }
+ config.lastConnectUid = uid;
+ return true;
+ }
+
+ /**
+ * Helper method to get the scan detail cache entry {@link #mScanDetailCaches} for the provided
+ * network.
+ *
+ * @param networkId network ID corresponding to the network.
+ * @return existing {@link ScanDetailCache} entry if one exists or null.
+ */
+ @VisibleForTesting
+ public ScanDetailCache getScanDetailCacheForNetwork(int networkId) {
+ return mScanDetailCaches.get(networkId);
+ }
+
+ /**
+ * Helper method to get or create a scan detail cache entry {@link #mScanDetailCaches} for
+ * the provided network.
+ *
+ * @param config configuration corresponding to the the network.
+ * @return existing {@link ScanDetailCache} entry if one exists or a new instance created for
+ * this network.
+ */
+ private ScanDetailCache getOrCreateScanDetailCacheForNetwork(WifiConfiguration config) {
+ if (config == null) return null;
+ ScanDetailCache cache = getScanDetailCacheForNetwork(config.networkId);
+ if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
+ cache =
+ new ScanDetailCache(
+ config, SCAN_CACHE_ENTRIES_MAX_SIZE, SCAN_CACHE_ENTRIES_TRIM_SIZE);
+ mScanDetailCaches.put(config.networkId, cache);
+ }
+ return cache;
+ }
+
+ /**
+ * Saves the provided ScanDetail into the corresponding scan detail cache entry
+ * {@link #mScanDetailCaches} for the provided network.
+ *
+ * @param config configuration corresponding to the the network.
+ * @param scanDetail new scan detail instance to be saved into the cache.
+ */
+ private void saveToScanDetailCacheForNetwork(
+ WifiConfiguration config, ScanDetail scanDetail) {
+ ScanResult scanResult = scanDetail.getScanResult();
+
+ ScanDetailCache scanDetailCache = getOrCreateScanDetailCacheForNetwork(config);
+ if (scanDetailCache == null) {
+ Log.e(TAG, "Could not allocate scan cache for " + config.getPrintableSsid());
+ return;
+ }
+
+ // Adding a new BSSID
+ ScanResult result = scanDetailCache.get(scanResult.BSSID);
+ if (result != null) {
+ // transfer the black list status
+ scanResult.blackListTimestamp = result.blackListTimestamp;
+ scanResult.numIpConfigFailures = result.numIpConfigFailures;
+ scanResult.numConnection = result.numConnection;
+ scanResult.isAutoJoinCandidate = result.isAutoJoinCandidate;
+ }
+ if (config.ephemeral) {
+ // For an ephemeral Wi-Fi config, the ScanResult should be considered
+ // untrusted.
+ scanResult.untrusted = true;
+ }
+
+ // Add the scan detail to this network's scan detail cache.
+ scanDetailCache.put(scanDetail);
+
+ // Since we added a scan result to this configuration, re-attempt linking
+ // TODO: linkConfiguration(config);
+ }
+
+ /**
+ * Retrieves a saved 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) {
+ ScanResult scanResult = scanDetail.getScanResult();
+ if (scanResult == null) {
+ Log.e(TAG, "No scan result found in scan detail");
+ return null;
+ }
+ // Add the double quotes to the scan result SSID for comparison with the network configs.
+ String ssidToCompare = "\"" + scanResult.SSID + "\"";
+ for (WifiConfiguration config : getInternalConfiguredNetworks()) {
+ if (config.SSID == null || !config.SSID.equals(ssidToCompare)) {
+ continue;
+ }
+ if (ScanResultUtil.doesScanResultEncryptionMatchWithNetwork(scanResult, config)) {
+ localLog("getSavedNetworkFromScanDetail: Found " + config.configKey()
+ + " for " + scanResult.SSID + "[" + scanResult.capabilities + "]");
+ return config;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 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
+ * {@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);
+ if (network == null) {
+ return null;
+ }
+ saveToScanDetailCacheForNetwork(network, scanDetail);
+ return getConfiguredNetworkWithPassword(network.networkId);
+ }
+
+ /**
+ * Read the config store and load the in-memory lists from the store data retrieved.
+ * This reads all the network configurations from:
+ * 1. Shared WifiConfigStore.xml
+ * 2. User WifiConfigStore.xml
+ * 3. PerProviderSubscription.conf
+ */
+ private void loadFromStore() {
+ WifiConfigStoreData storeData;
+
+ long readStartTime = mClock.getElapsedSinceBootMillis();
+ try {
+ storeData = mWifiConfigStore.read();
+ } catch (Exception e) {
+ Log.wtf(TAG, "Reading from new store failed " + e + ". All saved networks are lost!");
+ return;
+ }
+ long readTime = mClock.getElapsedSinceBootMillis() - readStartTime;
+ Log.d(TAG, "Loading from store completed in " + readTime + " ms.");
+
+ // Clear out all the existing in-memory lists and load the lists from what was retrieved
+ // from the config store.
+ mConfiguredNetworks.clear();
+ mDeletedEphemeralSSIDs.clear();
+ mScanDetailCaches.clear();
+ for (WifiConfiguration configuration : storeData.configurations) {
+ configuration.networkId = mLastNetworkId++;
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Adding network from store " + configuration.configKey());
+ }
+ mConfiguredNetworks.put(configuration);
+ }
+ for (String ssid : storeData.deletedEphemeralSSIDs) {
+ mDeletedEphemeralSSIDs.add(ssid);
+ }
+ if (mConfiguredNetworks.sizeForAllUsers() == 0) {
+ Log.w(TAG, "No stored networks found.");
+ }
+ }
+
+ /**
+ * Save the current snapshot of the in-memory lists to the config store.
+ *
+ * @param forceWrite Whether the write needs to be forced or not.
+ * @return Whether the write was successful or not, this is applicable only for force writes.
+ */
+ private boolean saveToStore(boolean forceWrite) {
+ ArrayList<WifiConfiguration> configurations = new ArrayList<>();
+ // Don't persist ephemeral networks to store.
+ for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
+ if (!config.ephemeral) {
+ configurations.add(config);
+ }
+ }
+ WifiConfigStoreData storeData =
+ new WifiConfigStoreData(configurations, mDeletedEphemeralSSIDs);
+
+ long writeStartTime = mClock.getElapsedSinceBootMillis();
+ try {
+ mWifiConfigStore.write(forceWrite, storeData);
+ } catch (Exception e) {
+ Log.wtf(TAG, "Writing to store failed " + e + ". Saved networks maybe lost!");
+ return false;
+ }
+ long writeTime = mClock.getElapsedSinceBootMillis() - writeStartTime;
+ Log.d(TAG, "Writing to store completed in " + writeTime + " ms.");
+ return true;
+ }
+
+ /**
+ * Helper method for logging into local log buffer.
+ */
+ private void localLog(String s) {
+ if (mLocalLog != null) {
+ mLocalLog.log(s);
+ }
+ }
+
+ /**
+ * Dump the local log buffer.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("Dump of WifiConfigManager");
+ pw.println("WifiConfigManager - Log Begin ----");
+ mLocalLog.dump(fd, pw, args);
+ pw.println("WifiConfigManager - Log End ----");
+ }
+}
diff --git a/service/java/com/android/server/wifi/WifiConfigStoreData.java b/service/java/com/android/server/wifi/WifiConfigStoreData.java
new file mode 100644
index 0000000..4d1802f
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiConfigStoreData.java
@@ -0,0 +1,521 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi;
+
+import android.net.IpConfiguration;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.util.Pair;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+import com.android.server.wifi.util.XmlUtil;
+import com.android.server.wifi.util.XmlUtil.IpConfigurationXmlUtil;
+import com.android.server.wifi.util.XmlUtil.NetworkSelectionStatusXmlUtil;
+import com.android.server.wifi.util.XmlUtil.WifiConfigurationXmlUtil;
+import com.android.server.wifi.util.XmlUtil.WifiEnterpriseConfigXmlUtil;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Class to encapsulate all the data to be stored across all the stores. This is a snapshot
+ * of all the settings passed from {@link WifiConfigManager} to persistent store.
+ * Instances of this class are passed from/to WifiConfigManager and WifiConfigStore for
+ * writing/parsing data to/from the store files.
+ *
+ * Note: Nesting of objects during serialization makes it hard to deserialize data especially
+ * when we have elements added to the parent object in future revisions. So, when we serialize
+ * {@link WifiConfiguration} objects (representing saved networks), we add separate sections in the
+ * XML for each nested object (such as {@link IpConfiguration} and {@link NetworkSelectionStatus})
+ * within WifiConfiguration object.
+ */
+public class WifiConfigStoreData {
+ /**
+ * Current config store data version. This will be incremented for any additions.
+ */
+ private static final int CURRENT_CONFIG_STORE_DATA_VERSION = 1;
+ /** This list of older versions will be used to restore data from older config store. */
+ /**
+ * First version of the config store data format.
+ */
+ private static final int INITIAL_CONFIG_STORE_DATA_VERSION = 1;
+ /**
+ * List of XML section header tags in the config store data.
+ */
+ private static final String XML_TAG_DOCUMENT_HEADER = "WifiConfigStoreData";
+ private static final String XML_TAG_VERSION = "Version";
+ private static final String XML_TAG_SECTION_HEADER_NETWORK_LIST = "NetworkList";
+ private static final String XML_TAG_SECTION_HEADER_NETWORK = "Network";
+ private static final String XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION = "WifiConfiguration";
+ private static final String XML_TAG_SECTION_HEADER_NETWORK_STATUS = "NetworkStatus";
+ private static final String XML_TAG_SECTION_HEADER_IP_CONFIGURATION = "IpConfiguration";
+ private static final String XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION =
+ "WifiEnterpriseConfiguration";
+ private static final String XML_TAG_SECTION_HEADER_DELETED_EPHEMERAL_SSID_LIST =
+ "DeletedEphemeralSSIDList";
+ /**
+ * List of saved networks visible to the current user to be stored (includes shared & private).
+ */
+ public List<WifiConfiguration> configurations;
+ /**
+ * List of deleted ephemeral ssids to be stored.
+ */
+ public Set<String> deletedEphemeralSSIDs;
+
+ /**
+ * Create a new instance of store data to be written to the store files.
+ *
+ * @param configurations list of saved networks to be stored.
+ * See {@link WifiConfigManager#mConfiguredNetworks}.
+ * @param deletedEphemeralSSIDs list of deleted ephemeral ssids to be stored.
+ * See {@link WifiConfigManager#mDeletedEphemeralSSIDs}
+ */
+ public WifiConfigStoreData(
+ List<WifiConfiguration> configurations, Set<String> deletedEphemeralSSIDs) {
+ this.configurations = configurations;
+ this.deletedEphemeralSSIDs = deletedEphemeralSSIDs;
+ }
+
+ /**
+ * Create a new instance of the store data parsed from the store file data.
+ *
+ * Note: If any of the raw data is null or empty, will create an empty corresponding store data.
+ * This is to handle fresh install devices where these stores are not yet created.
+ *
+ * @param sharedDataBytes raw data retrieved from the shared store file.
+ * @param userDataBytes raw data retrieved from the user store file.
+ * @return new instance of store data.
+ */
+ public static WifiConfigStoreData parseRawData(byte[] sharedDataBytes, byte[] userDataBytes)
+ throws XmlPullParserException, IOException {
+ SharedData sharedData;
+ UserData userData;
+ try {
+ if (sharedDataBytes != null && sharedDataBytes.length > 0) {
+ sharedData = SharedData.parseRawData(sharedDataBytes);
+ } else {
+ sharedData = new SharedData(new ArrayList<WifiConfiguration>());
+ }
+ if (userDataBytes != null && userDataBytes.length > 0) {
+ userData = UserData.parseRawData(userDataBytes);
+ } else {
+ userData = new UserData(new ArrayList<WifiConfiguration>(), new HashSet<String>());
+ }
+ return getStoreData(sharedData, userData);
+ } catch (ClassCastException e) {
+ throw new XmlPullParserException("Wrong value type parsed: " + e);
+ }
+ }
+
+ /**
+ * Create a WifiConfigStoreData instance from the retrieved UserData & SharedData instance.
+ */
+ private static WifiConfigStoreData getStoreData(SharedData sharedData, UserData userData) {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ configurations.addAll(sharedData.configurations);
+ configurations.addAll(userData.configurations);
+ return new WifiConfigStoreData(configurations, userData.deletedEphemeralSSIDs);
+
+ }
+
+ /**
+ * Write the list of networks to the XML stream.
+ *
+ * @param out XmlSerializer instance pointing to the XML stream.
+ * @param configurations list of WifiConfiguration objects corresponding to the networks.
+ */
+ private static void writeNetworksToXml(
+ XmlSerializer out, List<WifiConfiguration> configurations)
+ throws XmlPullParserException, IOException {
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_NETWORK_LIST);
+ for (WifiConfiguration configuration : configurations) {
+ // Write this configuration data now.
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_NETWORK);
+ writeNetworkToXml(out, configuration);
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_NETWORK);
+ }
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_NETWORK_LIST);
+ }
+
+ /**
+ * Write a network to the XML stream.
+ * Nested objects within the provided WifiConfiguration object are written into separate XML
+ * sections.
+ *
+ * @param out XmlSerializer instance pointing to the XML stream.
+ * @param configuration WifiConfiguration object corresponding to the network.
+ */
+ private static void writeNetworkToXml(
+ XmlSerializer out, WifiConfiguration configuration)
+ throws XmlPullParserException, IOException {
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION);
+ WifiConfigurationXmlUtil.writeToXmlForConfigStore(out, configuration);
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION);
+
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_NETWORK_STATUS);
+ NetworkSelectionStatusXmlUtil.writeToXml(out, configuration.getNetworkSelectionStatus());
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_NETWORK_STATUS);
+
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_IP_CONFIGURATION);
+ IpConfigurationXmlUtil.writeToXml(out, configuration.getIpConfiguration());
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_IP_CONFIGURATION);
+
+ // Store the enterprise configuration for enterprise networks.
+ if (configuration.isEnterprise()) {
+ XmlUtil.writeNextSectionStart(
+ out, XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION);
+ WifiEnterpriseConfigXmlUtil.writeToXml(out, configuration.enterpriseConfig);
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION);
+ }
+ }
+
+ /**
+ * Parses the list of networks from the provided XML stream.
+ *
+ * @param in XmlPullParser instance pointing to the XML stream.
+ * @param outerTagDepth depth of the outer tag in the XML document.
+ * @param dataVersion version number parsed from incoming data.
+ * @return list of WifiConfiguration objects corresponding to the networks if parsing is
+ * successful, null otherwise.
+ */
+ private static List<WifiConfiguration> parseNetworksFromXml(
+ XmlPullParser in, int outerTagDepth, int dataVersion)
+ throws XmlPullParserException, IOException {
+ // Find the configuration list section.
+ XmlUtil.gotoNextSectionWithName(in, XML_TAG_SECTION_HEADER_NETWORK_LIST, outerTagDepth);
+ // Find all the configurations within the configuration list section.
+ int networkListTagDepth = outerTagDepth + 1;
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ while (XmlUtil.gotoNextSectionWithNameOrEnd(
+ in, XML_TAG_SECTION_HEADER_NETWORK, networkListTagDepth)) {
+ WifiConfiguration configuration =
+ parseNetworkFromXml(in, networkListTagDepth, dataVersion);
+ if (configuration != null) {
+ configurations.add(configuration);
+ }
+ }
+ return configurations;
+ }
+
+ /**
+ * Helper method to parse the WifiConfiguration object and validate the configKey parsed.
+ */
+ private static WifiConfiguration parseWifiConfigurationFromXmlAndValidateConfigKey(
+ XmlPullParser in, int outerTagDepth)
+ throws XmlPullParserException, IOException {
+ Pair<String, WifiConfiguration> parsedConfig =
+ WifiConfigurationXmlUtil.parseFromXml(in, outerTagDepth);
+ if (parsedConfig == null || parsedConfig.first == null || parsedConfig.second == null) {
+ throw new XmlPullParserException("XML parsing of wifi configuration failed");
+ }
+ String configKeyParsed = parsedConfig.first;
+ WifiConfiguration configuration = parsedConfig.second;
+ String configKeyCalculated = configuration.configKey();
+ if (!configKeyParsed.equals(configKeyCalculated)) {
+ throw new XmlPullParserException(
+ "Configuration key does not match. Retrieved: " + configKeyParsed
+ + ", Calculated: " + configKeyCalculated);
+ }
+ return configuration;
+ }
+
+ /**
+ * Parses a network from the provided XML stream.
+ *
+ * @param in XmlPullParser instance pointing to the XML stream.
+ * @param outerTagDepth depth of the outer tag in the XML document.
+ * @param dataVersion version number parsed from incoming data.
+ * @return WifiConfiguration object corresponding to the network if parsing is successful,
+ * null otherwise.
+ */
+ private static WifiConfiguration parseNetworkFromXml(
+ XmlPullParser in, int outerTagDepth, int dataVersion)
+ throws XmlPullParserException, IOException {
+ // Any version migration needs to be handled here in future.
+ if (dataVersion == INITIAL_CONFIG_STORE_DATA_VERSION) {
+ WifiConfiguration configuration = null;
+
+ int networkTagDepth = outerTagDepth + 1;
+ XmlUtil.gotoNextSectionWithName(
+ in, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION, networkTagDepth);
+ int configTagDepth = networkTagDepth + 1;
+ configuration = parseWifiConfigurationFromXmlAndValidateConfigKey(in, configTagDepth);
+
+ XmlUtil.gotoNextSectionWithName(
+ in, XML_TAG_SECTION_HEADER_NETWORK_STATUS, networkTagDepth);
+ NetworkSelectionStatus status =
+ NetworkSelectionStatusXmlUtil.parseFromXml(in, configTagDepth);
+ configuration.setNetworkSelectionStatus(status);
+
+ XmlUtil.gotoNextSectionWithName(
+ in, XML_TAG_SECTION_HEADER_IP_CONFIGURATION, networkTagDepth);
+ IpConfiguration ipConfiguration =
+ IpConfigurationXmlUtil.parseFromXml(in, configTagDepth);
+ configuration.setIpConfiguration(ipConfiguration);
+
+ // Check if there is an enterprise configuration section.
+ if (XmlUtil.gotoNextSectionWithNameOrEnd(
+ in, XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION, networkTagDepth)) {
+ WifiEnterpriseConfig enterpriseConfig =
+ WifiEnterpriseConfigXmlUtil.parseFromXml(in, configTagDepth);
+ configuration.enterpriseConfig = enterpriseConfig;
+ }
+
+ return configuration;
+ }
+ return null;
+ }
+
+ /**
+ * Write the document start and version to the XML stream.
+ * This is used for both the shared and user config store data.
+ *
+ * @param out XmlSerializer instance pointing to the XML stream.
+ */
+ private static void writeDocumentStartAndVersionToXml(XmlSerializer out)
+ throws XmlPullParserException, IOException {
+ XmlUtil.writeDocumentStart(out, XML_TAG_DOCUMENT_HEADER);
+ XmlUtil.writeNextValue(out, XML_TAG_VERSION, CURRENT_CONFIG_STORE_DATA_VERSION);
+ }
+
+ /**
+ * Parse the document start and version from the XML stream.
+ * This is used for both the shared and user config store data.
+ *
+ * @param in XmlPullParser instance pointing to the XML stream.
+ * @return version number retrieved from the Xml stream.
+ */
+ private static int parseDocumentStartAndVersionFromXml(XmlPullParser in)
+ throws XmlPullParserException, IOException {
+ XmlUtil.gotoDocumentStart(in, XML_TAG_DOCUMENT_HEADER);
+ int version = (int) XmlUtil.readNextValueWithName(in, XML_TAG_VERSION);
+ if (version < INITIAL_CONFIG_STORE_DATA_VERSION
+ || version > CURRENT_CONFIG_STORE_DATA_VERSION) {
+ throw new XmlPullParserException("Invalid version of data: " + version);
+ }
+ return version;
+ }
+
+ /**
+ * Create raw byte array to be stored in the share store file.
+ * This method serializes the data to a byte array in XML format.
+ *
+ * @return byte array with the serialized output.
+ */
+ public byte[] createSharedRawData() throws XmlPullParserException, IOException {
+ SharedData sharedData = getSharedData();
+ return sharedData.createRawData();
+ }
+
+ /**
+ * Create raw byte array to be stored in the user store file.
+ * This method serializes the data to a byte array in XML format.
+ *
+ * @return byte array with the serialized output.
+ */
+ public byte[] createUserRawData() throws XmlPullParserException, IOException {
+ UserData userData = getUserData();
+ return userData.createRawData();
+ }
+
+ /**
+ * Retrieve the shared data to be stored in the shared config store file.
+ *
+ * @return SharedData instance.
+ */
+ private SharedData getSharedData() {
+ List<WifiConfiguration> sharedConfigurations = new ArrayList<>();
+ for (WifiConfiguration configuration : configurations) {
+ if (configuration.shared) {
+ sharedConfigurations.add(configuration);
+ }
+ }
+ return new SharedData(sharedConfigurations);
+ }
+
+ /**
+ * Retrieve the user specific data to be stored in the user config store file.
+ *
+ * @return UserData instance.
+ */
+ private UserData getUserData() {
+ List<WifiConfiguration> userConfigurations = new ArrayList<>();
+ for (WifiConfiguration configuration : configurations) {
+ if (!configuration.shared) {
+ userConfigurations.add(configuration);
+ }
+ }
+ return new UserData(userConfigurations, deletedEphemeralSSIDs);
+ }
+
+ /**
+ * Class to encapsulate all the data to be stored in the shared store.
+ */
+ public static class SharedData {
+ public List<WifiConfiguration> configurations;
+
+ /**
+ * Create a new instance of shared store data to be written to the store files.
+ *
+ * @param configurations list of shared saved networks to be stored.
+ */
+ public SharedData(List<WifiConfiguration> configurations) {
+ this.configurations = configurations;
+ }
+
+ /**
+ * Create a new instance of the shared store data parsed from the store file.
+ * This method deserializes the provided byte array in XML format to a new SharedData
+ * instance.
+ *
+ * @param sharedDataBytes raw data retrieved from the shared store file.
+ * @return new instance of store data.
+ */
+ public static SharedData parseRawData(byte[] sharedDataBytes)
+ throws XmlPullParserException, IOException{
+ final XmlPullParser in = Xml.newPullParser();
+ final ByteArrayInputStream inputStream = new ByteArrayInputStream(sharedDataBytes);
+ in.setInput(inputStream, StandardCharsets.UTF_8.name());
+
+ // Start parsing the XML stream.
+ int rootTagDepth = in.getDepth() + 1;
+ int version = parseDocumentStartAndVersionFromXml(in);
+
+ List<WifiConfiguration> configurations =
+ parseNetworksFromXml(in, rootTagDepth, version);
+
+ return new SharedData(configurations);
+ }
+
+ /**
+ * Create raw byte array to be stored in the store file.
+ * This method serializes the data to a byte array in XML format.
+ *
+ * @return byte array with the serialized output.
+ */
+ public byte[] createRawData() throws XmlPullParserException, IOException {
+ final XmlSerializer out = new FastXmlSerializer();
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+
+ // Start writing the XML stream.
+ writeDocumentStartAndVersionToXml(out);
+
+ // Write all the shared network configurations.
+ writeNetworksToXml(out, configurations);
+
+ XmlUtil.writeDocumentEnd(out, XML_TAG_DOCUMENT_HEADER);
+
+ byte[] data = outputStream.toByteArray();
+
+ return data;
+ }
+ }
+
+ /**
+ * Class to encapsulate all the data to be stored in the user specific store.
+ */
+ public static class UserData {
+ private static final String XML_TAG_SSID_LIST = "SSIDList";
+
+ public List<WifiConfiguration> configurations;
+ public Set<String> deletedEphemeralSSIDs;
+
+ /**
+ * Create a new instance of user specific store data to be written to the store files.
+ *
+ * @param configurations list of user specific saved networks to be stored.
+ * @param deletedEphemeralSSIDs list of deleted ephemeral ssids to be stored.
+ */
+ public UserData(List<WifiConfiguration> configurations, Set<String> deletedEphemeralSSIDs) {
+ this.configurations = configurations;
+ this.deletedEphemeralSSIDs = deletedEphemeralSSIDs;
+ }
+
+ /**
+ * Create a new instance of the user store data parsed from the store file.
+ * This method deserializes the provided byte array in XML format to a new UserData
+ * instance.
+ *
+ * @param userDataBytes raw data retrieved from the user store file.
+ * @return new instance of store data.
+ */
+ public static UserData parseRawData(byte[] userDataBytes)
+ throws XmlPullParserException, IOException {
+ final XmlPullParser in = Xml.newPullParser();
+ final ByteArrayInputStream inputStream = new ByteArrayInputStream(userDataBytes);
+ in.setInput(inputStream, StandardCharsets.UTF_8.name());
+
+ // Start parsing the XML stream.
+ int rootTagDepth = in.getDepth() + 1;
+ int version = parseDocumentStartAndVersionFromXml(in);
+
+ List<WifiConfiguration> configurations =
+ parseNetworksFromXml(in, rootTagDepth, version);
+
+ XmlUtil.gotoNextSectionWithName(
+ in, XML_TAG_SECTION_HEADER_DELETED_EPHEMERAL_SSID_LIST, rootTagDepth);
+ Set<String> deletedEphemralList =
+ (Set<String>) XmlUtil.readNextValueWithName(in, XML_TAG_SSID_LIST);
+
+ return new UserData(configurations, deletedEphemralList);
+ }
+
+ /**
+ * Create raw byte array to be stored in the store file.
+ * This method serializes the data to a byte array in XML format.
+ *
+ * @return byte array with the serialized output.
+ */
+ public byte[] createRawData() throws XmlPullParserException, IOException {
+ final XmlSerializer out = new FastXmlSerializer();
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+
+ // Start writing the XML stream.
+ writeDocumentStartAndVersionToXml(out);
+
+ // Write all the user network configurations.
+ writeNetworksToXml(out, configurations);
+
+ XmlUtil.writeNextSectionStart(
+ out, XML_TAG_SECTION_HEADER_DELETED_EPHEMERAL_SSID_LIST);
+ XmlUtil.writeNextValue(out, XML_TAG_SSID_LIST, deletedEphemeralSSIDs);
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_DELETED_EPHEMERAL_SSID_LIST);
+
+ XmlUtil.writeDocumentEnd(out, XML_TAG_DOCUMENT_HEADER);
+
+ byte[] data = outputStream.toByteArray();
+
+ return data;
+ }
+ }
+}
+
+
diff --git a/service/java/com/android/server/wifi/WifiConfigStoreNew.java b/service/java/com/android/server/wifi/WifiConfigStoreNew.java
new file mode 100644
index 0000000..cd0e1d2
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiConfigStoreNew.java
@@ -0,0 +1,329 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi;
+
+import android.app.AlarmManager;
+import android.content.Context;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.AtomicFile;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * This class provides the API's to save/load/modify network configurations from a persistent
+ * store. Uses keystore for certificate/key management operations.
+ * NOTE: This class should only be used from WifiConfigManager and is not thread-safe!
+ */
+public class WifiConfigStoreNew {
+ /**
+ * Alarm tag to use for starting alarms for buffering file writes.
+ */
+ @VisibleForTesting
+ public static final String BUFFERED_WRITE_ALARM_TAG = "WriteBufferAlarm";
+ /**
+ * Log tag.
+ */
+ private static final String TAG = "WifiConfigStoreNew";
+ /**
+ * Config store file name for both shared & user specific stores.
+ */
+ private static final String STORE_FILE_NAME = "WifiConfigStore.xml";
+ /**
+ * Directory to store the shared config store file.
+ */
+ private static final String SHARED_STORE_DIRECTORY =
+ new File(Environment.getDataDirectory(), "misc/wifi").getAbsolutePath();
+ /**
+ * Time interval for buffering file writes for non-forced writes
+ */
+ private static final int BUFFERED_WRITE_ALARM_INTERVAL_MS = 10 * 1000;
+ /**
+ * Handler instance to post alarm timeouts to
+ */
+ private final Handler mEventHandler;
+ /**
+ * Alarm manager instance to start buffer timeout alarms.
+ */
+ private final AlarmManager mAlarmManager;
+ /**
+ * Clock instance to retrieve timestamps for alarms.
+ */
+ private final Clock mClock;
+ /**
+ * Shared config store file instance.
+ */
+ private StoreFile mSharedStore;
+ /**
+ * User specific store file instance.
+ */
+ private StoreFile mUserStore;
+ /**
+ * Verbose logging flag.
+ */
+ private boolean mVerboseLoggingEnabled = false;
+ /**
+ * Flag to indicate if there is a buffered write pending.
+ */
+ private boolean mBufferedWritePending = false;
+ /**
+ * Alarm listener for flushing out any buffered writes.
+ */
+ private final AlarmManager.OnAlarmListener mBufferedWriteListener =
+ new AlarmManager.OnAlarmListener() {
+ public void onAlarm() {
+ try {
+ writeBufferedData();
+ } catch (IOException e) {
+ Log.wtf(TAG, "Buffered write failed");
+ }
+
+ }
+ };
+
+ /**
+ * Create a new instance of WifiConfigStore.
+ * Note: The store file instances have been made inputs to this class to ease unit-testing.
+ *
+ * @param context context to use for retrieving the alarm manager.
+ * @param looper looper instance to post alarm timeouts to.
+ * @param clock clock instance to retrieve timestamps for alarms.
+ * @param sharedStore StoreFile instance pointing to the shared store file. This should
+ * be retrieved using {@link #createSharedFile()} method.
+ * @param userStore StoreFile instance pointing to the user specific store file. This should
+ * be retrieved using {@link #createUserFile(int)} method.
+ */
+ public WifiConfigStoreNew(Context context, Looper looper, Clock clock,
+ StoreFile sharedStore, StoreFile userStore) {
+
+ mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ mEventHandler = new Handler(looper);
+ mClock = clock;
+
+ // Initialize the store files.
+ mSharedStore = sharedStore;
+ mUserStore = userStore;
+ }
+
+ /**
+ * Create a new instance of the shared store file.
+ *
+ * @return new instance of the store file.
+ */
+ public static StoreFile createSharedFile() {
+ return new StoreFile(new File(SHARED_STORE_DIRECTORY, STORE_FILE_NAME));
+ }
+
+ /**
+ * Create a new instance of the user specific store file.
+ * The user store file is inside the user's encrypted data directory.
+ *
+ * @param userId userId corresponding to the currently logged-in user.
+ * @return new instance of the store file.
+ */
+ public static StoreFile createUserFile(int userId) {
+ final String userDir = Environment.getDataSystemCeDirectory(userId).getAbsolutePath();
+ return new StoreFile(new File(userDir, STORE_FILE_NAME));
+ }
+
+ /**
+ * Enable verbose logging.
+ */
+ public void enableVerboseLogging(boolean verbose) {
+ mVerboseLoggingEnabled = verbose;
+ }
+
+ /**
+ * API to write the provided store data to config stores.
+ * The method writes the user specific configurations to user specific config store and the
+ * shared configurations to shared config store.
+ *
+ * @param forceSync boolean to force write the config stores now. if false, the writes are
+ * buffered and written after the configured interval.
+ * @param storeData The entire data to be stored across all the config store files.
+ */
+ public void write(boolean forceSync, WifiConfigStoreData storeData)
+ throws XmlPullParserException, IOException {
+ // Serialize the provided data and send it to the respective stores. The actual write will
+ // be performed later depending on the |forceSync| flag .
+ byte[] sharedDataBytes = storeData.createSharedRawData();
+ byte[] userDataBytes = storeData.createUserRawData();
+
+ mSharedStore.storeRawDataToWrite(sharedDataBytes);
+ mUserStore.storeRawDataToWrite(userDataBytes);
+
+ // Every write provides a new snapshot to be persisted, so |forceSync| flag overrides any
+ // pending buffer writes.
+ if (forceSync) {
+ writeBufferedData();
+ } else {
+ startBufferedWriteAlarm();
+ }
+ }
+
+ /**
+ * Helper method to start a buffered write alarm if one doesn't already exist.
+ */
+ private void startBufferedWriteAlarm() {
+ if (!mBufferedWritePending) {
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ mClock.getElapsedSinceBootMillis() + BUFFERED_WRITE_ALARM_INTERVAL_MS,
+ BUFFERED_WRITE_ALARM_TAG, mBufferedWriteListener, mEventHandler);
+ mBufferedWritePending = true;
+ }
+ }
+
+ /**
+ * Helper method to stop a buffered write alarm if one exists.
+ */
+ private void stopBufferedWriteAlarm() {
+ if (mBufferedWritePending) {
+ mAlarmManager.cancel(mBufferedWriteListener);
+ mBufferedWritePending = false;
+ }
+ }
+
+ /**
+ * Helper method to actually perform the writes to the file. This flushes out any write data
+ * being buffered in the respective stores and cancels any pending buffer write alarms.
+ */
+ private void writeBufferedData() throws IOException {
+ stopBufferedWriteAlarm();
+ mSharedStore.writeBufferedRawData();
+ mUserStore.writeBufferedRawData();
+ }
+
+ /**
+ * API to read the store data from the config stores.
+ * The method reads the user specific configurations from user specific config store and the
+ * shared configurations from the shared config store.
+ *
+ * @return storeData The entire data retrieved across all the config store files.
+ */
+ public WifiConfigStoreData read() throws XmlPullParserException, IOException {
+ byte[] sharedDataBytes = mSharedStore.readRawData();
+ byte[] userDataBytes = mUserStore.readRawData();
+
+ return WifiConfigStoreData.parseRawData(sharedDataBytes, userDataBytes);
+ }
+
+ /**
+ * Handle a user switch. This changes the user specific store.
+ *
+ * @param userStore StoreFile instance pointing to the user specific store file. This should
+ * be retrieved using {@link #createUserFile(int)} method.
+ */
+ public void handleUserSwitch(StoreFile userStore) throws IOException {
+ // Flush out any stored data if present before switching the user stores.
+ writeBufferedData();
+ mUserStore = userStore;
+ }
+
+ /**
+ * Class to encapsulate all file writes. This is a wrapper over {@link AtomicFile} to write/read
+ * raw data from the persistent file. This class provides helper methods to read/write the
+ * entire file into a byte array.
+ * This helps to separate out the processing/parsing from the actual file writing.
+ */
+ public static class StoreFile {
+ /**
+ * File permissions to lock down the file.
+ */
+ private static final int FILE_MODE = 0600;
+ /**
+ * The store file to be written to.
+ */
+ private final AtomicFile mAtomicFile;
+ /**
+ * This is an intermediate buffer to store the data to be written.
+ */
+ private byte[] mWriteData;
+ /**
+ * Store the file name for setting the file permissions/logging purposes.
+ */
+ private String mFileName;
+
+ public StoreFile(File file) {
+ mAtomicFile = new AtomicFile(file);
+ mFileName = mAtomicFile.getBaseFile().getAbsolutePath();
+ }
+
+ /**
+ * Read the entire raw data from the store file and return in a byte array.
+ *
+ * @return raw data read from the file or null if the file is not found.
+ * @throws IOException if an error occurs. The input stream is always closed by the method
+ * even when an exception is encountered.
+ */
+ public byte[] readRawData() throws IOException {
+ try {
+ return mAtomicFile.readFully();
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Store the provided byte array to be written when {@link #writeBufferedRawData()} method
+ * is invoked.
+ * This intermediate step is needed to help in buffering file writes.
+ *
+ * @param data raw data to be written to the file.
+ */
+ public void storeRawDataToWrite(byte[] data) {
+ mWriteData = data;
+ }
+
+ /**
+ * Write the stored raw data to the store file.
+ * After the write to file, the mWriteData member is reset.
+ * @throws IOException if an error occurs. The output stream is always closed by the method
+ * even when an exception is encountered.
+ */
+ public void writeBufferedRawData() throws IOException {
+ if (mWriteData == null) {
+ Log.w(TAG, "No data stored for writing to file: " + mFileName);
+ return;
+ }
+ // Write the data to the atomic file.
+ FileOutputStream out = null;
+ try {
+ out = mAtomicFile.startWrite();
+ FileUtils.setPermissions(mFileName, FILE_MODE, -1, -1);
+ out.write(mWriteData);
+ mAtomicFile.finishWrite(out);
+ } catch (IOException e) {
+ if (out != null) {
+ mAtomicFile.failWrite(out);
+ }
+ throw e;
+ }
+ // Reset the pending write data after write.
+ mWriteData = null;
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/WifiConfigurationUtil.java b/service/java/com/android/server/wifi/WifiConfigurationUtil.java
index 7d0fb3c..9059c45 100644
--- a/service/java/com/android/server/wifi/WifiConfigurationUtil.java
+++ b/service/java/com/android/server/wifi/WifiConfigurationUtil.java
@@ -23,14 +23,17 @@
import java.util.List;
/**
- * Helper for working with {@link android.net.wifi.WifiConfiguration} objects.
+ * WifiConfiguration utility for any {@link android.net.wifi.WifiConfiguration} related operations.
+ * Currently contains:
+ * > Helper method to check if the WifiConfiguration object is visible to the provided users.
+ * > Helper methods to identify the encryption of a WifiConfiguration object.
*/
public class WifiConfigurationUtil {
/**
* Check whether a network configuration is visible to a user or any of its managed profiles.
* @param config the network configuration whose visibility should be checked
* @param profiles the user IDs of the user itself and all its managed profiles (can be obtained
- * via {@link android.os.UserManager.getProfiles})
+ * via {@link android.os.UserManager#getProfiles})
* @return whether the network configuration is visible to the user or any of its managed
* profiles
*/
@@ -46,4 +49,48 @@
}
return false;
}
+
+ /**
+ * Checks if the provided |wepKeys| array contains any non-null value;
+ */
+ public static boolean hasAnyValidWepKey(String[] wepKeys) {
+ for (int i = 0; i < wepKeys.length; i++) {
+ if (wepKeys[i] != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Helper method to check if the provided |config| corresponds to a PSK network or not.
+ */
+ public static boolean isConfigForPskNetwork(WifiConfiguration config) {
+ return config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK);
+ }
+
+ /**
+ * Helper method to check if the provided |config| corresponds to a EAP network or not.
+ */
+ public static boolean isConfigForEapNetwork(WifiConfiguration config) {
+ return (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)
+ || config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X));
+ }
+
+ /**
+ * Helper method to check if the provided |config| corresponds to a WEP network or not.
+ */
+ public static boolean isConfigForWepNetwork(WifiConfiguration config) {
+ return (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)
+ && hasAnyValidWepKey(config.wepKeys));
+ }
+
+ /**
+ * Helper method to check if the provided |config| corresponds to an open network or not.
+ */
+ public static boolean isConfigForOpenNetwork(WifiConfiguration config) {
+ return !(isConfigForWepNetwork(config) || isConfigForPskNetwork(config)
+ || isConfigForEapNetwork(config));
+ }
+
}
diff --git a/service/java/com/android/server/wifi/WifiConnectivityManager.java b/service/java/com/android/server/wifi/WifiConnectivityManager.java
index 7f3d5d7..b73d149 100644
--- a/service/java/com/android/server/wifi/WifiConnectivityManager.java
+++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java
@@ -36,7 +36,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.wifi.util.ScanDetailUtil;
+import com.android.server.wifi.util.ScanResultUtil;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -220,10 +220,9 @@
localLog(listenerName + " onResults: start QNS");
WifiConfiguration candidate =
mQualifiedNetworkSelector.selectQualifiedNetwork(false,
- mUntrustedConnectionAllowed, scanDetails,
- mStateMachine.isLinkDebouncing(), mStateMachine.isConnected(),
- mStateMachine.isDisconnected(),
- mStateMachine.isSupplicantTransientState());
+ mUntrustedConnectionAllowed, mStateMachine.isLinkDebouncing(),
+ mStateMachine.isConnected(), mStateMachine.isDisconnected(),
+ mStateMachine.isSupplicantTransientState(), scanDetails);
mWifiLastResortWatchdog.updateAvailableNetworks(
mQualifiedNetworkSelector.getFilteredScanDetails());
if (candidate != null) {
@@ -286,7 +285,7 @@
+ fullScanResult.capabilities);
}
- mScanDetails.add(ScanDetailUtil.toScanDetail(fullScanResult));
+ mScanDetails.add(ScanResultUtil.toScanDetail(fullScanResult));
}
}
@@ -355,7 +354,7 @@
+ fullScanResult.capabilities);
}
- mScanDetails.add(ScanDetailUtil.toScanDetail(fullScanResult));
+ mScanDetails.add(ScanResultUtil.toScanDetail(fullScanResult));
}
}
@@ -479,7 +478,7 @@
localLog("PnoScanListener: onPnoNetworkFound: results len = " + results.length);
for (ScanResult result: results) {
- mScanDetails.add(ScanDetailUtil.toScanDetail(result));
+ mScanDetails.add(ScanResultUtil.toScanDetail(result));
}
boolean wasConnectAttempted;
@@ -615,7 +614,7 @@
return;
}
- Long elapsedTimeMillis = mClock.elapsedRealtime();
+ Long elapsedTimeMillis = mClock.getElapsedSinceBootMillis();
if (!mScreenOn && shouldSkipConnectionAttempt(elapsedTimeMillis)) {
localLog("connectToNetwork: Too many connection attempts. Skipping this attempt!");
mTotalConnectivityAttemptsRateLimited++;
@@ -705,7 +704,7 @@
// Start a single scan and set up the interval for next single scan.
private void startPeriodicSingleScan() {
- long currentTimeStamp = mClock.elapsedRealtime();
+ long currentTimeStamp = mClock.getElapsedSinceBootMillis();
if (mLastPeriodicSingleScanTimeStamp != RESET_TIME_STAMP) {
long msSinceLastScan = currentTimeStamp - mLastPeriodicSingleScanTimeStamp;
@@ -923,7 +922,7 @@
Log.i(TAG, "scheduleWatchdogTimer");
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- mClock.elapsedRealtime() + WATCHDOG_INTERVAL_MS,
+ mClock.getElapsedSinceBootMillis() + WATCHDOG_INTERVAL_MS,
WATCHDOG_TIMER_TAG,
mWatchdogListener, mEventHandler);
}
@@ -931,7 +930,7 @@
// Set up periodic scan timer
private void schedulePeriodicScanTimer(int intervalMs) {
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- mClock.elapsedRealtime() + intervalMs,
+ mClock.getElapsedSinceBootMillis() + intervalMs,
PERIODIC_SCAN_TIMER_TAG,
mPeriodicScanTimerListener, mEventHandler);
mPeriodicScanTimerSet = true;
@@ -952,7 +951,7 @@
RestartSingleScanListener restartSingleScanListener =
new RestartSingleScanListener(isFullBandScan);
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- mClock.elapsedRealtime() + RESTART_SCAN_DELAY_MS,
+ mClock.getElapsedSinceBootMillis() + RESTART_SCAN_DELAY_MS,
RESTART_SINGLE_SCAN_TIMER_TAG,
restartSingleScanListener, mEventHandler);
}
@@ -962,7 +961,7 @@
localLog("scheduleDelayedConnectivityScan");
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- mClock.elapsedRealtime() + msFromNow,
+ mClock.getElapsedSinceBootMillis() + msFromNow,
RESTART_CONNECTIVITY_SCAN_TIMER_TAG,
mRestartScanListener, mEventHandler);
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index 6939d45..cc117da 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -16,34 +16,170 @@
package com.android.server.wifi;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.IApInterface;
+import android.net.wifi.IWificond;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.os.UserManager;
+import android.provider.Settings;
import android.security.KeyStore;
+import com.android.internal.R;
+import com.android.server.am.BatteryStatsService;
+
+import java.util.ArrayList;
+
/**
- * WiFi dependency injector using thread-safe lazy singleton pattern. To be used for accessing
- * various wifi class instances and as a handle for mock injection.
+ * WiFi dependency injector. To be used for accessing various WiFi class instances and as a
+ * handle for mock injection.
+ *
+ * Some WiFi class instances currently depend on having a Looper from a HandlerThread that has
+ * been started. To accommodate this, we have a two-phased approach to initialize and retrieve
+ * an instance of the WifiInjector.
*/
public class WifiInjector {
- // see: https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom
- private static class LazyHolder {
- public static final WifiInjector sInstance = new WifiInjector();
- }
+ private static final String BOOT_DEFAULT_WIFI_COUNTRY_CODE = "ro.boot.wificountrycode";
+ private static final String WIFICOND_SERVICE_NAME = "wificond";
- public static WifiInjector getInstance() {
- return LazyHolder.sInstance;
- }
+ static WifiInjector sWifiInjector = null;
+ private final Context mContext;
+ private final FrameworkFacade mFrameworkFacade = new FrameworkFacade();
+ private final HandlerThread mWifiServiceHandlerThread;
+ private final HandlerThread mWifiStateMachineHandlerThread;
+ private final WifiTrafficPoller mTrafficPoller;
+ private final WifiCountryCode mCountryCode;
+ private final BackupManagerProxy mBackupManagerProxy = new BackupManagerProxy();
+ private final WifiStateMachine mWifiStateMachine;
+ private final WifiSettingsStore mSettingsStore;
+ private final WifiCertManager mCertManager;
+ private final WifiNotificationController mNotificationController;
+ private final WifiLockManager mLockManager;
+ private final WifiController mWifiController;
private final Clock mClock = new Clock();
private final WifiMetrics mWifiMetrics = new WifiMetrics(mClock);
- private final WifiLastResortWatchdog mWifiLastResortWatchdog =
- new WifiLastResortWatchdog(mWifiMetrics);
+ private final WifiLastResortWatchdog mWifiLastResortWatchdog;
private final PropertyService mPropertyService = new SystemPropertyService();
private final BuildProperties mBuildProperties = new SystemBuildProperties();
private final KeyStore mKeyStore = KeyStore.getInstance();
+ private final WifiBackupRestore mWifiBackupRestore = new WifiBackupRestore();
+ private final WifiMulticastLockManager mWifiMulticastLockManager;
+
+ public WifiInjector(Context context) {
+ if (context == null) {
+ throw new IllegalStateException(
+ "WifiInjector should not be initialized with a null Context.");
+ }
+
+ if (sWifiInjector != null) {
+ throw new IllegalStateException(
+ "WifiInjector was already created, use getInstance instead.");
+ }
+
+ sWifiInjector = this;
+
+ mContext = context;
+ // Now create and start handler threads
+ mWifiServiceHandlerThread = new HandlerThread("WifiService");
+ mWifiServiceHandlerThread.start();
+ mWifiStateMachineHandlerThread = new HandlerThread("WifiStateMachine");
+ mWifiStateMachineHandlerThread.start();
+
+ // Now get instances of all the objects that depend on the HandlerThreads
+ mTrafficPoller = new WifiTrafficPoller(mContext, mWifiServiceHandlerThread.getLooper(),
+ WifiNative.getWlanNativeInterface().getInterfaceName());
+ mCountryCode = new WifiCountryCode(WifiNative.getWlanNativeInterface(),
+ SystemProperties.get(BOOT_DEFAULT_WIFI_COUNTRY_CODE),
+ mFrameworkFacade.getStringSetting(mContext, Settings.Global.WIFI_COUNTRY_CODE),
+ mContext.getResources()
+ .getBoolean(R.bool.config_wifi_revert_country_code_on_cellular_loss));
+ mWifiStateMachine = new WifiStateMachine(mContext, mFrameworkFacade,
+ mWifiStateMachineHandlerThread.getLooper(), UserManager.get(mContext),
+ this, mBackupManagerProxy, mCountryCode);
+ mSettingsStore = new WifiSettingsStore(mContext);
+ mCertManager = new WifiCertManager(mContext);
+ mNotificationController = new WifiNotificationController(mContext,
+ mWifiServiceHandlerThread.getLooper(), mWifiStateMachine,
+ mFrameworkFacade, null);
+ mLockManager = new WifiLockManager(mContext, BatteryStatsService.getService());
+ mWifiController = new WifiController(mContext, mWifiStateMachine, mSettingsStore,
+ mLockManager, mWifiServiceHandlerThread.getLooper(), mFrameworkFacade);
+ mWifiLastResortWatchdog = new WifiLastResortWatchdog(mWifiController, mWifiMetrics);
+ mWifiMulticastLockManager = new WifiMulticastLockManager(mWifiStateMachine,
+ BatteryStatsService.getService());
+ }
+
+ /**
+ * Obtain an instance of the WifiInjector class.
+ *
+ * This is the generic method to get an instance of the class. The first instance should be
+ * retrieved using the getInstanceWithContext method.
+ */
+ public static WifiInjector getInstance() {
+ if (sWifiInjector == null) {
+ throw new IllegalStateException(
+ "Attempted to retrieve a WifiInjector instance before constructor was called.");
+ }
+ return sWifiInjector;
+ }
public WifiMetrics getWifiMetrics() {
return mWifiMetrics;
}
+ public BackupManagerProxy getBackupManagerProxy() {
+ return mBackupManagerProxy;
+ }
+
+ public FrameworkFacade getFrameworkFacade() {
+ return mFrameworkFacade;
+ }
+
+ public HandlerThread getWifiServiceHandlerThread() {
+ return mWifiServiceHandlerThread;
+ }
+
+ public HandlerThread getWifiStateMachineHandlerThread() {
+ return mWifiStateMachineHandlerThread;
+ }
+
+ public WifiTrafficPoller getWifiTrafficPoller() {
+ return mTrafficPoller;
+ }
+
+ public WifiCountryCode getWifiCountryCode() {
+ return mCountryCode;
+ }
+
+ public WifiStateMachine getWifiStateMachine() {
+ return mWifiStateMachine;
+ }
+
+ public WifiSettingsStore getWifiSettingsStore() {
+ return mSettingsStore;
+ }
+
+ public WifiCertManager getWifiCertManager() {
+ return mCertManager;
+ }
+
+ public WifiNotificationController getWifiNotificationController() {
+ return mNotificationController;
+ }
+
+ public WifiLockManager getWifiLockManager() {
+ return mLockManager;
+ }
+
+ public WifiController getWifiController() {
+ return mWifiController;
+ }
+
public WifiLastResortWatchdog getWifiLastResortWatchdog() {
return mWifiLastResortWatchdog;
}
@@ -56,9 +192,47 @@
return mPropertyService;
}
- public BuildProperties getBuildProperties() { return mBuildProperties; }
+ public BuildProperties getBuildProperties() {
+ return mBuildProperties;
+ }
public KeyStore getKeyStore() {
return mKeyStore;
}
+
+ public WifiBackupRestore getWifiBackupRestore() {
+ return mWifiBackupRestore;
+ }
+
+ public WifiMulticastLockManager getWifiMulticastLockManager() {
+ return mWifiMulticastLockManager;
+ }
+
+ public IWificond makeWificond() {
+ // We depend on being able to refresh our binder in WifiStateMachine, so don't cache it.
+ IBinder binder = ServiceManager.getService(WIFICOND_SERVICE_NAME);
+ return IWificond.Stub.asInterface(binder);
+ }
+
+ /**
+ * Create a SoftApManager.
+ * @param wifiNative reference to WifiNative
+ * @param nmService reference to NetworkManagementService
+ * @param cm reference to ConnectivityManager
+ * @param countryCode Country code
+ * @param allowed2GChannels list of allowed 2G channels
+ * @param listener listener for SoftApManager
+ * @param apInterface network interface to start hostapd against
+ * @return an instance of SoftApManager
+ */
+ public SoftApManager makeSoftApManager(
+ WifiNative wifiNative,
+ INetworkManagementService nmService, ConnectivityManager cm,
+ String countryCode, ArrayList<Integer> allowed2GChannels,
+ SoftApManager.Listener listener, IApInterface apInterface) {
+ return new SoftApManager(
+ mWifiServiceHandlerThread.getLooper(),
+ wifiNative, nmService, countryCode,
+ allowed2GChannels, listener, apInterface);
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiKeyStore.java b/service/java/com/android/server/wifi/WifiKeyStore.java
new file mode 100644
index 0000000..f921988
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiKeyStore.java
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi;
+
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.os.Process;
+import android.security.Credentials;
+import android.security.KeyChain;
+import android.security.KeyStore;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.io.IOException;
+import java.security.Key;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This class provides the methods to access keystore for certificate management.
+ *
+ * NOTE: This class should only be used from WifiConfigManager!
+ */
+public class WifiKeyStore {
+ private static final String TAG = "WifiKeyStore";
+
+ private boolean mVerboseLoggingEnabled = false;
+
+ private final KeyStore mKeyStore;
+
+ WifiKeyStore(KeyStore keyStore) {
+ mKeyStore = keyStore;
+ }
+
+ /**
+ * Enable verbose logging.
+ */
+ void enableVerboseLogging(boolean verbose) {
+ mVerboseLoggingEnabled = verbose;
+ }
+
+ // Certificate and private key management for EnterpriseConfig
+ private static boolean needsKeyStore(WifiEnterpriseConfig config) {
+ return (config.getClientCertificate() != null || config.getCaCertificate() != null);
+ }
+
+ private static boolean isHardwareBackedKey(Key key) {
+ return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm());
+ }
+
+ private static boolean hasHardwareBackedKey(Certificate certificate) {
+ return isHardwareBackedKey(certificate.getPublicKey());
+ }
+
+ /**
+ * Install keys for given enterprise network.
+ *
+ * @param existingConfig Existing config corresponding to the network already stored in our
+ * database. This maybe null if it's a new network.
+ * @param config Config corresponding to the network.
+ * @return true if successful, false otherwise.
+ */
+ private boolean installKeys(WifiEnterpriseConfig existingConfig, WifiEnterpriseConfig config,
+ String name) {
+ boolean ret = true;
+ String privKeyName = Credentials.USER_PRIVATE_KEY + name;
+ String userCertName = Credentials.USER_CERTIFICATE + name;
+ if (config.getClientCertificate() != null) {
+ byte[] privKeyData = config.getClientPrivateKey().getEncoded();
+ if (mVerboseLoggingEnabled) {
+ if (isHardwareBackedKey(config.getClientPrivateKey())) {
+ Log.d(TAG, "importing keys " + name + " in hardware backed store");
+ } else {
+ Log.d(TAG, "importing keys " + name + " in software backed store");
+ }
+ }
+ ret = mKeyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID,
+ KeyStore.FLAG_NONE);
+
+ if (!ret) {
+ return ret;
+ }
+
+ ret = putCertInKeyStore(userCertName, config.getClientCertificate());
+ if (!ret) {
+ // Remove private key installed
+ mKeyStore.delete(privKeyName, Process.WIFI_UID);
+ return ret;
+ }
+ }
+
+ X509Certificate[] caCertificates = config.getCaCertificates();
+ Set<String> oldCaCertificatesToRemove = new ArraySet<>();
+ if (existingConfig != null && existingConfig.getCaCertificateAliases() != null) {
+ oldCaCertificatesToRemove.addAll(
+ Arrays.asList(existingConfig.getCaCertificateAliases()));
+ }
+ List<String> caCertificateAliases = null;
+ if (caCertificates != null) {
+ caCertificateAliases = new ArrayList<>();
+ for (int i = 0; i < caCertificates.length; i++) {
+ String alias = caCertificates.length == 1 ? name
+ : String.format("%s_%d", name, i);
+
+ oldCaCertificatesToRemove.remove(alias);
+ ret = putCertInKeyStore(Credentials.CA_CERTIFICATE + alias, caCertificates[i]);
+ if (!ret) {
+ // Remove client key+cert
+ if (config.getClientCertificate() != null) {
+ mKeyStore.delete(privKeyName, Process.WIFI_UID);
+ mKeyStore.delete(userCertName, Process.WIFI_UID);
+ }
+ // Remove added CA certs.
+ for (String addedAlias : caCertificateAliases) {
+ mKeyStore.delete(Credentials.CA_CERTIFICATE + addedAlias, Process.WIFI_UID);
+ }
+ return ret;
+ } else {
+ caCertificateAliases.add(alias);
+ }
+ }
+ }
+ // Remove old CA certs.
+ for (String oldAlias : oldCaCertificatesToRemove) {
+ mKeyStore.delete(Credentials.CA_CERTIFICATE + oldAlias, Process.WIFI_UID);
+ }
+ // Set alias names
+ if (config.getClientCertificate() != null) {
+ config.setClientCertificateAlias(name);
+ config.resetClientKeyEntry();
+ }
+
+ if (caCertificates != null) {
+ config.setCaCertificateAliases(
+ caCertificateAliases.toArray(new String[caCertificateAliases.size()]));
+ config.resetCaCertificate();
+ }
+ return ret;
+ }
+
+ private boolean putCertInKeyStore(String name, Certificate cert) {
+ try {
+ byte[] certData = Credentials.convertToPem(cert);
+ if (mVerboseLoggingEnabled) Log.d(TAG, "putting certificate " + name + " in keystore");
+ return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE);
+ } catch (IOException e1) {
+ return false;
+ } catch (CertificateException e2) {
+ return false;
+ }
+ }
+
+ /**
+ * Remove enterprise keys from the network config.
+ *
+ * @param config Config corresponding to the network.
+ */
+ public void removeKeys(WifiEnterpriseConfig config) {
+ String client = config.getClientCertificateAlias();
+ // a valid client certificate is configured
+ if (!TextUtils.isEmpty(client)) {
+ if (mVerboseLoggingEnabled) Log.d(TAG, "removing client private key and user cert");
+ mKeyStore.delete(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
+ mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
+ }
+
+ String[] aliases = config.getCaCertificateAliases();
+ // a valid ca certificate is configured
+ if (aliases != null) {
+ for (String ca : aliases) {
+ if (!TextUtils.isEmpty(ca)) {
+ if (mVerboseLoggingEnabled) Log.d(TAG, "removing CA cert: " + ca);
+ mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
+ }
+ }
+ }
+ }
+
+ /**
+ * Update/Install keys for given enterprise network.
+ *
+ * @param config Config corresponding to the network.
+ * @param existingConfig Existing config corresponding to the network already stored in our
+ * database. This maybe null if it's a new network.
+ * @return true if successful, false otherwise.
+ */
+ public boolean updateNetworkKeys(WifiConfiguration config, WifiConfiguration existingConfig) {
+ WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
+ if (needsKeyStore(enterpriseConfig)) {
+ try {
+ /* config passed may include only fields being updated.
+ * In order to generate the key id, fetch uninitialized
+ * fields from the currently tracked configuration
+ */
+ String keyId = config.getKeyIdForCredentials(existingConfig);
+ if (!installKeys(existingConfig != null
+ ? existingConfig.enterpriseConfig : null, enterpriseConfig, keyId)) {
+ Log.e(TAG, config.SSID + ": failed to install keys");
+ return false;
+ }
+ } catch (IllegalStateException e) {
+ Log.e(TAG, config.SSID + " invalid config for key installation: " + e.getMessage());
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Checks whether the configuration requires a software backed keystore or not.
+ * @param config WifiEnterprise config instance pointing to the enterprise configuration of the
+ * network.
+ */
+ public static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) {
+ String client = config.getClientCertificateAlias();
+ if (!TextUtils.isEmpty(client)) {
+ // a valid client certificate is configured
+
+ // BUGBUG(b/29578316): keyStore.get() never returns certBytes; because it is not
+ // taking WIFI_UID as a parameter. It always looks for certificate
+ // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that
+ // all certificates need software keystore until we get the get() API
+ // fixed.
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java
index 558b50e..6fb2050 100644
--- a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java
+++ b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java
@@ -34,8 +34,7 @@
*/
public class WifiLastResortWatchdog {
private static final String TAG = "WifiLastResortWatchdog";
- private static final boolean VDBG = false;
- private static final boolean DBG = true;
+ private boolean mVerboseLoggingEnabled = false;
/**
* Association Failure code
*/
@@ -80,9 +79,10 @@
private WifiMetrics mWifiMetrics;
- private WifiController mWifiController = null;
+ private WifiController mWifiController;
- WifiLastResortWatchdog(WifiMetrics wifiMetrics) {
+ WifiLastResortWatchdog(WifiController wifiController, WifiMetrics wifiMetrics) {
+ mWifiController = wifiController;
mWifiMetrics = wifiMetrics;
}
@@ -95,7 +95,9 @@
*/
public void updateAvailableNetworks(
List<Pair<ScanDetail, WifiConfiguration>> availableNetworks) {
- if (VDBG) Log.v(TAG, "updateAvailableNetworks: size = " + availableNetworks.size());
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "updateAvailableNetworks: size = " + availableNetworks.size());
+ }
// Add new networks to mRecentAvailableNetworks
if (availableNetworks != null) {
for (Pair<ScanDetail, WifiConfiguration> pair : availableNetworks) {
@@ -105,7 +107,9 @@
if (scanResult == null) continue;
String bssid = scanResult.BSSID;
String ssid = "\"" + scanDetail.getSSID() + "\"";
- if (VDBG) Log.v(TAG, " " + bssid + ": " + scanDetail.getSSID());
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, " " + bssid + ": " + scanDetail.getSSID());
+ }
// Cache the scanResult & WifiConfig
AvailableNetworkFailureCount availableNetworkFailureCount =
mRecentAvailableNetworks.get(bssid);
@@ -161,15 +165,12 @@
mSsidFailureCount.remove(ssid);
}
} else {
- if (DBG) {
- Log.d(TAG, "updateAvailableNetworks: SSID to AP count mismatch for "
- + ssid);
- }
+ Log.d(TAG, "updateAvailableNetworks: SSID to AP count mismatch for " + ssid);
}
it.remove();
}
}
- if (VDBG) Log.v(TAG, toString());
+ if (mVerboseLoggingEnabled) Log.v(TAG, toString());
}
/**
@@ -180,7 +181,7 @@
* @return true if watchdog triggers, returned for test visibility
*/
public boolean noteConnectionFailureAndTriggerIfNeeded(String ssid, String bssid, int reason) {
- if (VDBG) {
+ if (mVerboseLoggingEnabled) {
Log.v(TAG, "noteConnectionFailureAndTriggerIfNeeded: [" + ssid + ", " + bssid + ", "
+ reason + "]");
}
@@ -189,7 +190,9 @@
// Have we met conditions to trigger the Watchdog Wifi restart?
boolean isRestartNeeded = checkTriggerCondition();
- if (VDBG) Log.v(TAG, "isRestartNeeded = " + isRestartNeeded);
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "isRestartNeeded = " + isRestartNeeded);
+ }
if (isRestartNeeded) {
// Stop the watchdog from triggering until re-enabled
setWatchdogTriggerEnabled(false);
@@ -207,7 +210,9 @@
* @param isEntering true if called from ConnectedState.enter(), false for exit()
*/
public void connectedStateTransition(boolean isEntering) {
- if (VDBG) Log.v(TAG, "connectedStateTransition: isEntering = " + isEntering);
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "connectedStateTransition: isEntering = " + isEntering);
+ }
mWifiIsConnected = isEntering;
if (isEntering) {
// We connected to something! Reset failure counts for everything
@@ -228,7 +233,7 @@
* @param reason Message id from WifiStateMachine for this failure
*/
private void updateFailureCountForNetwork(String ssid, String bssid, int reason) {
- if (VDBG) {
+ if (mVerboseLoggingEnabled) {
Log.v(TAG, "updateFailureCountForNetwork: [" + ssid + ", " + bssid + ", "
+ reason + "]");
}
@@ -249,9 +254,7 @@
private void incrementSsidFailureCount(String ssid, int reason) {
Pair<AvailableNetworkFailureCount, Integer> ssidFails = mSsidFailureCount.get(ssid);
if (ssidFails == null) {
- if (DBG) {
- Log.v(TAG, "updateFailureCountForNetwork: No networks for ssid = " + ssid);
- }
+ Log.d(TAG, "updateFailureCountForNetwork: No networks for ssid = " + ssid);
return;
}
AvailableNetworkFailureCount failureCount = ssidFails.first;
@@ -267,22 +270,18 @@
AvailableNetworkFailureCount availableNetworkFailureCount =
mRecentAvailableNetworks.get(bssid);
if (availableNetworkFailureCount == null) {
- if (DBG) {
- Log.d(TAG, "updateFailureCountForNetwork: Unable to find Network [" + ssid
- + ", " + bssid + "]");
- }
+ Log.d(TAG, "updateFailureCountForNetwork: Unable to find Network [" + ssid
+ + ", " + bssid + "]");
return;
}
if (!availableNetworkFailureCount.ssid.equals(ssid)) {
- if (DBG) {
- Log.d(TAG, "updateFailureCountForNetwork: Failed connection attempt has"
- + " wrong ssid. Failed [" + ssid + ", " + bssid + "], buffered ["
- + availableNetworkFailureCount.ssid + ", " + bssid + "]");
- }
+ Log.d(TAG, "updateFailureCountForNetwork: Failed connection attempt has"
+ + " wrong ssid. Failed [" + ssid + ", " + bssid + "], buffered ["
+ + availableNetworkFailureCount.ssid + ", " + bssid + "]");
return;
}
if (availableNetworkFailureCount.config == null) {
- if (VDBG) {
+ if (mVerboseLoggingEnabled) {
Log.v(TAG, "updateFailureCountForNetwork: network has no config ["
+ ssid + ", " + bssid + "]");
}
@@ -297,7 +296,7 @@
* @return is the trigger condition true
*/
private boolean checkTriggerCondition() {
- if (VDBG) Log.v(TAG, "checkTriggerCondition.");
+ if (mVerboseLoggingEnabled) Log.v(TAG, "checkTriggerCondition.");
// Don't check Watchdog trigger if wifi is in a connected state
// (This should not occur, but we want to protect against any race conditions)
if (mWifiIsConnected) return false;
@@ -319,7 +318,7 @@
}
// We have met the failure count for every available network & there is at-least one network
// we have previously connected to present.
- if (VDBG) {
+ if (mVerboseLoggingEnabled) {
Log.v(TAG, "checkTriggerCondition: return = " + atleastOneNetworkHasEverConnected);
}
return atleastOneNetworkHasEverConnected;
@@ -329,7 +328,7 @@
* Trigger a restart of the wifi stack.
*/
private void restartWifiStack() {
- if (VDBG) Log.v(TAG, "restartWifiStack.");
+ if (mVerboseLoggingEnabled) Log.v(TAG, "restartWifiStack.");
// First verify that we can send the trigger message.
if (mWifiController == null) {
@@ -337,17 +336,15 @@
return;
}
- if (DBG) Log.d(TAG, toString());
-
mWifiController.sendMessage(WifiController.CMD_RESTART_WIFI);
- Log.i(TAG, "Triggered WiFi stack restart.");
+ Log.i(TAG, "Triggered WiFi stack restart.\n" + toString());
}
/**
* Update WifiMetrics with various Watchdog stats (trigger counts, failed network counts)
*/
private void incrementWifiMetricsTriggerCounts() {
- if (VDBG) Log.v(TAG, "incrementWifiMetricsTriggerCounts.");
+ if (mVerboseLoggingEnabled) Log.v(TAG, "incrementWifiMetricsTriggerCounts.");
mWifiMetrics.incrementNumLastResortWatchdogTriggers();
mWifiMetrics.addCountToNumLastResortWatchdogAvailableNetworksTotal(
mSsidFailureCount.size());
@@ -379,7 +376,7 @@
* Clear failure counts for each network in recentAvailableNetworks
*/
private void clearAllFailureCounts() {
- if (VDBG) Log.v(TAG, "clearAllFailureCounts.");
+ if (mVerboseLoggingEnabled) Log.v(TAG, "clearAllFailureCounts.");
for (Map.Entry<String, AvailableNetworkFailureCount> entry
: mRecentAvailableNetworks.entrySet()) {
final AvailableNetworkFailureCount failureCount = entry.getValue();
@@ -403,7 +400,7 @@
* @param enable true to enable the Watchdog trigger, false to disable it
*/
private void setWatchdogTriggerEnabled(boolean enable) {
- if (VDBG) Log.v(TAG, "setWatchdogTriggerEnabled: enable = " + enable);
+ if (mVerboseLoggingEnabled) Log.v(TAG, "setWatchdogTriggerEnabled: enable = " + enable);
mWatchdogAllowedToTrigger = enable;
}
@@ -417,14 +414,15 @@
sb.append("\nmRecentAvailableNetworks: ").append(mRecentAvailableNetworks.size());
for (Map.Entry<String, AvailableNetworkFailureCount> entry
: mRecentAvailableNetworks.entrySet()) {
- sb.append("\n ").append(entry.getKey()).append(": ").append(entry.getValue());
+ sb.append("\n ").append(entry.getKey()).append(": ").append(entry.getValue())
+ .append(", Age: ").append(entry.getValue().age);
}
sb.append("\nmSsidFailureCount:");
for (Map.Entry<String, Pair<AvailableNetworkFailureCount, Integer>> entry :
mSsidFailureCount.entrySet()) {
final AvailableNetworkFailureCount failureCount = entry.getValue().first;
final Integer apCount = entry.getValue().second;
- sb.append("\n").append(entry.getKey()).append(": ").append(apCount).append(", ")
+ sb.append("\n").append(entry.getKey()).append(": ").append(apCount).append(",")
.append(failureCount.toString());
}
return sb.toString();
@@ -457,9 +455,7 @@
String ssid = availableNetworkFailureCount.ssid;
Pair<AvailableNetworkFailureCount, Integer> ssidFails = mSsidFailureCount.get(ssid);
if (ssidFails == null) {
- if (DBG) {
- Log.d(TAG, "getFailureCount: Could not find SSID count for " + ssid);
- }
+ Log.d(TAG, "getFailureCount: Could not find SSID count for " + ssid);
return 0;
}
final AvailableNetworkFailureCount failCount = ssidFails.first;
@@ -475,6 +471,14 @@
}
}
+ protected void enableVerboseLogging(int verbose) {
+ if (verbose > 0) {
+ mVerboseLoggingEnabled = true;
+ } else {
+ mVerboseLoggingEnabled = false;
+ }
+ }
+
/**
* This class holds the failure counts for an 'available network' (one of the potential
* candidates for connection, as determined by framework).
@@ -537,24 +541,13 @@
}
public String toString() {
- return ssid + ", HasEverConnected: " + ((config != null)
+ return ssid + " HasEverConnected: " + ((config != null)
? config.getNetworkSelectionStatus().getHasEverConnected() : "null_config")
+ ", Failures: {"
+ "Assoc: " + associationRejection
+ ", Auth: " + authenticationFailure
+ ", Dhcp: " + dhcpFailure
- + "}"
- + ", Age: " + age;
+ + "}";
}
}
-
- /**
- * Method used to set the WifiController for the this watchdog.
- *
- * The WifiController is used to send the restart wifi command to carry out the wifi restart.
- * @param wifiController WifiController instance that will be sent the CMD_RESTART_WIFI message.
- */
- public void setWifiController(WifiController wifiController) {
- mWifiController = wifiController;
- }
}
diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java
index 0dc5ccf..0eaf029 100644
--- a/service/java/com/android/server/wifi/WifiMetrics.java
+++ b/service/java/com/android/server/wifi/WifiMetrics.java
@@ -77,7 +77,7 @@
*/
private final SparseIntArray mWifiSystemStateEntries = new SparseIntArray();
/**
- * Records the elapsedRealtime (in seconds) that represents the beginning of data
+ * Records the getElapsedSinceBootMillis (in seconds) that represents the beginning of data
* capture for for this WifiMetricsProto
*/
private final SparseIntArray mRssiPollCounts = new SparseIntArray();
@@ -313,7 +313,7 @@
mCurrentConnectionEvent = null;
mScreenOn = true;
mWifiState = WifiMetricsProto.WifiLog.WIFI_DISABLED;
- mRecordStartTimeSec = mClock.elapsedRealtime() / 1000;
+ mRecordStartTimeSec = mClock.getElapsedSinceBootMillis() / 1000;
}
// Values used for indexing SystemStateEntries
@@ -356,12 +356,12 @@
}
mCurrentConnectionEvent = new ConnectionEvent();
mCurrentConnectionEvent.mConnectionEvent.startTimeMillis =
- mClock.currentTimeMillis();
+ mClock.getWallClockMillis();
mCurrentConnectionEvent.mConfigBssid = targetBSSID;
mCurrentConnectionEvent.mConnectionEvent.roamType = roamType;
mCurrentConnectionEvent.mRouterFingerPrint.updateFromWifiConfiguration(config);
mCurrentConnectionEvent.mConfigBssid = "any";
- mCurrentConnectionEvent.mRealStartTime = mClock.elapsedRealtime();
+ mCurrentConnectionEvent.mRealStartTime = mClock.getElapsedSinceBootMillis();
mCurrentConnectionEvent.mWifiState = mWifiState;
mCurrentConnectionEvent.mScreenOn = mScreenOn;
mConnectionEventList.add(mCurrentConnectionEvent);
@@ -414,7 +414,7 @@
boolean result = (level2FailureCode == 1)
&& (connectivityFailureCode == WifiMetricsProto.ConnectionEvent.HLF_NONE);
mCurrentConnectionEvent.mConnectionEvent.connectionResult = result ? 1 : 0;
- mCurrentConnectionEvent.mRealEndTime = mClock.elapsedRealtime();
+ mCurrentConnectionEvent.mRealEndTime = mClock.getElapsedSinceBootMillis();
mCurrentConnectionEvent.mConnectionEvent.durationTakenToConnectMillis = (int)
(mCurrentConnectionEvent.mRealEndTime
- mCurrentConnectionEvent.mRealStartTime);
@@ -922,7 +922,7 @@
pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadOther="
+ mWifiLogProto.numLastResortWatchdogTriggersWithBadOther);
pw.println("mWifiLogProto.recordDurationSec="
- + ((mClock.elapsedRealtime() / 1000) - mRecordStartTimeSec));
+ + ((mClock.getElapsedSinceBootMillis() / 1000) - mRecordStartTimeSec));
pw.println("mWifiLogProto.rssiPollRssiCount: Printing counts for [" + MIN_RSSI_POLL
+ ", " + MAX_RSSI_POLL + "]");
StringBuilder sb = new StringBuilder();
@@ -986,7 +986,7 @@
mWifiLogProto.wifiSystemStateEntries[i].isScreenOn =
(mWifiSystemStateEntries.keyAt(i) % 2) > 0;
}
- mWifiLogProto.recordDurationSec = (int) ((mClock.elapsedRealtime() / 1000)
+ mWifiLogProto.recordDurationSec = (int) ((mClock.getElapsedSinceBootMillis() / 1000)
- mRecordStartTimeSec);
/**
@@ -1014,7 +1014,7 @@
}
mScanReturnEntries.clear();
mWifiSystemStateEntries.clear();
- mRecordStartTimeSec = mClock.elapsedRealtime() / 1000;
+ mRecordStartTimeSec = mClock.getElapsedSinceBootMillis() / 1000;
mRssiPollCounts.clear();
mWifiLogProto.clear();
}
diff --git a/service/java/com/android/server/wifi/WifiMonitor.java b/service/java/com/android/server/wifi/WifiMonitor.java
index 1f2b397..8c0d36a 100644
--- a/service/java/com/android/server/wifi/WifiMonitor.java
+++ b/service/java/com/android/server/wifi/WifiMonitor.java
@@ -59,8 +59,7 @@
*/
public class WifiMonitor {
- private static boolean DBG = false;
- private static final boolean VDBG = false;
+ private static final boolean DBG = false;
private static final String TAG = "WifiMonitor";
/** Events we receive from the supplicant daemon */
@@ -536,12 +535,13 @@
}
private int mRecvErrors = 0;
+ private boolean mVerboseLoggingEnabled = false;
void enableVerboseLogging(int verbose) {
if (verbose > 0) {
- DBG = true;
+ mVerboseLoggingEnabled = true;
} else {
- DBG = false;
+ mVerboseLoggingEnabled = false;
}
}
@@ -589,7 +589,7 @@
return true;
}
- if (DBG) Log.d(TAG, "connecting to supplicant");
+ if (mVerboseLoggingEnabled) Log.d(TAG, "connecting to supplicant");
int connectTries = 0;
while (true) {
if (mWifiNative.connectToSupplicant()) {
@@ -625,7 +625,7 @@
}
public synchronized void stopMonitoring(String iface) {
- if (DBG) Log.d(TAG, "stopMonitoring(" + iface + ")");
+ if (mVerboseLoggingEnabled) Log.d(TAG, "stopMonitoring(" + iface + ")");
setMonitoring(iface, true);
sendMessage(iface, SUP_DISCONNECTION_EVENT);
setMonitoring(iface, false);
@@ -693,10 +693,14 @@
}
}
} else {
- if (DBG) Log.d(TAG, "Dropping event because (" + iface + ") is stopped");
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "Dropping event because (" + iface + ") is stopped");
+ }
}
} else {
- if (DBG) Log.d(TAG, "Sending to all monitors because there's no matching iface");
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "Sending to all monitors because there's no matching iface");
+ }
boolean firstHandler = true;
for (Map.Entry<String, SparseArray<Set<Handler>>> entry : mHandlerMap.entrySet()) {
if (isMonitoring(entry.getKey())) {
@@ -731,25 +735,29 @@
}
public void run() {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
Log.d(TAG, "MonitorThread start with mConnected=" + mConnected);
}
//noinspection InfiniteLoopStatement
for (;;) {
if (!mConnected) {
- if (DBG) Log.d(TAG, "MonitorThread exit because mConnected is false");
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "MonitorThread exit because mConnected is false");
+ }
break;
}
String eventStr = mWifiNative.waitForEvent();
// Skip logging the common but mostly uninteresting events
if (!eventStr.contains(BSS_ADDED_STR) && !eventStr.contains(BSS_REMOVED_STR)) {
- if (DBG) Log.d(TAG, "Event [" + eventStr + "]");
+ if (mVerboseLoggingEnabled) Log.d(TAG, "Event [" + eventStr + "]");
mLocalLog.log("Event [" + eventStr + "]");
}
if (dispatchEvent(eventStr)) {
- if (DBG) Log.d(TAG, "Disconnecting from the supplicant, no more events");
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "Disconnecting from the supplicant, no more events");
+ }
break;
}
}
@@ -782,7 +790,7 @@
iface = "p2p0";
}
- if (VDBG) Log.d(TAG, "Dispatching event to interface: " + iface);
+ if (DBG) Log.d(TAG, "Dispatching event to interface: " + iface);
if (dispatchEvent(eventStr, iface)) {
mConnected = false;
@@ -803,7 +811,7 @@
/* @return true if the event was supplicant disconnection */
private boolean dispatchEvent(String eventStr, String iface) {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
// Dont log CTRL-EVENT-BSS-ADDED which are too verbose and not handled
if (eventStr != null && !eventStr.contains("CTRL-EVENT-BSS-ADDED")) {
Log.d(TAG, iface + " cnt=" + Integer.toString(eventLogCounter)
@@ -856,7 +864,9 @@
eventStr.endsWith(AUTH_TIMEOUT_STR)) {
sendMessage(iface, AUTHENTICATION_FAILURE_EVENT);
} else {
- if (DBG) Log.w(TAG, "couldn't identify event type - " + eventStr);
+ if (mVerboseLoggingEnabled) {
+ Log.w(TAG, "couldn't identify event type - " + eventStr);
+ }
}
eventLogCounter++;
return false;
@@ -867,7 +877,9 @@
if (nameEnd != -1)
eventName = eventName.substring(0, nameEnd);
if (eventName.length() == 0) {
- if (DBG) Log.i(TAG, "Received wpa_supplicant event with empty event name");
+ if (mVerboseLoggingEnabled) {
+ Log.i(TAG, "Received wpa_supplicant event with empty event name");
+ }
eventLogCounter++;
return false;
}
@@ -978,7 +990,7 @@
*/
if (eventData.startsWith(WPA_RECV_ERROR_STR)) {
if (++mRecvErrors > MAX_RECV_ERRORS) {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
Log.d(TAG, "too many recv errors, closing connection");
}
} else {
@@ -999,7 +1011,9 @@
String BSSID = "";
int status = -1;
if (!match.find()) {
- if (DBG) Log.d(TAG, "Assoc Reject: Could not parse assoc reject string");
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "Assoc Reject: Could not parse assoc reject string");
+ }
} else {
int groupNumber = match.groupCount();
int statusGroupNumber = -1;
@@ -1018,9 +1032,9 @@
}
}
sendMessage(iface, ASSOCIATION_REJECTION_EVENT, eventLogCounter, status, BSSID);
- } else if (event == BSS_ADDED && !VDBG) {
+ } else if (event == BSS_ADDED && !DBG) {
// Ignore that event - it is not handled, and dont log it as it is too verbose
- } else if (event == BSS_REMOVED && !VDBG) {
+ } else if (event == BSS_REMOVED && !DBG) {
// Ignore that event - it is not handled, and dont log it as it is too verbose
} else {
handleEvent(event, eventData, iface);
@@ -1046,7 +1060,7 @@
* event name and " — "
*/
private void handleEvent(int event, String remainder, String iface) {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
Log.d(TAG, "handleEvent " + Integer.toString(event) + " " + remainder);
}
switch (event) {
@@ -1067,7 +1081,7 @@
break;
case UNKNOWN:
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
Log.w(TAG, "handleEvent unknown: " + Integer.toString(event) + " " + remainder);
}
break;
@@ -1345,7 +1359,9 @@
}
} else {
- if (DBG) Log.w(TAG, "couldn't identify request type - " + dataString);
+ if (mVerboseLoggingEnabled) {
+ Log.w(TAG, "couldn't identify request type - " + dataString);
+ }
}
}
@@ -1418,7 +1434,9 @@
if (newState == NetworkInfo.DetailedState.CONNECTED) {
match = mConnectedEventPattern.matcher(data);
if (!match.find()) {
- if (DBG) Log.d(TAG, "handleNetworkStateChange: Couldnt find BSSID in event string");
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "handleNetworkStateChange: Couldnt find BSSID in event string");
+ }
} else {
BSSID = match.group(1);
try {
@@ -1431,7 +1449,9 @@
} else if (newState == NetworkInfo.DetailedState.DISCONNECTED) {
match = mDisconnectedEventPattern.matcher(data);
if (!match.find()) {
- if (DBG) Log.d(TAG, "handleNetworkStateChange: Could not parse disconnect string");
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "handleNetworkStateChange: Could not parse disconnect string");
+ }
} else {
BSSID = match.group(1);
try {
@@ -1445,9 +1465,10 @@
local = -1;
}
}
- if (DBG) Log.d(TAG, "WifiMonitor notify network disconnect: "
- + BSSID
- + " reason=" + Integer.toString(reason));
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "WifiMonitor notify network disconnect: " + BSSID
+ + " reason=" + Integer.toString(reason));
+ }
sendMessage(iface, NETWORK_DISCONNECTION_EVENT, local, reason, BSSID);
}
}
diff --git a/service/java/com/android/server/wifi/WifiMulticastLockManager.java b/service/java/com/android/server/wifi/WifiMulticastLockManager.java
new file mode 100644
index 0000000..40b259d
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiMulticastLockManager.java
@@ -0,0 +1,195 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.app.IBatteryStats;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * WifiMulticastLockManager tracks holders of multicast locks and
+ * triggers enabling and disabling of filtering.
+ *
+ * @hide
+ */
+public class WifiMulticastLockManager {
+ private static final String TAG = "WifiMulticastLockManager";
+ private final List<Multicaster> mMulticasters = new ArrayList<>();
+ private int mMulticastEnabled = 0;
+ private int mMulticastDisabled = 0;
+ private boolean mVerboseLoggingEnabled = false;
+ private final IBatteryStats mBatteryStats;
+ private final FilterController mFilterController;
+
+ /** Delegate for handling state change events for multicast filtering. */
+ public interface FilterController {
+ /** Called when multicast filtering should be enabled */
+ void startFilteringMulticastPackets();
+
+ /** Called when multicast filtering should be disabled */
+ void stopFilteringMulticastPackets();
+ }
+
+ public WifiMulticastLockManager(FilterController filterController, IBatteryStats batteryStats) {
+ mBatteryStats = batteryStats;
+ mFilterController = filterController;
+ }
+
+ private class Multicaster implements IBinder.DeathRecipient {
+ String mTag;
+ int mUid;
+ IBinder mBinder;
+
+ Multicaster(String tag, IBinder binder) {
+ mTag = tag;
+ mUid = Binder.getCallingUid();
+ mBinder = binder;
+ try {
+ mBinder.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ binderDied();
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ Slog.e(TAG, "Multicaster binderDied");
+ synchronized (mMulticasters) {
+ int i = mMulticasters.indexOf(this);
+ if (i != -1) {
+ removeMulticasterLocked(i, mUid);
+ }
+ }
+ }
+
+ void unlinkDeathRecipient() {
+ mBinder.unlinkToDeath(this, 0);
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ public String toString() {
+ return "Multicaster{" + mTag + " uid=" + mUid + "}";
+ }
+ }
+
+ protected void dump(PrintWriter pw) {
+ pw.println("mMulticastEnabled " + mMulticastEnabled);
+ pw.println("mMulticastDisabled " + mMulticastDisabled);
+ pw.println("Multicast Locks held:");
+ for (Multicaster l : mMulticasters) {
+ pw.print(" ");
+ pw.println(l);
+ }
+ }
+
+ protected void enableVerboseLogging(int verbose) {
+ if (verbose > 0) {
+ mVerboseLoggingEnabled = true;
+ } else {
+ mVerboseLoggingEnabled = false;
+ }
+ }
+
+ /** Start filtering if no multicasters exist. */
+ public void initializeFiltering() {
+ synchronized (mMulticasters) {
+ // if anybody had requested filters be off, leave off
+ if (mMulticasters.size() != 0) {
+ return;
+ } else {
+ mFilterController.startFilteringMulticastPackets();
+ }
+ }
+ }
+
+ /**
+ * Acquire a multicast lock.
+ * @param binder a binder used to ensure caller is still alive
+ * @param tag string name of the caller.
+ */
+ public void acquireLock(IBinder binder, String tag) {
+ synchronized (mMulticasters) {
+ mMulticastEnabled++;
+ mMulticasters.add(new Multicaster(tag, binder));
+ // Note that we could call stopFilteringMulticastPackets only when
+ // our new size == 1 (first call), but this function won't
+ // be called often and by making the stopPacket call each
+ // time we're less fragile and self-healing.
+ mFilterController.stopFilteringMulticastPackets();
+ }
+
+ int uid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mBatteryStats.noteWifiMulticastEnabled(uid);
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /** Releases a multicast lock */
+ public void releaseLock() {
+ int uid = Binder.getCallingUid();
+ synchronized (mMulticasters) {
+ mMulticastDisabled++;
+ int size = mMulticasters.size();
+ for (int i = size - 1; i >= 0; i--) {
+ Multicaster m = mMulticasters.get(i);
+ if ((m != null) && (m.getUid() == uid)) {
+ removeMulticasterLocked(i, uid);
+ }
+ }
+ }
+ }
+
+ private void removeMulticasterLocked(int i, int uid) {
+ Multicaster removed = mMulticasters.remove(i);
+
+ if (removed != null) {
+ removed.unlinkDeathRecipient();
+ }
+ if (mMulticasters.size() == 0) {
+ mFilterController.startFilteringMulticastPackets();
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mBatteryStats.noteWifiMulticastDisabled(uid);
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /** Returns whether multicast should be allowed (filterning disabled). */
+ public boolean isMulticastEnabled() {
+ synchronized (mMulticasters) {
+ return (mMulticasters.size() > 0);
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index 73765ee..75f5915 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -18,12 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.net.apf.ApfCapabilities;
import android.net.wifi.RttManager;
import android.net.wifi.RttManager.ResponderConfig;
@@ -46,6 +41,7 @@
import android.util.Log;
import com.android.internal.annotations.Immutable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
import com.android.server.connectivity.KeepalivePacketData;
import com.android.server.wifi.hotspot2.NetworkDetail;
@@ -339,24 +335,6 @@
}
/**
- * Create a comma separate string from integer set.
- * @param values List of integers.
- * @return comma separated string.
- */
- private static String createCSVStringFromIntegerSet(Set<Integer> values) {
- StringBuilder list = new StringBuilder();
- boolean first = true;
- for (Integer value : values) {
- if (!first) {
- list.append(",");
- }
- list.append(value);
- first = false;
- }
- return list.toString();
- }
-
- /**
* Start a scan using wpa_supplicant for the given frequencies.
* @param freqs list of frequencies to scan for, if null scan all supported channels.
* @param hiddenNetworkIds List of hidden networks to be scanned for.
@@ -365,10 +343,10 @@
String freqList = null;
String hiddenNetworkIdList = null;
if (freqs != null && freqs.size() != 0) {
- freqList = createCSVStringFromIntegerSet(freqs);
+ freqList = TextUtils.join(",", freqs);
}
if (hiddenNetworkIds != null && hiddenNetworkIds.size() != 0) {
- hiddenNetworkIdList = createCSVStringFromIntegerSet(hiddenNetworkIds);
+ hiddenNetworkIdList = TextUtils.join(",", hiddenNetworkIds);
}
return scanWithParams(freqList, hiddenNetworkIdList);
}
@@ -408,17 +386,26 @@
}
public boolean setNetworkExtra(int netId, String name, Map<String, String> values) {
+ String encoded = createNetworkExtra(values);
+ if (encoded == null) {
+ return false;
+ }
+ return setNetworkVariable(netId, name, "\"" + encoded + "\"");
+ }
+
+ @VisibleForTesting
+ public static String createNetworkExtra(Map<String, String> values) {
final String encoded;
try {
encoded = URLEncoder.encode(new JSONObject(values).toString(), "UTF-8");
} catch (NullPointerException e) {
Log.e(TAG, "Unable to serialize networkExtra: " + e.toString());
- return false;
+ return null;
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Unable to serialize networkExtra: " + e.toString());
- return false;
+ return null;
}
- return setNetworkVariable(netId, name, "\"" + encoded + "\"");
+ return encoded;
}
public boolean setNetworkVariable(int netId, String name, String value) {
@@ -432,12 +419,16 @@
}
public Map<String, String> getNetworkExtra(int netId, String name) {
- final String wrapped = getNetworkVariable(netId, name);
- if (wrapped == null || !wrapped.startsWith("\"") || !wrapped.endsWith("\"")) {
+ final String extraString = getNetworkVariable(netId, name);
+ return parseNetworkExtra(extraString);
+ }
+
+ public static Map<String, String> parseNetworkExtra(String extraSting) {
+ if (extraSting == null || !extraSting.startsWith("\"") || !extraSting.endsWith("\"")) {
return null;
}
try {
- final String encoded = wrapped.substring(1, wrapped.length() - 1);
+ final String encoded = extraSting.substring(1, extraSting.length() - 1);
// This method reads a JSON dictionary that was written by setNetworkExtra(). However,
// on devices that upgraded from Marshmallow, it may encounter a legacy value instead -
// an FQDN stored as a plain string. If such a value is encountered, the JSONObject
@@ -882,7 +873,7 @@
* some of the low-level scan parameters used by the driver are changed to
* reduce interference with A2DP streaming.
*
- * @param isSet whether to enable or disable this mode
+ * @param setCoexScanMode whether to enable or disable this mode
* @return {@code true} if the command succeeded, {@code false} otherwise.
*/
public boolean setBluetoothCoexistenceScanMode(boolean setCoexScanMode) {
@@ -1557,6 +1548,7 @@
private static native void waitForHalEventNative();
private static class MonitorThread extends Thread {
+ @Override
public void run() {
Log.i(TAG, "Waiting for HAL events mWifiHalHandle=" + Long.toString(sWifiHalHandle));
waitForHalEventNative();
diff --git a/service/java/com/android/server/wifi/WifiNetworkHistory.java b/service/java/com/android/server/wifi/WifiNetworkHistory.java
index edbc516..06081f5 100644
--- a/service/java/com/android/server/wifi/WifiNetworkHistory.java
+++ b/service/java/com/android/server/wifi/WifiNetworkHistory.java
@@ -633,7 +633,10 @@
if (config == null || scanDetailCaches == null) return null;
ScanDetailCache cache = scanDetailCaches.get(config.networkId);
if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
- cache = new ScanDetailCache(config);
+ cache =
+ new ScanDetailCache(
+ config, WifiConfigManager.MAX_NUM_SCAN_CACHE_ENTRIES + 64,
+ WifiConfigManager.MAX_NUM_SCAN_CACHE_ENTRIES);
scanDetailCaches.put(config.networkId, cache);
}
return cache;
diff --git a/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java b/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java
index ff8b7f6..1dba35f 100644
--- a/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java
+++ b/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java
@@ -45,7 +45,7 @@
/**
* This class looks at all the connectivity scan results then
- * select an network for the phone to connect/roam to.
+ * selects a network for the phone to connect or roam to.
*/
public class WifiQualifiedNetworkSelector {
private WifiConfigManager mWifiConfigManager;
@@ -59,42 +59,44 @@
private boolean mDbg = FORCE_DEBUG;
private WifiConfiguration mCurrentConnectedNetwork = null;
private String mCurrentBssid = null;
- //buffer most recent scan results
- private List<ScanDetail> mScanDetails = null;
- //buffer of filtered scan results (Scan results considered by network selection) & associated
- //WifiConfiguration (if any)
+
+ // Buffer of filtered scan results (Scan results considered by network selection) & associated
+ // WifiConfiguration (if any).
private volatile List<Pair<ScanDetail, WifiConfiguration>> mFilteredScanDetails = null;
- //Minimum time gap between last successful Qualified Network Selection and new selection attempt
- //usable only when current state is connected state default 10 s
- private static final int MINIMUM_QUALIFIED_NETWORK_SELECTION_INTERVAL = 10 * 1000;
+ // Minimum time gap between last successful Qualified Network Selection and a new selection
+ // attempt.
+ private static final int MINIMUM_QUALIFIED_NETWORK_SELECTION_INTERVAL_MS = 10 * 1000;
- //if current network is on 2.4GHz band and has a RSSI over this, need not new network selection
+ // A 2.4GHz network with RSSI value above this threshold is considered qualified. No new
+ // selection attempt necessary.
public static final int QUALIFIED_RSSI_24G_BAND = -73;
- //if current network is on 5GHz band and has a RSSI over this, need not new network selection
+ // A 5GHz network with RSSI value above this threshold is considered qualified. No new
+ // selection attempt necessary.
public static final int QUALIFIED_RSSI_5G_BAND = -70;
- //any RSSI larger than this will benefit the traffic very limited
+ // A RSSI vaule larger than this threshold is considered saturated and switching to a
+ // higher RSSI value network won't benefit the connection much.
public static final int RSSI_SATURATION_2G_BAND = -60;
public static final int RSSI_SATURATION_5G_BAND = -57;
- //Any value below this will be considered not usable
+ // Any RSSI value below this is considered unacceptable, and the network will be filtered out.
public static final int MINIMUM_2G_ACCEPT_RSSI = -85;
public static final int MINIMUM_5G_ACCEPT_RSSI = -82;
+ // Constants for BSSID scoring formula.
public static final int RSSI_SCORE_SLOPE = 4;
public static final int RSSI_SCORE_OFFSET = 85;
-
public static final int BAND_AWARD_5GHz = 40;
public static final int SAME_NETWORK_AWARD = 16;
-
public static final int SAME_BSSID_AWARD = 24;
public static final int LAST_SELECTION_AWARD = 480;
public static final int PASSPOINT_SECURITY_AWARD = 40;
public static final int SECURITY_AWARD = 80;
+
+ // BSSID blacklist parameters.
public static final int BSSID_BLACKLIST_THRESHOLD = 3;
- public static final int BSSID_BLACKLIST_EXPIRE_TIME = 5 * 60 * 1000;
+ public static final int BSSID_BLACKLIST_EXPIRE_TIME_MS = 5 * 60 * 1000;
+
private final int mNoIntnetPenalty;
- //TODO: check whether we still need this one when we update the scan manager
- public static final int SCAN_RESULT_MAXIMUNM_AGE = 40000;
private static final int INVALID_TIME_STAMP = -1;
private long mLastQualifiedNetworkSelectionTimeStamp = INVALID_TIME_STAMP;
@@ -110,10 +112,11 @@
new HashMap<String, BssidBlacklistStatus>();
/**
- * class save the blacklist status of a given BSSID
+ * Class that saves the blacklist status of a given BSSID.
*/
private static class BssidBlacklistStatus {
- //how many times it is requested to be blacklisted (association rejection trigger this)
+ // Number of times this BSSID has been requested to be blacklisted.
+ // Association rejection triggers such a request.
int mCounter;
boolean mIsBlacklisted;
long mBlacklistedTimeStamp = INVALID_TIME_STAMP;
@@ -152,7 +155,7 @@
}
/**
- * set the user selected preferred band
+ * Set the user preferred band.
*
* @param band preferred band user selected
*/
@@ -171,8 +174,7 @@
mNetworkScoreCache = new WifiNetworkScoreCache(context);
mScoreManager.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache);
} else {
- localLoge("No network score service: Couldn't register as a WiFi score Manager, type="
- + NetworkKey.TYPE_WIFI + " service= " + Context.NETWORK_SCORE_SERVICE);
+ localLoge("Couldn't get NETWORK_SCORE_SERVICE.");
mNetworkScoreCache = null;
}
@@ -207,50 +209,58 @@
}
/**
- * check whether current network is good enough we need not consider any potential switch
+ * Check if the current connected network is already qualified so that network
+ * selection from the new scan results is not necessary.
*
* @param currentNetwork -- current connected network
- * @return true -- qualified and do not consider potential network switch
- * false -- not good enough and should try potential network switch
*/
- private boolean isNetworkQualified(WifiConfiguration currentNetwork) {
-
+ private boolean isCurrentNetworkQualified(WifiConfiguration currentNetwork) {
if (currentNetwork == null) {
- localLog("Disconnected");
+ localLog("No current connected network");
return false;
} else {
- localLog("Current network is: " + currentNetwork.SSID + " ,ID is: "
- + currentNetwork.networkId);
+ localLog("Current connected network: " + currentNetwork.SSID
+ + " , ID: " + currentNetwork.networkId);
}
- //if current connected network is an ephemeral network,we will consider
- // there is no current network
+ // Ephemeral networks are not qualified.
if (currentNetwork.ephemeral) {
- localLog("Current is ephemeral. Start reselect");
+ localLog("Current network is an ephemeral one");
return false;
}
- //if current network is open network, not qualified
+ // Open networks are not qualified.
if (mWifiConfigManager.isOpenNetwork(currentNetwork)) {
- localLog("Current network is open network");
+ localLog("Current network is a open one");
return false;
}
- // Current network band must match with user preference selection
- if (mWifiInfo.is24GHz() && (mUserPreferedBand != WifiManager.WIFI_FREQUENCY_BAND_2GHZ)) {
- localLog("Current band dose not match user preference. Start Qualified Network"
- + " Selection Current band = " + (mWifiInfo.is24GHz() ? "2.4GHz band"
- : "5GHz band") + "UserPreference band = " + mUserPreferedBand);
+ // Does the current network band match the user preference?
+ //
+ // Note, here the check for 2.4GHz band is different from the one for 5GHz band
+ // such that 5GHz band is always favored.
+ // When the current network is 2.4GHz, it is considered as not qualified as long
+ // as the band preference set by user is not 2.4GHz only. This gives QNS an
+ // opportunity to recommend a 5GHz network if one is available.
+ // When the current network is 5GHz, it's considered as not qualified only if
+ // the band preference set by user is 2.4GHz only.
+ if ((mWifiInfo.is24GHz() && (mUserPreferedBand != WifiManager.WIFI_FREQUENCY_BAND_2GHZ))
+ || (mWifiInfo.is5GHz()
+ && (mUserPreferedBand == WifiManager.WIFI_FREQUENCY_BAND_2GHZ))) {
+ localLog("Current network band does not match user preference: "
+ + "current network band=" + (mWifiInfo.is24GHz() ? "2.4GHz" : "5GHz")
+ + ", however user preferred band=" + mUserPreferedBand);
return false;
}
+ // Is the current network's singnal strength qualified?
int currentRssi = mWifiInfo.getRssi();
if ((mWifiInfo.is24GHz()
&& currentRssi < mWifiConfigManager.mThresholdQualifiedRssi24.get())
|| (mWifiInfo.is5GHz()
&& currentRssi < mWifiConfigManager.mThresholdQualifiedRssi5.get())) {
- localLog("Current band = " + (mWifiInfo.is24GHz() ? "2.4GHz band" : "5GHz band")
- + "current RSSI is: " + currentRssi);
+ localLog("Current network band=" + (mWifiInfo.is24GHz() ? "2.4GHz" : "5GHz")
+ + ", RSSI[" + currentRssi + "]-acceptable but not qualified");
return false;
}
@@ -258,7 +268,7 @@
}
/**
- * check whether QualifiedNetworkSelection is needed or not
+ * Check whether QualifiedNetworkSelection is needed.
*
* @param isLinkDebouncing true -- Link layer is under debouncing
* false -- Link layer is not under debouncing
@@ -268,37 +278,31 @@
* false -- WifiStateMachine is not at disconnected state
* @param isSupplicantTransientState true -- supplicant is in a transient state now
* false -- supplicant is not in a transient state now
- * @return true -- need a Qualified Network Selection procedure
- * false -- do not need a QualifiedNetworkSelection procedure
*/
private boolean needQualifiedNetworkSelection(boolean isLinkDebouncing, boolean isConnected,
boolean isDisconnected, boolean isSupplicantTransientState) {
- if (mScanDetails.size() == 0) {
- localLog("empty scan result");
- return false;
- }
-
- // Do not trigger Qualified Network Selection during L2 link debouncing procedure
+ // No Qualified Network Selection during the L2 link debouncing procedure.
if (isLinkDebouncing) {
- localLog("Need not Qualified Network Selection during L2 debouncing");
+ localLog("No QNS during L2 debouncing");
return false;
}
if (isConnected) {
- //already connected. Just try to find better candidate
- //if switch network is not allowed in connected mode, do not trigger Qualified Network
- //Selection
+ // Already connected. Looking for a better candidate.
+
+ // Is network switching allowed in connected state?
if (!mWifiConfigManager.getEnableAutoJoinWhenAssociated()) {
- localLog("Switch network under connection is not allowed");
+ localLog("Switching networks in connected state is not allowed");
return false;
}
- //Do not select again if last selection is within
- //MINIMUM_QUALIFIED_NETWORK_SELECTION_INTERVAL
+ // Do not select again if last selection is within
+ // MINIMUM_QUALIFIED_NETWORK_SELECTION_INTERVAL_MS.
if (mLastQualifiedNetworkSelectionTimeStamp != INVALID_TIME_STAMP) {
- long gap = mClock.elapsedRealtime() - mLastQualifiedNetworkSelectionTimeStamp;
- if (gap < MINIMUM_QUALIFIED_NETWORK_SELECTION_INTERVAL) {
- localLog("Too short to last successful Qualified Network Selection Gap is:"
+ long gap = mClock.getElapsedSinceBootMillis()
+ - mLastQualifiedNetworkSelectionTimeStamp;
+ if (gap < MINIMUM_QUALIFIED_NETWORK_SELECTION_INTERVAL_MS) {
+ localLog("Too short from last successful Qualified Network Selection. Gap is:"
+ gap + " ms!");
return false;
}
@@ -308,14 +312,15 @@
mWifiConfigManager.getWifiConfiguration(mWifiInfo.getNetworkId());
if (currentNetwork == null) {
// WifiStateMachine in connected state but WifiInfo is not. It means there is a race
- // condition happened. Do not make QNS until WifiStateMachine goes into
- // disconnected state
+ // condition. Defer QNS until WifiStateMachine enters the disconnected state.
+ //
+ // TODO(b/28249371): Root cause this race condition.
return false;
}
- if (!isNetworkQualified(mCurrentConnectedNetwork)) {
- //need not trigger Qualified Network Selection if current network is qualified
- localLog("Current network is not qualified");
+ // Already connected to a qualified network?
+ if (!isCurrentNetworkQualified(mCurrentConnectedNetwork)) {
+ localLog("Current connected network is not qualified");
return true;
} else {
return false;
@@ -323,12 +328,13 @@
} else if (isDisconnected) {
mCurrentConnectedNetwork = null;
mCurrentBssid = null;
- //Do not start Qualified Network Selection if current state is a transient state
+ // Defer Qualified Network Selection if wpa_supplicant is in the transient state.
if (isSupplicantTransientState) {
return false;
}
} else {
- //Do not allow new network selection in other state
+ // Do not allow new network selection if WifiStateMachine is in a state
+ // other than connected or disconnected.
localLog("WifiStateMachine is not on connected or disconnected state");
return false;
}
@@ -341,68 +347,67 @@
StringBuffer sbuf) {
int score = 0;
- //calculate the RSSI score
+ // Calculate the RSSI score.
int rssi = scanResult.level <= mWifiConfigManager.mThresholdSaturatedRssi24.get()
? scanResult.level : mWifiConfigManager.mThresholdSaturatedRssi24.get();
score += (rssi + mRssiScoreOffset) * mRssiScoreSlope;
- sbuf.append(" RSSI score: " + score);
+ sbuf.append("RSSI score: ").append(score);
+
+ // 5GHz band bonus.
if (scanResult.is5GHz()) {
- //5GHz band
score += mWifiConfigManager.mBandAward5Ghz.get();
- sbuf.append(" 5GHz bonus: " + mWifiConfigManager.mBandAward5Ghz.get());
+ sbuf.append(" 5GHz bonus: ").append(mWifiConfigManager.mBandAward5Ghz.get());
}
- //last user selection award
+ // Last user selection award.
if (sameSelect) {
- long timeDifference = mClock.elapsedRealtime()
+ long timeDifference = mClock.getElapsedSinceBootMillis()
- mWifiConfigManager.getLastSelectedTimeStamp();
if (timeDifference > 0) {
int bonus = mLastSelectionAward - (int) (timeDifference / 1000 / 60);
score += bonus > 0 ? bonus : 0;
- sbuf.append(" User selected it last time " + (timeDifference / 1000 / 60)
- + " minutes ago, bonus:" + bonus);
+ sbuf.append(" User selected it last time ").append(timeDifference / 1000 / 60)
+ .append(" minutes ago, bonus: ").append(bonus);
}
}
- //same network award
+ // Same network award.
if (network == currentNetwork || network.isLinked(currentNetwork)) {
score += mWifiConfigManager.mCurrentNetworkBoost.get();
- sbuf.append(" Same network with current associated. Bonus: "
- + mWifiConfigManager.mCurrentNetworkBoost.get());
+ sbuf.append(" Same network as the current one, bonus: ")
+ .append(mWifiConfigManager.mCurrentNetworkBoost.get());
}
- //same BSSID award
+ // Same BSSID award.
if (sameBssid) {
score += mSameBssidAward;
- sbuf.append(" Same BSSID with current association. Bonus: " + mSameBssidAward);
+ sbuf.append(" Same BSSID as the current one, bonus: ").append(mSameBssidAward);
}
- //security award
+ // Security award.
if (network.isPasspoint()) {
score += mPasspointSecurityAward;
- sbuf.append(" Passpoint Bonus:" + mPasspointSecurityAward);
+ sbuf.append(" Passpoint bonus: ").append(mPasspointSecurityAward);
} else if (!mWifiConfigManager.isOpenNetwork(network)) {
score += mSecurityAward;
- sbuf.append(" Secure network Bonus:" + mSecurityAward);
+ sbuf.append(" Secure network bonus: ").append(mSecurityAward);
}
- //Penalty for no internet network. Make sure if there is any network with Internet,
- //however, if there is no any other network with internet, this network can be chosen
+ // No internet penalty.
if (network.numNoInternetAccessReports > 0 && !network.validatedInternetAccess) {
score -= mNoIntnetPenalty;
- sbuf.append(" No internet Penalty:-" + mNoIntnetPenalty);
+ sbuf.append(" No internet penalty: -").append(mNoIntnetPenalty);
}
-
- sbuf.append(" Score for scanResult: " + scanResult + " and Network ID: "
- + network.networkId + " final score:" + score + "\n\n");
+ sbuf.append(" -- ScanResult: ").append(scanResult).append(" for network: ")
+ .append(network.networkId).append(" score: ").append(score).append(" --\n");
return score;
}
/**
- * This API try to update all the saved networks' network selection status
+ * Update all the saved networks' selection status
*/
private void updateSavedNetworkSelectionStatus() {
List<WifiConfiguration> savedNetworks = mWifiConfigManager.getSavedNetworks();
@@ -411,62 +416,65 @@
return;
}
- StringBuffer sbuf = new StringBuffer("Saved Network List\n");
+ StringBuffer sbuf = new StringBuffer("Saved Network List: \n");
for (WifiConfiguration network : savedNetworks) {
WifiConfiguration config = mWifiConfigManager.getWifiConfiguration(network.networkId);
WifiConfiguration.NetworkSelectionStatus status =
config.getNetworkSelectionStatus();
- //If the configuration is temporarily disabled, try to re-enable it
+ // If a configuration is temporarily disabled, re-enable it before trying
+ // to connect to it.
if (status.isNetworkTemporaryDisabled()) {
mWifiConfigManager.tryEnableQualifiedNetwork(network.networkId);
}
- //clean the cached candidate, score and seen
+ // Clear the cached candidate, score and seen.
status.setCandidate(null);
status.setCandidateScore(Integer.MIN_VALUE);
status.setSeenInLastQualifiedNetworkSelection(false);
- //print the debug messages
- sbuf.append(" " + getNetworkString(network) + " " + " User Preferred BSSID:"
- + network.BSSID + " FQDN:" + network.FQDN + " "
- + status.getNetworkStatusString() + " Disable account: ");
- for (int index = status.NETWORK_SELECTION_ENABLE;
- index < status.NETWORK_SELECTION_DISABLED_MAX; index++) {
- sbuf.append(status.getDisableReasonCounter(index) + " ");
+ sbuf.append(" ").append(getNetworkString(network)).append(" ")
+ .append(" User Preferred BSSID: ").append(network.BSSID)
+ .append(" FQDN: ").append(network.FQDN).append(" ")
+ .append(status.getNetworkStatusString()).append(" Disable account: ");
+ for (int index = WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE;
+ index < WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX;
+ index++) {
+ sbuf.append(status.getDisableReasonCounter(index)).append(" ");
}
- sbuf.append("Connect Choice:" + status.getConnectChoice() + " set time:"
- + status.getConnectChoiceTimestamp());
- sbuf.append("\n");
+ sbuf.append("Connect Choice: ").append(status.getConnectChoice())
+ .append(" set time: ").append(status.getConnectChoiceTimestamp())
+ .append("\n");
}
localLog(sbuf.toString());
}
/**
- * This API is called when user explicitly select a network. Currently, it is used in following
+ * This API is called when user explicitly selects a network. Currently, it is used in following
* cases:
- * (1) User explicitly choose to connect to a saved network
- * (2) User save a network after add a new network
- * (3) User save a network after modify a saved network
+ * (1) User explicitly chooses to connect to a saved network.
+ * (2) User saves a network after adding a new network.
+ * (3) User saves a network after modifying a saved network.
* Following actions will be triggered:
- * 1. if this network is disabled, we need re-enable it again
- * 2. we considered user prefer this network over all the networks visible in latest network
- * selection procedure
+ * 1. If this network is disabled, we need re-enable it again.
+ * 2. This network is favored over all the other networks visible in latest network
+ * selection procedure.
*
- * @param netId new network ID for either the network the user choose or add
- * @param persist whether user has the authority to overwrite current connect choice
- * @return true -- There is change made to connection choice of any saved network
- * false -- There is no change made to connection choice of any saved network
+ * @param netId ID for the network chosen by the user
+ * @param persist whether user has the authority to overwrite current connect choice
+ * @return true -- There is change made to connection choice of any saved network.
+ * false -- There is no change made to connection choice of any saved network.
*/
public boolean userSelectNetwork(int netId, boolean persist) {
+ localLog("userSelectNetwork: network ID=" + netId + " persist=" + persist);
+
WifiConfiguration selected = mWifiConfigManager.getWifiConfiguration(netId);
- localLog("userSelectNetwork:" + netId + " persist:" + persist);
if (selected == null || selected.SSID == null) {
- localLoge("userSelectNetwork: Bad configuration with nid=" + netId);
+ localLoge("userSelectNetwork: Invalid configuration with nid=" + netId);
return false;
}
-
+ // Enable the network if it is disabled.
if (!selected.getNetworkSelectionStatus().isNetworkEnabled()) {
mWifiConfigManager.updateNetworkSelectionStatus(netId,
WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
@@ -480,7 +488,7 @@
boolean change = false;
String key = selected.configKey();
// This is only used for setting the connect choice timestamp for debugging purposes.
- long currentTime = mClock.currentTimeMillis();
+ long currentTime = mClock.getWallClockMillis();
List<WifiConfiguration> savedNetworks = mWifiConfigManager.getSavedNetworks();
for (WifiConfiguration network : savedNetworks) {
@@ -502,14 +510,15 @@
if (status.getSeenInLastQualifiedNetworkSelection()
&& (status.getConnectChoice() == null
|| !status.getConnectChoice().equals(key))) {
- localLog("Add key:" + key + " Set Time: " + currentTime + " to "
+ localLog("Add key: " + key + " Set Time: " + currentTime + " to "
+ getNetworkString(config));
status.setConnectChoice(key);
status.setConnectChoiceTimestamp(currentTime);
change = true;
}
}
- //Write this change to file
+
+ // Persist changes.
if (change) {
mWifiConfigManager.writeKnownNetworkHistory();
return true;
@@ -519,7 +528,7 @@
}
/**
- * enable/disable a BSSID for Quality Network Selection
+ * Enable/disable a BSSID for Quality Network Selection
* When an association rejection event is obtained, Quality Network Selector will disable this
* BSSID but supplicant still can try to connect to this bssid. If supplicant connect to it
* successfully later, this bssid can be re-enabled.
@@ -535,7 +544,7 @@
if (bssid != null) {
BssidBlacklistStatus status = mBssidBlacklist.get(bssid);
if (status == null) {
- //first time
+ // First time for this BSSID
BssidBlacklistStatus newStatus = new BssidBlacklistStatus();
newStatus.mCounter++;
mBssidBlacklist.put(bssid, newStatus);
@@ -543,7 +552,7 @@
status.mCounter++;
if (status.mCounter >= BSSID_BLACKLIST_THRESHOLD) {
status.mIsBlacklisted = true;
- status.mBlacklistedTimeStamp = mClock.elapsedRealtime();
+ status.mBlacklistedTimeStamp = mClock.getElapsedSinceBootMillis();
return true;
}
}
@@ -553,18 +562,18 @@
}
/**
- * update the buffered BSSID blacklist
+ * Update the buffered BSSID blacklist
*
* Go through the whole buffered BSSIDs blacklist and check when the BSSIDs is blocked. If they
- * were blacked before BSSID_BLACKLIST_EXPIRE_TIME, re-enable it again.
+ * have been blacklisted for BSSID_BLACKLIST_EXPIRE_TIME_MS, re-enable them.
*/
private void updateBssidBlacklist() {
Iterator<BssidBlacklistStatus> iter = mBssidBlacklist.values().iterator();
while (iter.hasNext()) {
BssidBlacklistStatus status = iter.next();
if (status != null && status.mIsBlacklisted) {
- if (mClock.elapsedRealtime() - status.mBlacklistedTimeStamp
- >= BSSID_BLACKLIST_EXPIRE_TIME) {
+ if (mClock.getElapsedSinceBootMillis() - status.mBlacklistedTimeStamp
+ >= BSSID_BLACKLIST_EXPIRE_TIME_MS) {
iter.remove();
}
}
@@ -574,8 +583,6 @@
/**
* Check whether a bssid is disabled
* @param bssid -- the bssid to check
- * @return true -- bssid is disabled
- * false -- bssid is not disabled
*/
public boolean isBssidDisabled(String bssid) {
BssidBlacklistStatus status = mBssidBlacklist.get(bssid);
@@ -583,35 +590,36 @@
}
/**
- * ToDo: This should be called in Connectivity Manager when it gets new scan result
- * check whether a network slection is needed. If need, check all the new scan results and
- * select a new qualified network/BSSID to connect to
+ * Select the best network candidate from the new scan results for WifiConnectivityManager
+ * to connect/roam to.
*
- * @param forceSelectNetwork true -- start a qualified network selection anyway,no matter
- * current network is already qualified or not.
- * false -- if current network is already qualified, do not do new
- * selection
- * @param isUntrustedConnectionsAllowed true -- user allow to connect to untrusted network
- * false -- user do not allow to connect to untrusted
- * network
- * @param scanDetails latest scan result obtained (should be connectivity scan only)
- * @param isLinkDebouncing true -- Link layer is under debouncing
- * false -- Link layer is not under debouncing
- * @param isConnected true -- device is connected to an AP currently
- * false -- device is not connected to an AP currently
- * @param isDisconnected true -- WifiStateMachine is at disconnected state
- * false -- WifiStateMachine is not at disconnected state
- * @param isSupplicantTransient true -- supplicant is in a transient state
- * false -- supplicant is not in a transient state
- * @return the qualified network candidate found. If no available candidate, return null
+ * @param forceSelectNetwork true -- start a qualified network selection anyway, no matter
+ * the current network is already qualified or not.
+ * false -- if current network is already qualified, stay connected
+ * to it.
+ * @param isUntrustedConnectionsAllowed connection to untrusted networks is allowed or not
+ * @param isLinkDebouncing Link layer is under debouncing or not
+ * @param isConnected WifiStateMachine is in the Connected state or not
+ * @param isDisconnected WifiStateMachine is in the Disconnected state or not
+ * @param isSupplicantTransient wpa_supplicant is in a transient state or not
+ * @param scanDetails new connectivity scan results
+ * @return Best network candidate identified. Null if no candidate available or we should
+ * stay connected to the current network.
*/
- public WifiConfiguration selectQualifiedNetwork(boolean forceSelectNetwork ,
- boolean isUntrustedConnectionsAllowed, List<ScanDetail> scanDetails,
- boolean isLinkDebouncing, boolean isConnected, boolean isDisconnected,
- boolean isSupplicantTransient) {
+ public WifiConfiguration selectQualifiedNetwork(boolean forceSelectNetwork,
+ boolean isUntrustedConnectionsAllowed, boolean isLinkDebouncing,
+ boolean isConnected, boolean isDisconnected, boolean isSupplicantTransient,
+ List<ScanDetail> scanDetails) {
localLog("==========start qualified Network Selection==========");
- mScanDetails = scanDetails;
- List<Pair<ScanDetail, WifiConfiguration>> filteredScanDetails = new ArrayList<>();
+
+ List<Pair<ScanDetail, WifiConfiguration>> filteredScanDetails = new ArrayList<>();
+
+ if (scanDetails.size() == 0) {
+ localLog("Empty connectivity scan result");
+ mFilteredScanDetails = filteredScanDetails;
+ return null;
+ }
+
if (mCurrentConnectedNetwork == null) {
mCurrentConnectedNetwork =
mWifiConfigManager.getWifiConfiguration(mWifiInfo.getNetworkId());
@@ -623,8 +631,7 @@
if (!forceSelectNetwork && !needQualifiedNetworkSelection(isLinkDebouncing, isConnected,
isDisconnected, isSupplicantTransient)) {
- localLog("Quit qualified Network Selection since it is not forced and current network"
- + " is qualified already");
+ localLog("Stay connected to the current qualified network");
mFilteredScanDetails = filteredScanDetails;
return null;
}
@@ -639,8 +646,8 @@
mWifiConfigManager.getWifiConfiguration(lastUserSelectedNetWorkKey);
if (lastUserSelectedNetwork != null) {
localLog("Last selection is " + lastUserSelectedNetwork.SSID + " Time to now: "
- + ((mClock.elapsedRealtime() - mWifiConfigManager.getLastSelectedTimeStamp())
- / 1000 / 60 + " minutes"));
+ + ((mClock.getElapsedSinceBootMillis()
+ - mWifiConfigManager.getLastSelectedTimeStamp()) / 1000 / 60 + " minutes"));
}
updateSavedNetworkSelectionStatus();
@@ -649,51 +656,65 @@
StringBuffer lowSignalScan = new StringBuffer();
StringBuffer notSavedScan = new StringBuffer();
StringBuffer noValidSsid = new StringBuffer();
+ StringBuffer unwantedBand = new StringBuffer();
StringBuffer scoreHistory = new StringBuffer();
ArrayList<NetworkKey> unscoredNetworks = new ArrayList<NetworkKey>();
- //iterate all scan results and find the best candidate with the highest score
- for (ScanDetail scanDetail : mScanDetails) {
+ // Iterate over all scan results to find the best candidate.
+ for (ScanDetail scanDetail : scanDetails) {
ScanResult scanResult = scanDetail.getScanResult();
- //skip bad scan result
+ // Skip bad scan result.
if (scanResult.SSID == null || TextUtils.isEmpty(scanResult.SSID)) {
if (mDbg) {
- //We should not see this in ePNO
- noValidSsid.append(scanResult.BSSID + " / ");
+ noValidSsid.append(scanResult.BSSID).append(" / ");
}
continue;
}
final String scanId = toScanId(scanResult);
- //check whether this BSSID is blocked or not
+ // Skip blacklisted BSSID.
if (mWifiConfigManager.isBssidBlacklisted(scanResult.BSSID)
|| isBssidDisabled(scanResult.BSSID)) {
- //We should not see this in ePNO
- Log.e(TAG, scanId + " is in blacklist.");
+ Log.i(TAG, scanId + " is in the blacklist.");
continue;
}
- //skip scan result with too weak signals
+ // Skip network with too weak signals.
if ((scanResult.is24GHz() && scanResult.level
< mWifiConfigManager.mThresholdMinimumRssi24.get())
|| (scanResult.is5GHz() && scanResult.level
< mWifiConfigManager.mThresholdMinimumRssi5.get())) {
if (mDbg) {
- lowSignalScan.append(scanId + "(" + (scanResult.is24GHz() ? "2.4GHz" : "5GHz")
- + ")" + scanResult.level + " / ");
+ lowSignalScan.append(scanId).append("(")
+ .append(scanResult.is24GHz() ? "2.4GHz" : "5GHz")
+ .append(")").append(scanResult.level).append(" / ");
}
continue;
}
- //check if there is already a score for this network
+ // Skip network not matching band preference set by user.
+ // WifiConnectivityManager schedules scan according to the user band prefrence. This is
+ // a check for the ScanResults generated from the old settings.
+ if ((scanResult.is24GHz()
+ && (mUserPreferedBand == WifiManager.WIFI_FREQUENCY_BAND_5GHZ))
+ || (scanResult.is5GHz()
+ && (mUserPreferedBand == WifiManager.WIFI_FREQUENCY_BAND_2GHZ))) {
+ if (mDbg) {
+ unwantedBand.append(scanId).append("(")
+ .append(scanResult.is24GHz() ? "2.4GHz" : "5GHz")
+ .append(")").append(" / ");
+ }
+ continue;
+ }
+
+ // Is there a score for this network? If not, request a score.
if (mNetworkScoreCache != null && !mNetworkScoreCache.isScoredNetwork(scanResult)) {
- //no score for this network yet.
WifiKey wifiKey;
try {
wifiKey = new WifiKey("\"" + scanResult.SSID + "\"", scanResult.BSSID);
NetworkKey ntwkKey = new NetworkKey(wifiKey);
- //add to the unscoredNetworks list so we can request score later
+ // Add to the unscoredNetworks list so we can request score later
unscoredNetworks.add(ntwkKey);
} catch (IllegalArgumentException e) {
Log.w(TAG, "Invalid SSID=" + scanResult.SSID + " BSSID=" + scanResult.BSSID
@@ -701,7 +722,7 @@
}
}
- //check whether this scan result belong to a saved network
+ // Is this scan result from an ephemeral network?
boolean potentiallyEphemeral = false;
// Stores WifiConfiguration of potential connection candidates for scan result filtering
WifiConfiguration potentialEphemeralCandidate = null;
@@ -711,10 +732,10 @@
if (associatedWifiConfigurations == null) {
potentiallyEphemeral = true;
if (mDbg) {
- notSavedScan.append(scanId + " / ");
+ notSavedScan.append(scanId).append(" / ");
}
} else if (associatedWifiConfigurations.size() == 1) {
- //if there are more than 1 associated network, it must be a passpoint network
+ // If there is more than one associated network, it must be a passpoint network.
WifiConfiguration network = associatedWifiConfigurations.get(0);
if (network.ephemeral) {
potentialEphemeralCandidate = network;
@@ -728,7 +749,7 @@
if (isUntrustedConnectionsAllowed) {
Integer netScore = getNetworkScore(scanResult, false);
if (netScore != null
- && !mWifiConfigManager.wasEphemeralNetworkDeleted(scanResult.SSID)) {
+ && !mWifiConfigManager.wasEphemeralNetworkDeleted(scanResult.SSID)) {
externalScoreEvaluator.evalUntrustedCandidate(netScore, scanResult);
// scanDetail is for available ephemeral network
filteredScanDetails.add(Pair.create(scanDetail,
@@ -738,9 +759,9 @@
continue;
}
- // calculate the score of each scanresult whose associated network is not ephemeral. Due
- // to one scan result can associated with more than 1 network, we need calculate all
- // the scores and use the highest one as the scanresults score.
+ // Calculate the score of each ScanResult whose associated network is not ephemeral.
+ // One ScanResult can associated with more than one network, hence we calculate all
+ // the scores and use the highest one as the ScanResult's score
int highestScore = Integer.MIN_VALUE;
int score;
WifiConfiguration configurationCandidateForThisScan = null;
@@ -756,9 +777,9 @@
continue;
} else if (network.BSSID != null && !network.BSSID.equals("any")
&& !network.BSSID.equals(scanResult.BSSID)) {
- //in such scenario, user (APP) has specified the only BSSID to connect for this
- // configuration. So only the matched scan result can be candidate
- localLog("Network: " + getNetworkString(network) + " has specified" + "BSSID:"
+ // App has specified the only BSSID to connect for this
+ // configuration. So only the matching ScanResult can be a candidate.
+ localLog("Network " + getNetworkString(network) + " has specified BSSID "
+ network.BSSID + ". Skip " + scanResult.BSSID);
continue;
}
@@ -780,13 +801,13 @@
configurationCandidateForThisScan = network;
potentialCandidate = network;
}
- //update the cached candidate
+ // Update the cached candidate.
if (score > status.getCandidateScore()) {
status.setCandidate(scanResult);
status.setCandidateScore(score);
}
}
- // Create potential filteredScanDetail entry
+ // Create potential filteredScanDetail entry.
filteredScanDetails.add(Pair.create(scanDetail, potentialCandidate));
if (highestScore > currentHighestScore || (highestScore == currentHighestScore
@@ -800,7 +821,7 @@
mFilteredScanDetails = filteredScanDetails;
- //kick the score manager if there is any unscored network
+ // Kick the score manager if there is any unscored network.
if (mScoreManager != null && unscoredNetworks.size() != 0) {
NetworkKey[] unscoredNetworkKeys =
unscoredNetworks.toArray(new NetworkKey[unscoredNetworks.size()]);
@@ -808,13 +829,22 @@
}
if (mDbg) {
- localLog(lowSignalScan + " skipped due to low signal\n");
- localLog(notSavedScan + " skipped due to not saved\n ");
- localLog(noValidSsid + " skipped due to not valid SSID\n");
+ if (lowSignalScan.length() != 0) {
+ localLog(lowSignalScan + " skipped due to low signal");
+ }
+ if (notSavedScan.length() != 0) {
+ localLog(notSavedScan + " skipped due to not saved");
+ }
+ if (noValidSsid.length() != 0) {
+ localLog(noValidSsid + " skipped due to invalid SSID");
+ }
+ if (unwantedBand.length() != 0) {
+ localLog(unwantedBand + " skipped due to user band preference");
+ }
localLog(scoreHistory.toString());
}
- //we need traverse the whole user preference to choose the one user like most now
+ // Traverse the whole user preference to choose the one user likes the most.
if (scanResultCandidate != null) {
WifiConfiguration tempConfig = networkCandidate;
@@ -830,12 +860,12 @@
networkCandidate = tempConfig;
}
} else {
- //we should not come here in theory
+ // We should not come here in theory.
localLoge("Connect choice: " + key + " has no corresponding saved config");
break;
}
}
- localLog("After user choice adjust, the final candidate is:"
+ localLog("After user choice adjustment, the final candidate is:"
+ getNetworkString(networkCandidate) + " : " + scanResultCandidate.BSSID);
}
@@ -857,27 +887,15 @@
String currentAssociationId = mCurrentConnectedNetwork == null ? "Disconnected" :
getNetworkString(mCurrentConnectedNetwork);
String targetAssociationId = getNetworkString(networkCandidate);
- //In passpoint, saved configuration has garbage SSID. We need update it with the SSID of
- //the scan result.
+ // In passpoint, saved configuration is initialized with a fake SSID. Now update it with
+ // the real SSID from the scan result.
if (networkCandidate.isPasspoint()) {
- // This will update the passpoint configuration in WifiConfigManager
networkCandidate.SSID = "\"" + scanResultCandidate.SSID + "\"";
}
- //For debug purpose only
- if (scanResultCandidate.BSSID.equals(mCurrentBssid)) {
- localLog(currentAssociationId + " is already the best choice!");
- } else if (mCurrentConnectedNetwork != null
- && (mCurrentConnectedNetwork.networkId == networkCandidate.networkId
- || mCurrentConnectedNetwork.isLinked(networkCandidate))) {
- localLog("Roaming from " + currentAssociationId + " to " + targetAssociationId);
- } else {
- localLog("reconnect from " + currentAssociationId + " to " + targetAssociationId);
- }
-
mCurrentBssid = scanResultCandidate.BSSID;
mCurrentConnectedNetwork = networkCandidate;
- mLastQualifiedNetworkSelectionTimeStamp = mClock.elapsedRealtime();
+ mLastQualifiedNetworkSelectionTimeStamp = mClock.getElapsedSinceBootMillis();
return networkCandidate;
}
@@ -958,7 +976,7 @@
: String.format("%s:%s", scanResult.SSID, scanResult.BSSID);
}
- //Dump the logs
+ // Dump the logs.
void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Dump of WifiQualifiedNetworkSelector");
pw.println("WifiQualifiedNetworkSelector - Log Begin ----");
diff --git a/service/java/com/android/server/wifi/WifiScoreReport.java b/service/java/com/android/server/wifi/WifiScoreReport.java
index 50e28bf..dfdf3cd 100644
--- a/service/java/com/android/server/wifi/WifiScoreReport.java
+++ b/service/java/com/android/server/wifi/WifiScoreReport.java
@@ -97,12 +97,8 @@
WifiConfigManager wifiConfigManager,
NetworkAgent networkAgent,
WifiScoreReport lastReport,
- int aggressiveHandover) {
- boolean debugLogging = false;
- if (wifiConfigManager.mEnableVerboseLogging.get() > 0) {
- debugLogging = true;
- }
-
+ int aggressiveHandover,
+ boolean verboseLogging) {
StringBuilder sb = new StringBuilder();
int score = STARTING_SCORE;
@@ -258,7 +254,7 @@
currentConfiguration.numTicksAtNotHighRSSI));
}
- if (debugLogging) {
+ if (verboseLogging) {
String rssiStatus = "";
if (isBadRSSI) {
rssiStatus += " badRSSI ";
@@ -286,7 +282,7 @@
wifiInfo.linkStuckCount += 1;
}
sb.append(String.format(" ls+=%d", wifiInfo.linkStuckCount));
- if (debugLogging) {
+ if (verboseLogging) {
Log.d(TAG, " bad link -> stuck count ="
+ Integer.toString(wifiInfo.linkStuckCount));
}
@@ -295,7 +291,7 @@
wifiInfo.linkStuckCount -= 1;
}
sb.append(String.format(" ls-=%d", wifiInfo.linkStuckCount));
- if (debugLogging) {
+ if (verboseLogging) {
Log.d(TAG, " good link -> stuck count ="
+ Integer.toString(wifiInfo.linkStuckCount));
}
@@ -311,7 +307,7 @@
if (isBadLinkspeed) {
score -= BAD_LINKSPEED_PENALTY;
- if (debugLogging) {
+ if (verboseLogging) {
Log.d(TAG, " isBadLinkspeed ---> count=" + badLinkspeedcount
+ " score=" + Integer.toString(score));
}
@@ -338,7 +334,7 @@
score -= wifiInfo.badRssiCount * BAD_RSSI_COUNT_PENALTY + wifiInfo.lowRssiCount;
sb.append(String.format(",%d", score));
- if (debugLogging) {
+ if (verboseLogging) {
Log.d(TAG, " badRSSI count" + Integer.toString(wifiInfo.badRssiCount)
+ " lowRSSI count" + Integer.toString(wifiInfo.lowRssiCount)
+ " --> score " + Integer.toString(score));
@@ -346,7 +342,7 @@
if (isHighRSSI) {
score += 5;
- if (debugLogging) Log.d(TAG, " isHighRSSI ---> score=" + Integer.toString(score));
+ if (verboseLogging) Log.d(TAG, " isHighRSSI ---> score=" + score);
}
sb.append(String.format(",%d]", score));
@@ -362,7 +358,7 @@
//report score
if (score != wifiInfo.score) {
- if (debugLogging) {
+ if (verboseLogging) {
Log.d(TAG, "calculateWifiScore() report new score " + Integer.toString(score));
}
wifiInfo.score = score;
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index be24e49..cb107f5 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -73,7 +73,6 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
@@ -82,13 +81,10 @@
import android.util.Log;
import android.util.Slog;
-import com.android.internal.R;
-import com.android.internal.app.IBatteryStats;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.AsyncChannel;
-import com.android.server.am.BatteryStatsService;
import com.android.server.wifi.configparse.ConfigBuilder;
import org.xml.sax.SAXException;
@@ -123,19 +119,12 @@
private static final String TAG = "WifiService";
private static final boolean DBG = true;
private static final boolean VDBG = false;
- private static final String BOOT_DEFAULT_WIFI_COUNTRY_CODE = "ro.boot.wificountrycode";
final WifiStateMachine mWifiStateMachine;
private final Context mContext;
private final FrameworkFacade mFacade;
- private final List<Multicaster> mMulticasters =
- new ArrayList<Multicaster>();
- private int mMulticastEnabled;
- private int mMulticastDisabled;
-
- private final IBatteryStats mBatteryStats;
private final PowerManager mPowerManager;
private final AppOpsManager mAppOps;
private final UserManager mUserManager;
@@ -155,6 +144,9 @@
private final WifiCertManager mCertManager;
private final WifiInjector mWifiInjector;
+ /* Backup/Restore Module */
+ private final WifiBackupRestore mWifiBackupRestore;
+
/**
* Asynchronous channel to WifiStateMachine
*/
@@ -254,7 +246,7 @@
}
private void replyFailed(Message msg, int what, int why) {
- Message reply = msg.obtain();
+ Message reply = Message.obtain();
reply.what = what;
reply.arg1 = why;
try {
@@ -306,48 +298,38 @@
}
WifiStateMachineHandler mWifiStateMachineHandler;
-
private WifiController mWifiController;
private final WifiLockManager mWifiLockManager;
+ private final WifiMulticastLockManager mWifiMulticastLockManager;
public WifiServiceImpl(Context context) {
mContext = context;
- mWifiInjector = WifiInjector.getInstance();
- mFacade = new FrameworkFacade();
- HandlerThread wifiThread = new HandlerThread("WifiService");
- wifiThread.start();
+ mWifiInjector = new WifiInjector(context);
+
+ mFacade = mWifiInjector.getFrameworkFacade();
mWifiMetrics = mWifiInjector.getWifiMetrics();
- mTrafficPoller = new WifiTrafficPoller(mContext, wifiThread.getLooper(),
- WifiNative.getWlanNativeInterface().getInterfaceName());
+ mTrafficPoller = mWifiInjector.getWifiTrafficPoller();
mUserManager = UserManager.get(mContext);
- HandlerThread wifiStateMachineThread = new HandlerThread("WifiStateMachine");
- wifiStateMachineThread.start();
- mCountryCode = new WifiCountryCode(
- WifiNative.getWlanNativeInterface(),
- SystemProperties.get(BOOT_DEFAULT_WIFI_COUNTRY_CODE),
- mFacade.getStringSetting(mContext, Settings.Global.WIFI_COUNTRY_CODE),
- mContext.getResources().getBoolean(
- R.bool.config_wifi_revert_country_code_on_cellular_loss));
- mWifiStateMachine = new WifiStateMachine(mContext, mFacade,
- wifiStateMachineThread.getLooper(), mUserManager, mWifiInjector,
- new BackupManagerProxy(), mCountryCode);
- mSettingsStore = new WifiSettingsStore(mContext);
+ mCountryCode = mWifiInjector.getWifiCountryCode();
+ mWifiStateMachine = mWifiInjector.getWifiStateMachine();
mWifiStateMachine.enableRssiPolling(true);
- mBatteryStats = BatteryStatsService.getService();
- mPowerManager = context.getSystemService(PowerManager.class);
- mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
- mCertManager = new WifiCertManager(mContext);
+ mSettingsStore = mWifiInjector.getWifiSettingsStore();
+ mPowerManager = mContext.getSystemService(PowerManager.class);
+ mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mCertManager = mWifiInjector.getWifiCertManager();
- mNotificationController = new WifiNotificationController(mContext,
- wifiThread.getLooper(), mWifiStateMachine, mFacade, null);
+ mNotificationController = mWifiInjector.getWifiNotificationController();
- mWifiLockManager = new WifiLockManager(mContext, mBatteryStats);
- mClientHandler = new ClientHandler(wifiThread.getLooper());
- mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper());
- mWifiController = new WifiController(mContext, mWifiStateMachine,
- mSettingsStore, mWifiLockManager, wifiThread.getLooper(), mFacade);
- // Set the WifiController for WifiLastResortWatchdog
- mWifiInjector.getWifiLastResortWatchdog().setWifiController(mWifiController);
+ mWifiLockManager = mWifiInjector.getWifiLockManager();
+ mWifiMulticastLockManager = mWifiInjector.getWifiMulticastLockManager();
+ HandlerThread wifiServiceHandlerThread = mWifiInjector.getWifiServiceHandlerThread();
+ mClientHandler = new ClientHandler(wifiServiceHandlerThread.getLooper());
+ mWifiStateMachineHandler =
+ new WifiStateMachineHandler(wifiServiceHandlerThread.getLooper());
+ mWifiController = mWifiInjector.getWifiController();
+ mWifiBackupRestore = mWifiInjector.getWifiBackupRestore();
+
+ enableVerboseLoggingInternal(getVerboseLoggingLevel());
}
@@ -419,6 +401,7 @@
* see {@link android.net.wifi.WifiManager#pingSupplicant()}
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
+ @Override
public boolean pingSupplicant() {
enforceAccessPermission();
if (mWifiStateMachineChannel != null) {
@@ -436,6 +419,7 @@
* @param settings If null, use default parameter, i.e. full scan.
* @param workSource If null, all blame is given to the calling uid.
*/
+ @Override
public void startScan(ScanSettings settings, WorkSource workSource) {
enforceChangePermission();
synchronized (this) {
@@ -475,6 +459,7 @@
settings, workSource);
}
+ @Override
public String getWpsNfcConfigurationToken(int netId) {
enforceConnectivityInternalPermission();
return mWifiStateMachine.syncGetWpsNfcConfigurationToken(netId);
@@ -547,6 +532,7 @@
* @return {@code true} if the enable/disable operation was
* started or is already in the queue.
*/
+ @Override
public synchronized boolean setWifiEnabled(boolean enable) {
enforceChangePermission();
Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid()
@@ -579,6 +565,7 @@
* {@link WifiManager#WIFI_STATE_ENABLING},
* {@link WifiManager#WIFI_STATE_UNKNOWN}
*/
+ @Override
public int getWifiEnabledState() {
enforceAccessPermission();
return mWifiStateMachine.syncGetWifiState();
@@ -590,6 +577,7 @@
* part of WifiConfiguration
* @param enabled true to enable and false to disable
*/
+ @Override
public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
enforceChangePermission();
ConnectivityManager.enforceTetherChangePermission(mContext);
@@ -612,6 +600,7 @@
* {@link WifiManager#WIFI_AP_STATE_ENABLING},
* {@link WifiManager#WIFI_AP_STATE_FAILED}
*/
+ @Override
public int getWifiApEnabledState() {
enforceAccessPermission();
return mWifiStateMachine.syncGetWifiApState();
@@ -621,6 +610,7 @@
* see {@link WifiManager#getWifiApConfiguration()}
* @return soft access point configuration
*/
+ @Override
public WifiConfiguration getWifiApConfiguration() {
enforceAccessPermission();
return mWifiStateMachine.syncGetWifiApConfiguration();
@@ -630,6 +620,7 @@
* see {@link WifiManager#buildWifiConfig()}
* @return a WifiConfiguration.
*/
+ @Override
public WifiConfiguration buildWifiConfig(String uriString, String mimeType, byte[] data) {
if (mimeType.equals(ConfigBuilder.WifiConfigType)) {
try {
@@ -649,6 +640,7 @@
* see {@link WifiManager#setWifiApConfiguration(WifiConfiguration)}
* @param wifiConfig WifiConfiguration details for soft access point
*/
+ @Override
public void setWifiApConfiguration(WifiConfiguration wifiConfig) {
enforceChangePermission();
if (wifiConfig == null)
@@ -661,10 +653,9 @@
}
/**
- * @param enable {@code true} to enable, {@code false} to disable.
- * @return {@code true} if the enable/disable operation was
- * started or is already in the queue.
+ * see {@link android.net.wifi.WifiManager#isScanAlwaysAvailable()}
*/
+ @Override
public boolean isScanAlwaysAvailable() {
enforceAccessPermission();
return mSettingsStore.isScanAlwaysAvailable();
@@ -673,6 +664,7 @@
/**
* see {@link android.net.wifi.WifiManager#disconnect()}
*/
+ @Override
public void disconnect() {
enforceChangePermission();
mWifiStateMachine.disconnectCommand();
@@ -681,6 +673,7 @@
/**
* see {@link android.net.wifi.WifiManager#reconnect()}
*/
+ @Override
public void reconnect() {
enforceChangePermission();
mWifiStateMachine.reconnectCommand();
@@ -689,6 +682,7 @@
/**
* see {@link android.net.wifi.WifiManager#reassociate()}
*/
+ @Override
public void reassociate() {
enforceChangePermission();
mWifiStateMachine.reassociateCommand();
@@ -697,6 +691,7 @@
/**
* see {@link android.net.wifi.WifiManager#getSupportedFeatures}
*/
+ @Override
public int getSupportedFeatures() {
enforceAccessPermission();
if (mWifiStateMachineChannel != null) {
@@ -717,6 +712,7 @@
/**
* see {@link android.net.wifi.WifiManager#getControllerActivityEnergyInfo(int)}
*/
+ @Override
public WifiActivityEnergyInfo reportActivityInfo() {
enforceAccessPermission();
if ((getSupportedFeatures() & WifiManager.WIFI_FEATURE_LINK_LAYER_STATS) == 0) {
@@ -788,6 +784,7 @@
* see {@link android.net.wifi.WifiManager#getConfiguredNetworks()}
* @return the list of configured networks
*/
+ @Override
public List<WifiConfiguration> getConfiguredNetworks() {
enforceAccessPermission();
if (mWifiStateMachineChannel != null) {
@@ -803,6 +800,7 @@
* see {@link android.net.wifi.WifiManager#getPrivilegedConfiguredNetworks()}
* @return the list of configured networks with real preSharedKey
*/
+ @Override
public List<WifiConfiguration> getPrivilegedConfiguredNetworks() {
enforceReadCredentialPermission();
enforceAccessPermission();
@@ -819,6 +817,7 @@
* @param scanResult scanResult that represents the BSSID
* @return {@link WifiConfiguration} that matches this BSSID or null
*/
+ @Override
public WifiConfiguration getMatchingWifiConfig(ScanResult scanResult) {
enforceAccessPermission();
return mWifiStateMachine.syncGetMatchingWifiConfig(scanResult, mWifiStateMachineChannel);
@@ -829,6 +828,7 @@
* @return the supplicant-assigned identifier for the new or updated
* network if the operation succeeds, or {@code -1} if it fails
*/
+ @Override
public int addOrUpdateNetwork(WifiConfiguration config) {
enforceChangePermission();
if (isValid(config) && isValidPasspoint(config)) {
@@ -899,6 +899,7 @@
* to the supplicant
* @return {@code true} if the operation succeeded
*/
+ @Override
public boolean removeNetwork(int netId) {
enforceChangePermission();
@@ -917,6 +918,7 @@
* @param disableOthers if true, disable all other networks.
* @return {@code true} if the operation succeeded
*/
+ @Override
public boolean enableNetwork(int netId, boolean disableOthers) {
enforceChangePermission();
if (mWifiStateMachineChannel != null) {
@@ -934,6 +936,7 @@
* to the supplicant
* @return {@code true} if the operation succeeded
*/
+ @Override
public boolean disableNetwork(int netId) {
enforceChangePermission();
if (mWifiStateMachineChannel != null) {
@@ -948,6 +951,7 @@
* See {@link android.net.wifi.WifiManager#getConnectionInfo()}
* @return the Wi-Fi information, contained in {@link WifiInfo}.
*/
+ @Override
public WifiInfo getConnectionInfo() {
enforceAccessPermission();
/*
@@ -962,6 +966,7 @@
* a list of {@link ScanResult} objects.
* @return the list of results
*/
+ @Override
public List<ScanResult> getScanResults(String callingPackage) {
enforceAccessPermission();
int userId = UserHandle.getCallingUserId();
@@ -998,6 +1003,7 @@
* @param mo The MO in XML form
* @return -1 for failure
*/
+ @Override
public int addPasspointManagementObject(String mo) {
return mWifiStateMachine.syncAddPasspointManagementObject(mWifiStateMachineChannel, mo);
}
@@ -1008,6 +1014,7 @@
* @param mos A List of MO definitions to be updated
* @return the number of nodes updated, or -1 for failure
*/
+ @Override
public int modifyPasspointManagementObject(String fqdn, List<PasspointManagementObjectDefinition> mos) {
return mWifiStateMachine.syncModifyPasspointManagementObject(mWifiStateMachineChannel, fqdn, mos);
}
@@ -1017,6 +1024,7 @@
* @param bssid The BSSID of the AP
* @param fileName Icon file name
*/
+ @Override
public void queryPasspointIcon(long bssid, String fileName) {
mWifiStateMachine.syncQueryPasspointIcon(mWifiStateMachineChannel, bssid, fileName);
}
@@ -1026,6 +1034,7 @@
* @param fqdn FQDN of the SP
* @return ordinal [HomeProvider, RoamingProvider, Incomplete, None, Declined]
*/
+ @Override
public int matchProviderWithCurrentNetwork(String fqdn) {
return mWifiStateMachine.matchProviderWithCurrentNetwork(mWifiStateMachineChannel, fqdn);
}
@@ -1035,6 +1044,7 @@
* @param holdoff hold off time in milliseconds
* @param ess set if the hold off pertains to an ESS rather than a BSS
*/
+ @Override
public void deauthenticateNetwork(long holdoff, boolean ess) {
mWifiStateMachine.deauthenticateNetwork(mWifiStateMachineChannel, holdoff, ess);
}
@@ -1088,8 +1098,8 @@
*
* TODO: deprecate this
*/
+ @Override
public boolean saveConfiguration() {
- boolean result = true;
enforceChangePermission();
if (mWifiStateMachineChannel != null) {
return mWifiStateMachine.syncSaveConfig(mWifiStateMachineChannel);
@@ -1108,6 +1118,7 @@
* persisted country code on a restart, when the locale information is
* not available from telephony.
*/
+ @Override
public void setCountryCode(String countryCode, boolean persist) {
Slog.i(TAG, "WifiService trying to set country code to " + countryCode +
" with persist set to " + persist);
@@ -1129,6 +1140,7 @@
* Get the country code
* @return ISO 3166 country code.
*/
+ @Override
public String getCountryCode() {
enforceConnectivityInternalPermission();
String country = mCountryCode.getCurrentCountryCode();
@@ -1143,6 +1155,7 @@
* @param persist {@code true} if the setting should be remembered.
*
*/
+ @Override
public void setFrequencyBand(int band, boolean persist) {
enforceChangePermission();
if (!isDualBandSupported()) return;
@@ -1160,11 +1173,13 @@
/**
* Get the operational frequency band
*/
+ @Override
public int getFrequencyBand() {
enforceAccessPermission();
return mWifiStateMachine.getFrequencyBand();
}
+ @Override
public boolean isDualBandSupported() {
//TODO: Should move towards adding a driver API that checks at runtime
return mContext.getResources().getBoolean(
@@ -1177,6 +1192,8 @@
* @return the DHCP information
* @deprecated
*/
+ @Override
+ @Deprecated
public DhcpInfo getDhcpInfo() {
enforceAccessPermission();
DhcpResults dhcpResults = mWifiStateMachine.syncGetDhcpResults();
@@ -1216,6 +1233,7 @@
* see {@link android.net.wifi.WifiManager#addToBlacklist}
*
*/
+ @Override
public void addToBlacklist(String bssid) {
enforceChangePermission();
@@ -1226,6 +1244,7 @@
* see {@link android.net.wifi.WifiManager#clearBlacklist}
*
*/
+ @Override
public void clearBlacklist() {
enforceChangePermission();
@@ -1306,6 +1325,7 @@
}
}
+ @Override
public void enableTdls(String remoteAddress, boolean enable) {
if (remoteAddress == null) {
throw new IllegalArgumentException("remoteAddress cannot be null");
@@ -1318,6 +1338,7 @@
}
+ @Override
public void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable) {
if (remoteMacAddress == null) {
throw new IllegalArgumentException("remoteMacAddress cannot be null");
@@ -1330,6 +1351,7 @@
* Get a reference to handler. This is used by a client to establish
* an AsyncChannel communication with WifiService
*/
+ @Override
public Messenger getWifiServiceMessenger() {
enforceAccessPermission();
enforceChangePermission();
@@ -1339,6 +1361,7 @@
/**
* Disable an ephemeral network, i.e. network that is created thru a WiFi Scorer
*/
+ @Override
public void disableEphemeralNetwork(String SSID) {
enforceAccessPermission();
enforceChangePermission();
@@ -1348,6 +1371,7 @@
/**
* Get the IP and proxy configuration file
*/
+ @Override
public String getConfigFile() {
enforceAccessPermission();
return mWifiStateMachine.getConfigFile();
@@ -1473,8 +1497,6 @@
pw.println("Stay-awake conditions: " +
Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0));
- pw.println("mMulticastEnabled " + mMulticastEnabled);
- pw.println("mMulticastDisabled " + mMulticastDisabled);
pw.println("mInIdleMode " + mInIdleMode);
pw.println("mScanPending " + mScanPending);
mWifiController.dump(fd, pw, args);
@@ -1511,15 +1533,12 @@
pw.println("Locks held:");
mWifiLockManager.dump(pw);
pw.println();
- pw.println("Multicast Locks held:");
- for (Multicaster l : mMulticasters) {
- pw.print(" ");
- pw.println(l);
- }
-
+ mWifiMulticastLockManager.dump(pw);
pw.println();
mWifiStateMachine.dump(fd, pw, args);
pw.println();
+ mWifiBackupRestore.dump(fd, pw, args);
+ pw.println();
}
}
@@ -1546,168 +1565,91 @@
return false;
}
- private class Multicaster implements IBinder.DeathRecipient {
- String mTag;
- int mUid;
- IBinder mBinder;
-
- Multicaster(String tag, IBinder binder) {
- mTag = tag;
- mUid = Binder.getCallingUid();
- mBinder = binder;
- try {
- mBinder.linkToDeath(this, 0);
- } catch (RemoteException e) {
- binderDied();
- }
- }
-
- @Override
- public void binderDied() {
- Slog.e(TAG, "Multicaster binderDied");
- synchronized (mMulticasters) {
- int i = mMulticasters.indexOf(this);
- if (i != -1) {
- removeMulticasterLocked(i, mUid);
- }
- }
- }
-
- void unlinkDeathRecipient() {
- mBinder.unlinkToDeath(this, 0);
- }
-
- public int getUid() {
- return mUid;
- }
-
- public String toString() {
- return "Multicaster{" + mTag + " uid=" + mUid + "}";
- }
- }
-
+ @Override
public void initializeMulticastFiltering() {
enforceMulticastChangePermission();
-
- synchronized (mMulticasters) {
- // if anybody had requested filters be off, leave off
- if (mMulticasters.size() != 0) {
- return;
- } else {
- mWifiStateMachine.startFilteringMulticastPackets();
- }
- }
+ mWifiMulticastLockManager.initializeFiltering();
}
+ @Override
public void acquireMulticastLock(IBinder binder, String tag) {
enforceMulticastChangePermission();
-
- synchronized (mMulticasters) {
- mMulticastEnabled++;
- mMulticasters.add(new Multicaster(tag, binder));
- // Note that we could call stopFilteringMulticastPackets only when
- // our new size == 1 (first call), but this function won't
- // be called often and by making the stopPacket call each
- // time we're less fragile and self-healing.
- mWifiStateMachine.stopFilteringMulticastPackets();
- }
-
- int uid = Binder.getCallingUid();
- final long ident = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteWifiMulticastEnabled(uid);
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ mWifiMulticastLockManager.acquireLock(binder, tag);
}
+ @Override
public void releaseMulticastLock() {
enforceMulticastChangePermission();
-
- int uid = Binder.getCallingUid();
- synchronized (mMulticasters) {
- mMulticastDisabled++;
- int size = mMulticasters.size();
- for (int i = size - 1; i >= 0; i--) {
- Multicaster m = mMulticasters.get(i);
- if ((m != null) && (m.getUid() == uid)) {
- removeMulticasterLocked(i, uid);
- }
- }
- }
+ mWifiMulticastLockManager.releaseLock();
}
- private void removeMulticasterLocked(int i, int uid)
- {
- Multicaster removed = mMulticasters.remove(i);
-
- if (removed != null) {
- removed.unlinkDeathRecipient();
- }
- if (mMulticasters.size() == 0) {
- mWifiStateMachine.startFilteringMulticastPackets();
- }
-
- final long ident = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteWifiMulticastDisabled(uid);
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
+ @Override
public boolean isMulticastEnabled() {
enforceAccessPermission();
-
- synchronized (mMulticasters) {
- return (mMulticasters.size() > 0);
- }
+ return mWifiMulticastLockManager.isMulticastEnabled();
}
+ @Override
public void enableVerboseLogging(int verbose) {
enforceAccessPermission();
+ mFacade.setIntegerSetting(
+ mContext, Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, verbose);
+ enableVerboseLoggingInternal(verbose);
+ }
+
+ private void enableVerboseLoggingInternal(int verbose) {
mWifiStateMachine.enableVerboseLogging(verbose);
mWifiLockManager.enableVerboseLogging(verbose);
+ mWifiMulticastLockManager.enableVerboseLogging(verbose);
+ mWifiInjector.getWifiLastResortWatchdog().enableVerboseLogging(verbose);
+ mWifiInjector.getWifiBackupRestore().enableVerboseLogging(verbose);
}
+ @Override
public int getVerboseLoggingLevel() {
enforceAccessPermission();
- return mWifiStateMachine.getVerboseLoggingLevel();
+ return mFacade.getIntegerSetting(
+ mContext, Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, 0);
}
+ @Override
public void enableAggressiveHandover(int enabled) {
enforceAccessPermission();
mWifiStateMachine.enableAggressiveHandover(enabled);
}
+ @Override
public int getAggressiveHandover() {
enforceAccessPermission();
return mWifiStateMachine.getAggressiveHandover();
}
+ @Override
public void setAllowScansWithTraffic(int enabled) {
enforceAccessPermission();
mWifiStateMachine.setAllowScansWithTraffic(enabled);
}
+ @Override
public int getAllowScansWithTraffic() {
enforceAccessPermission();
return mWifiStateMachine.getAllowScansWithTraffic();
}
+ @Override
public boolean setEnableAutoJoinWhenAssociated(boolean enabled) {
enforceChangePermission();
return mWifiStateMachine.setEnableAutoJoinWhenAssociated(enabled);
}
+ @Override
public boolean getEnableAutoJoinWhenAssociated() {
enforceAccessPermission();
return mWifiStateMachine.getEnableAutoJoinWhenAssociated();
}
/* Return the Wifi Connection statistics object */
+ @Override
public WifiConnectionStatistics getConnectionStatistics() {
enforceAccessPermission();
enforceReadCredentialPermission();
@@ -1719,6 +1661,7 @@
}
}
+ @Override
public void factoryReset() {
enforceConnectivityInternalPermission();
@@ -1807,6 +1750,7 @@
return null;
}
+ @Override
public Network getCurrentNetwork() {
enforceAccessPermission();
return mWifiStateMachine.getCurrentNetwork();
@@ -1888,8 +1832,96 @@
*
* @param enabled true-enable; false-disable
*/
+ @Override
public void enableWifiConnectivityManager(boolean enabled) {
enforceConnectivityInternalPermission();
mWifiStateMachine.enableWifiConnectivityManager(enabled);
}
+
+ /**
+ * Retrieve the data to be backed to save the current state.
+ *
+ * @return Raw byte stream of the data to be backed up.
+ */
+ @Override
+ public byte[] retrieveBackupData() {
+ enforceReadCredentialPermission();
+ enforceAccessPermission();
+ if (mWifiStateMachineChannel == null) {
+ Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
+ return null;
+ }
+
+ Slog.d(TAG, "Retrieving backup data");
+ List<WifiConfiguration> wifiConfigurations =
+ mWifiStateMachine.syncGetPrivilegedConfiguredNetwork(mWifiStateMachineChannel);
+ byte[] backupData =
+ mWifiBackupRestore.retrieveBackupDataFromConfigurations(wifiConfigurations);
+ Slog.d(TAG, "Retrieved backup data");
+ return backupData;
+ }
+
+ /**
+ * Helper method to restore networks retrieved from backup data.
+ *
+ * @param configurations list of WifiConfiguration objects parsed from the backup data.
+ */
+ private void restoreNetworks(List<WifiConfiguration> configurations) {
+ if (configurations == null) {
+ Slog.e(TAG, "Backup data parse failed");
+ return;
+ }
+ for (WifiConfiguration configuration : configurations) {
+ int networkId = mWifiStateMachine.syncAddOrUpdateNetwork(
+ mWifiStateMachineChannel, configuration);
+ if (networkId == WifiConfiguration.INVALID_NETWORK_ID) {
+ Slog.e(TAG, "Restore network failed: " + configuration.configKey());
+ continue;
+ }
+ // Enable all networks restored.
+ mWifiStateMachine.syncEnableNetwork(mWifiStateMachineChannel, networkId, false);
+ }
+ }
+
+ /**
+ * Restore state from the backed up data.
+ *
+ * @param data Raw byte stream of the backed up data.
+ */
+ @Override
+ public void restoreBackupData(byte[] data) {
+ enforceChangePermission();
+ if (mWifiStateMachineChannel == null) {
+ Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
+ return;
+ }
+
+ Slog.d(TAG, "Restoring backup data");
+ List<WifiConfiguration> wifiConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromBackupData(data);
+ restoreNetworks(wifiConfigurations);
+ Slog.d(TAG, "Restored backup data");
+ }
+
+ /**
+ * Restore state from the older supplicant back up data.
+ * The old backup data was essentially a backup of wpa_supplicant.conf & ipconfig.txt file.
+ *
+ * @param supplicantData Raw byte stream of wpa_supplicant.conf
+ * @param ipConfigData Raw byte stream of ipconfig.txt
+ */
+ public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) {
+ enforceChangePermission();
+ if (mWifiStateMachineChannel == null) {
+ Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
+ return;
+ }
+
+ Slog.d(TAG, "Restoring supplicant backup data");
+ List<WifiConfiguration> wifiConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromSupplicantBackupData(
+ supplicantData, ipConfigData);
+ restoreNetworks(wifiConfigurations);
+ Slog.d(TAG, "Restored supplicant backup data");
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index e3ab3bc..c2e4b93 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -29,7 +29,6 @@
import android.Manifest;
import android.app.ActivityManager;
-import android.app.AlarmManager;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
@@ -56,6 +55,8 @@
import android.net.StaticIpConfiguration;
import android.net.dhcp.DhcpClient;
import android.net.ip.IpManager;
+import android.net.wifi.IApInterface;
+import android.net.wifi.IWificond;
import android.net.wifi.PasspointManagementObjectDefinition;
import android.net.wifi.RssiPacketCountInfo;
import android.net.wifi.ScanResult;
@@ -73,6 +74,7 @@
import android.net.wifi.WpsInfo;
import android.net.wifi.WpsResult;
import android.net.wifi.WpsResult.Status;
+import android.net.wifi.nan.WifiNanManager;
import android.net.wifi.p2p.IWifiP2pManager;
import android.os.BatteryStats;
import android.os.Binder;
@@ -85,7 +87,6 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
@@ -120,12 +121,10 @@
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Calendar;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
-import java.util.Random;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -146,22 +145,22 @@
*
* @hide
*/
-public class WifiStateMachine extends StateMachine implements WifiNative.WifiRssiEventHandler {
+public class WifiStateMachine extends StateMachine implements WifiNative.WifiRssiEventHandler,
+ WifiMulticastLockManager.FilterController {
private static final String NETWORKTYPE = "WIFI";
private static final String NETWORKTYPE_UNTRUSTED = "WIFI_UT";
@VisibleForTesting public static final short NUM_LOG_RECS_NORMAL = 100;
@VisibleForTesting public static final short NUM_LOG_RECS_VERBOSE_LOW_MEMORY = 200;
@VisibleForTesting public static final short NUM_LOG_RECS_VERBOSE = 3000;
- private static boolean DBG = false;
- private static boolean USE_PAUSE_SCANS = false;
private static final String TAG = "WifiStateMachine";
private static final int ONE_HOUR_MILLI = 1000 * 60 * 60;
private static final String GOOGLE_OUI = "DA-A1-19";
- private int mVerboseLoggingLevel = 0;
+ private boolean mVerboseLoggingEnabled = false;
+
/* debug flag, indicating if handling of ASSOCIATION_REJECT ended up blacklisting
* the corresponding BSSID.
*/
@@ -172,16 +171,18 @@
*
* @param s is string log
*/
+ @Override
protected void loge(String s) {
Log.e(getName(), s);
}
+ @Override
protected void logd(String s) {
Log.d(getName(), s);
}
- protected void log(String s) {;
+ @Override
+ protected void log(String s) {
Log.d(getName(), s);
}
- private WifiLastResortWatchdog mWifiLastResortWatchdog;
private WifiMetrics mWifiMetrics;
private WifiInjector mWifiInjector;
private WifiMonitor mWifiMonitor;
@@ -190,11 +191,13 @@
private WifiConnectivityManager mWifiConnectivityManager;
private WifiQualifiedNetworkSelector mWifiQualifiedNetworkSelector;
private INetworkManagementService mNwService;
+ private IWificond mWificond;
private ConnectivityManager mCm;
private BaseWifiLogger mWifiLogger;
private WifiApConfigStore mWifiApConfigStore;
private final boolean mP2pSupported;
private final AtomicBoolean mP2pConnected = new AtomicBoolean(false);
+ private final boolean mNanSupported;
private boolean mTemporarilyDisconnectWifi = false;
private final String mPrimaryDeviceType;
private final Clock mClock;
@@ -213,21 +216,16 @@
private boolean mScreenOn = false;
- /* Chipset supports background scan */
- private final boolean mBackgroundScanSupported;
-
private final String mInterfaceName;
- /* Tethering interface could be separate from wlan interface */
- private String mTetherInterfaceName;
private int mLastSignalLevel = -1;
private String mLastBssid;
private int mLastNetworkId; // The network Id we successfully joined
- private boolean linkDebouncing = false;
+ private boolean mIsLinkDebouncing = false;
@Override
public void onRssiThresholdBreached(byte curRssi) {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
Log.e(TAG, "onRssiThresholdBreach event. Cur Rssi = " + curRssi);
}
sendMessage(CMD_RSSI_THRESHOLD_BREACH, curRssi);
@@ -247,7 +245,7 @@
// This value of hw has to be believed as this value is averaged and has breached
// the rssi thresholds and raised event to host. This would be eggregious if this
// value is invalid
- mWifiInfo.setRssi((int) curRssi);
+ mWifiInfo.setRssi(curRssi);
updateCapabilities(getCurrentWifiConfiguration());
int ret = startRssiMonitoringOffload(maxRssi, minRssi);
Log.d(TAG, "Re-program RSSI thresholds for " + smToString(reason) +
@@ -274,12 +272,8 @@
private boolean mSendScanResultsBroadcast = false;
private final Queue<Message> mBufferedScanMsg = new LinkedList<Message>();
- private WorkSource mScanWorkSource = null;
private static final int UNKNOWN_SCAN_SOURCE = -1;
private static final int ADD_OR_UPDATE_SOURCE = -3;
- private static final int SET_ALLOW_UNTRUSTED_SOURCE = -4;
- private static final int ENABLE_WIFI = -5;
- public static final int DFS_RESTRICTED_SCAN_REQUEST = -6;
private static final int SCAN_REQUEST_BUFFER_MAX_SIZE = 10;
private static final String CUSTOMIZED_SCAN_SETTING = "customized_scan_settings";
@@ -322,14 +316,6 @@
private int mSupplicantStopFailureToken = 0;
/**
- * Tether state change notification time out
- */
- private static final int TETHER_NOTIFICATION_TIME_OUT_MSECS = 5000;
-
- /* Tracks sequence number on a tether notification time out */
- private int mTetherToken = 0;
-
- /**
* Driver start time out.
*/
private static final int DRIVER_START_TIME_OUT_MSECS = 10000;
@@ -338,12 +324,6 @@
private int mDriverStartToken = 0;
/**
- * Don't select new network when previous network selection is
- * pending connection for this much time
- */
- private static final int CONNECT_TIMEOUT_MSEC = 3000;
-
- /**
* The link properties of the wifi interface.
* Do not modify this directly; use updateLinkProperties instead.
*/
@@ -394,9 +374,6 @@
// Used as debug to indicate which configuration last was removed
private WifiConfiguration lastForgetConfigurationAttempt = null;
- //Random used by softAP channel Selection
- private static Random mRandom = new Random(Calendar.getInstance().getTimeInMillis());
-
boolean isRoaming() {
return mAutoRoaming;
}
@@ -417,18 +394,18 @@
}
if (!mTargetRoamBSSID.equals("any") && bssid.equals("any")) {
// Changing to ANY
- if (!mWifiConfigManager.ROAM_ON_ANY) {
+ if (!WifiConfigManager.ROAM_ON_ANY) {
ret = false; // Nothing to do
}
}
if (config.BSSID != null) {
bssid = config.BSSID;
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
Log.d(TAG, "force BSSID to " + bssid + "due to config");
}
}
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd("autoRoamSetBSSID " + bssid + " key=" + config.configKey());
}
mTargetRoamBSSID = bssid;
@@ -450,7 +427,7 @@
if (config.BSSID != null) {
bssid = config.BSSID;
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
Log.d(TAG, "force BSSID to " + bssid + "due to config");
}
}
@@ -462,13 +439,13 @@
String networkSelectionBSSID = config.getNetworkSelectionStatus()
.getNetworkSelectionBSSID();
if (networkSelectionBSSID != null && networkSelectionBSSID.equals(bssid)) {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
Log.d(TAG, "Current preferred BSSID is the same as the target one");
}
return false;
}
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
Log.d(TAG, "target set to " + config.SSID + ":" + bssid);
}
mTargetRoamBSSID = bssid;
@@ -528,9 +505,6 @@
private final IpManager mIpManager;
- private AlarmManager mAlarmManager;
- private PendingIntent mScanIntent;
-
/* Tracks current frequency mode */
private AtomicInteger mFrequencyBand = new AtomicInteger(WifiManager.WIFI_FREQUENCY_BAND_AUTO);
@@ -538,6 +512,7 @@
private AsyncChannel mReplyChannel = new AsyncChannel();
private WifiP2pServiceImpl mWifiP2pServiceImpl;
+ private WifiNanManager mWifiNanManager;
// Used to initiate a connection with WifiP2pService
private AsyncChannel mWifiP2pChannel;
@@ -549,8 +524,6 @@
private UntrustedWifiNetworkFactory mUntrustedNetworkFactory;
private WifiNetworkAgent mNetworkAgent;
- private String[] mWhiteListedSsids = null;
-
private byte[] mRssiRanges;
// Keep track of various statistics, for retrieval by System Apps, i.e. under @SystemApi
@@ -667,8 +640,6 @@
static final int CMD_SET_FREQUENCY_BAND = BASE + 90;
/* Enable TDLS on a specific MAC address */
static final int CMD_ENABLE_TDLS = BASE + 92;
- /* DHCP/IP configuration watchdog */
- static final int CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER = BASE + 93;
/**
* Watchdog for protecting against b/16823537
@@ -708,15 +679,6 @@
/* try to match a provider with current network */
static final int CMD_MATCH_PROVIDER_NETWORK = BASE + 105;
- /**
- * Make this timer 40 seconds, which is about the normal DHCP timeout.
- * In no valid case, the WiFiStateMachine should remain stuck in ObtainingIpAddress
- * for more than 30 seconds.
- */
- static final int OBTAINING_IP_ADDRESS_GUARD_TIMER_MSEC = 40000;
-
- int obtainingIpWatchdogCount = 0;
-
/* Commands from/to the SupplicantStateTracker */
/* Reset the supplicant state tracker */
static final int CMD_RESET_SUPPLICANT_STATE = BASE + 111;
@@ -771,9 +733,6 @@
static final int CMD_ACCEPT_UNVALIDATED = BASE + 153;
- /* used to log if PNO was started */
- static final int CMD_UPDATE_ASSOCIATED_SCAN_PERMISSION = BASE + 158;
-
/* used to offload sending IP packet */
static final int CMD_START_IP_PACKET_OFFLOAD = BASE + 160;
@@ -846,16 +805,6 @@
private AtomicBoolean mUserWantsSuspendOpt = new AtomicBoolean(true);
/**
- * Default framework scan interval in milliseconds. This is used in the scenario in which
- * wifi chipset does not support background scanning to set up a
- * periodic wake up scan so that the device can connect to a new access
- * point on the move. {@link Settings.Global#WIFI_FRAMEWORK_SCAN_INTERVAL_MS} can
- * override this.
- */
- private final int mDefaultFrameworkScanIntervalMs;
-
-
- /**
* Scan period for the NO_NETWORKS_PERIIDOC_SCAN_FEATURE
*/
private final int mNoNetworksPeriodicScan;
@@ -948,8 +897,6 @@
*/
private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
- private static final int SCAN_REQUEST = 0;
-
/**
* Work source to use to blame usage on the WiFi service
*/
@@ -982,11 +929,6 @@
// Used for debug and stats gathering
private static int sScanAlarmIntentCount = 0;
- private static final int sFrameworkMinScanIntervalSaneValue = 10000;
-
- private long mGScanStartTimeMilli;
- private long mGScanPeriodMilli;
-
private FrameworkFacade mFacade;
private final BackupManagerProxy mBackupManagerProxy;
@@ -1000,7 +942,6 @@
super("WifiStateMachine", looper);
mWifiInjector = wifiInjector;
mWifiMetrics = mWifiInjector.getWifiMetrics();
- mWifiLastResortWatchdog = wifiInjector.getWifiLastResortWatchdog();
mClock = wifiInjector.getClock();
mPropertyService = wifiInjector.getPropertyService();
mBuildProperties = wifiInjector.getBuildProperties();
@@ -1021,6 +962,8 @@
mP2pSupported = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI_DIRECT);
+ mNanSupported = mContext.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_WIFI_NAN);
mWifiConfigManager = mFacade.makeWifiConfigManager(context, mWifiNative, facade,
mWifiInjector.getClock(), userManager, mWifiInjector.getKeyStore());
@@ -1047,6 +990,8 @@
IBinder s1 = mFacade.getService(Context.WIFI_P2P_SERVICE);
mWifiP2pServiceImpl = (WifiP2pServiceImpl) IWifiP2pManager.Stub.asInterface(s1);
+ mWifiNanManager = (WifiNanManager) mContext.getSystemService(Context.WIFI_NAN_SERVICE);
+
mNetworkInfo.setIsAvailable(false);
mLastBssid = null;
mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
@@ -1055,21 +1000,12 @@
mIpManager = mFacade.makeIpManager(mContext, mInterfaceName, new IpManagerCallback());
mIpManager.setMulticastFilter(true);
- mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
-
- // Make sure the interval is not configured less than 10 seconds
- int period = mContext.getResources().getInteger(
- R.integer.config_wifi_framework_scan_interval);
- if (period < sFrameworkMinScanIntervalSaneValue) {
- period = sFrameworkMinScanIntervalSaneValue;
- }
- mDefaultFrameworkScanIntervalMs = period;
-
mNoNetworksPeriodicScan = mContext.getResources().getInteger(
R.integer.config_wifi_no_network_periodic_scan_interval);
- mBackgroundScanSupported = mContext.getResources().getBoolean(
- R.bool.config_wifi_background_scan_support);
+ // TODO: remove these settings from the config file since we no longer obey them
+ // mContext.getResources().getInteger(R.integer.config_wifi_framework_scan_interval);
+ // mContext.getResources().getBoolean(R.bool.config_wifi_background_scan_support);
mPrimaryDeviceType = mContext.getResources().getString(
R.string.config_wifi_p2p_device_type);
@@ -1210,10 +1146,6 @@
} catch (PackageManager.NameNotFoundException e) {
loge("Unable to resolve SystemUI's UID.");
}
-
- mVerboseLoggingLevel = mFacade.getIntegerSetting(
- mContext, Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, 0);
- updateLoggingLevel();
}
class IpManagerCallback extends IpManager.Callback {
@@ -1233,8 +1165,8 @@
sendMessage(CMD_IPV4_PROVISIONING_SUCCESS, dhcpResults);
} else {
sendMessage(CMD_IPV4_PROVISIONING_FAILURE);
- mWifiLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(getTargetSsid(),
- mTargetRoamBSSID,
+ mWifiInjector.getWifiLastResortWatchdog().noteConnectionFailureAndTriggerIfNeeded(
+ getTargetSsid(), mTargetRoamBSSID,
WifiLastResortWatchdog.FAILURE_CODE_DHCP);
}
}
@@ -1289,48 +1221,37 @@
return mFacade.getBroadcast(mContext, requestCode, intent, 0);
}
- int getVerboseLoggingLevel() {
- return mVerboseLoggingLevel;
- }
-
- void enableVerboseLogging(int verbose) {
- mVerboseLoggingLevel = verbose;
- mFacade.setIntegerSetting(
- mContext, Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, verbose);
- updateLoggingLevel();
- }
-
/**
* Set wpa_supplicant log level using |mVerboseLoggingLevel| flag.
*/
void setSupplicantLogLevel() {
- if (mVerboseLoggingLevel > 0) {
+ if (mVerboseLoggingEnabled) {
mWifiNative.setSupplicantLogLevel("DEBUG");
} else {
mWifiNative.setSupplicantLogLevel("INFO");
}
}
- void updateLoggingLevel() {
- if (mVerboseLoggingLevel > 0) {
- DBG = true;
+ void enableVerboseLogging(int verbose) {
+ if (verbose > 0) {
+ mVerboseLoggingEnabled = true;
setLogRecSize(ActivityManager.isLowRamDeviceStatic()
? NUM_LOG_RECS_VERBOSE_LOW_MEMORY : NUM_LOG_RECS_VERBOSE);
} else {
- DBG = false;
+ mVerboseLoggingEnabled = false;
setLogRecSize(NUM_LOG_RECS_NORMAL);
}
- configureVerboseHalLogging(mVerboseLoggingLevel > 0);
+ configureVerboseHalLogging(mVerboseLoggingEnabled);
setSupplicantLogLevel();
- mCountryCode.enableVerboseLogging(mVerboseLoggingLevel);
- mWifiLogger.startLogging(DBG);
- mWifiMonitor.enableVerboseLogging(mVerboseLoggingLevel);
- mWifiNative.enableVerboseLogging(mVerboseLoggingLevel);
- mWifiConfigManager.enableVerboseLogging(mVerboseLoggingLevel);
- mSupplicantStateTracker.enableVerboseLogging(mVerboseLoggingLevel);
- mWifiQualifiedNetworkSelector.enableVerboseLogging(mVerboseLoggingLevel);
+ mCountryCode.enableVerboseLogging(verbose);
+ mWifiLogger.startLogging(mVerboseLoggingEnabled);
+ mWifiMonitor.enableVerboseLogging(verbose);
+ mWifiNative.enableVerboseLogging(verbose);
+ mWifiConfigManager.enableVerboseLogging(verbose);
+ mSupplicantStateTracker.enableVerboseLogging(verbose);
+ mWifiQualifiedNetworkSelector.enableVerboseLogging(verbose);
if (mWifiConnectivityManager != null) {
- mWifiConnectivityManager.enableVerboseLogging(mVerboseLoggingLevel);
+ mWifiConnectivityManager.enableVerboseLogging(verbose);
}
}
@@ -1345,15 +1266,6 @@
enableVerbose ? LOGD_LEVEL_VERBOSE : LOGD_LEVEL_DEBUG);
}
- long mLastScanPermissionUpdate = 0;
- boolean mConnectedModeGScanOffloadStarted = false;
- // Don't do a G-scan enable/re-enable cycle more than once within 20seconds
- // The function updateAssociatedScanPermission() can be called quite frequently, hence
- // we want to throttle the GScan Stop->Start transition
- static final long SCAN_PERMISSION_UPDATE_THROTTLE_MILLI = 20000;
- void updateAssociatedScanPermission() {
- }
-
private int mAggressiveHandover = 0;
int getAggressiveHandover() {
@@ -1437,7 +1349,7 @@
Bundle bundle = new Bundle();
bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, settings);
bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
- bundle.putLong(SCAN_REQUEST_TIME, System.currentTimeMillis());
+ bundle.putLong(SCAN_REQUEST_TIME, mClock.getWallClockMillis());
sendMessage(CMD_START_SCAN, callingUid, scanCounter, bundle);
}
@@ -1477,7 +1389,7 @@
public long getDisconnectedTimeMilli() {
if (getCurrentState() == mDisconnectedState
&& mDisconnectedTimeStamp != 0) {
- long now_ms = System.currentTimeMillis();
+ long now_ms = mClock.getWallClockMillis();
return now_ms - mDisconnectedTimeStamp;
}
return 0;
@@ -1506,7 +1418,7 @@
//TODO: this is used only to track connection attempts, however the link state and packet per
//TODO: second logic should be folded into that
private boolean checkOrDeferScanAllowed(Message msg) {
- long now = System.currentTimeMillis();
+ long now = mClock.getWallClockMillis();
if (lastConnectAttemptTimestamp != 0 && (now - lastConnectAttemptTimestamp) < 10000) {
Message dmsg = Message.obtain(msg);
sendMessageDelayed(dmsg, 11000 - (now - lastConnectAttemptTimestamp));
@@ -1529,7 +1441,7 @@
private long lastLinkLayerStatsUpdate = 0;
String reportOnTime() {
- long now = System.currentTimeMillis();
+ long now = mClock.getWallClockMillis();
StringBuilder sb = new StringBuilder();
// Report stats since last report
int on = mOnTime - mOnTimeLastReport;
@@ -1548,7 +1460,7 @@
return sb.toString();
}
- WifiLinkLayerStats getWifiLinkLayerStats(boolean dbg) {
+ WifiLinkLayerStats getWifiLinkLayerStats() {
WifiLinkLayerStats stats = null;
if (mWifiLinkLayerStatsSupported > 0) {
String name = "wlan0";
@@ -1556,7 +1468,7 @@
if (name != null && stats == null && mWifiLinkLayerStatsSupported > 0) {
mWifiLinkLayerStatsSupported -= 1;
} else if (stats != null) {
- lastLinkLayerStatsUpdate = System.currentTimeMillis();
+ lastLinkLayerStatsUpdate = mClock.getWallClockMillis();
mOnTime = stats.on_time;
mTxTime = stats.tx_time;
mRxTime = stats.rx_time;
@@ -1704,16 +1616,21 @@
}
WifiScanner.ScanListener nativeScanListener = new WifiScanner.ScanListener() {
// ignore all events since WifiStateMachine is registered for the supplicant events
+ @Override
public void onSuccess() {
}
+ @Override
public void onFailure(int reason, String description) {
mIsScanOngoing = false;
mIsFullScanOngoing = false;
}
+ @Override
public void onResults(WifiScanner.ScanData[] results) {
}
+ @Override
public void onFullResult(ScanResult fullScanResult) {
}
+ @Override
public void onPeriodChanged(int periodInMs) {
}
};
@@ -1817,19 +1734,19 @@
}
public boolean isSupplicantTransientState() {
- SupplicantState SupplicantState = mWifiInfo.getSupplicantState();
- if (SupplicantState == SupplicantState.ASSOCIATING
- || SupplicantState == SupplicantState.AUTHENTICATING
- || SupplicantState == SupplicantState.FOUR_WAY_HANDSHAKE
- || SupplicantState == SupplicantState.GROUP_HANDSHAKE) {
+ SupplicantState supplicantState = mWifiInfo.getSupplicantState();
+ if (supplicantState == SupplicantState.ASSOCIATING
+ || supplicantState == SupplicantState.AUTHENTICATING
+ || supplicantState == SupplicantState.FOUR_WAY_HANDSHAKE
+ || supplicantState == SupplicantState.GROUP_HANDSHAKE) {
- if (DBG) {
- Log.d(TAG, "Supplicant is under transient state: " + SupplicantState);
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "Supplicant is under transient state: " + supplicantState);
}
return true;
} else {
- if (DBG) {
- Log.d(TAG, "Supplicant is under steady state: " + SupplicantState);
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "Supplicant is under steady state: " + supplicantState);
}
}
@@ -1837,7 +1754,7 @@
}
public boolean isLinkDebouncing() {
- return linkDebouncing;
+ return mIsLinkDebouncing;
}
/**
@@ -1874,7 +1791,7 @@
* TODO: doc
*/
public void setOperationalMode(int mode) {
- if (DBG) log("setting operational mode to " + String.valueOf(mode));
+ if (mVerboseLoggingEnabled) log("setting operational mode to " + String.valueOf(mode));
sendMessage(CMD_SET_OPERATIONAL_MODE, mode, 0);
}
@@ -2305,14 +2222,6 @@
} else {
pw.println("CurrentCountryCode is not initialized");
}
- pw.println("mConnectedModeGScanOffloadStarted " + mConnectedModeGScanOffloadStarted);
- pw.println("mGScanPeriodMilli " + mGScanPeriodMilli);
- if (mWhiteListedSsids != null && mWhiteListedSsids.length > 0) {
- pw.println("SSID whitelist :" );
- for (int i=0; i < mWhiteListedSsids.length; i++) {
- pw.println(" " + mWhiteListedSsids[i]);
- }
- }
if (mNetworkFactory != null) {
mNetworkFactory.dump(fd, pw, args);
} else {
@@ -2353,7 +2262,7 @@
private void logStateAndMessage(Message message, State state) {
messageHandlingStatus = 0;
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd(" " + state.getClass().getSimpleName() + " " + getLogRecString(message));
}
}
@@ -2363,8 +2272,8 @@
*/
String printTime() {
StringBuilder sb = new StringBuilder();
- sb.append(" rt=").append(SystemClock.uptimeMillis());
- sb.append("/").append(SystemClock.elapsedRealtime());
+ sb.append(" rt=").append(mClock.getUptimeSinceBootMillis());
+ sb.append("/").append(mClock.getElapsedSinceBootMillis());
return sb.toString();
}
@@ -2374,6 +2283,7 @@
* @param msg that was processed
* @return information to be logged as a String
*/
+ @Override
protected String getLogRecString(Message msg) {
WifiConfiguration config;
Long now;
@@ -2392,22 +2302,8 @@
}
sb.append(" ").append(printTime());
switch (msg.what) {
- case CMD_UPDATE_ASSOCIATED_SCAN_PERMISSION:
- sb.append(" ");
- sb.append(Integer.toString(msg.arg1));
- sb.append(" ");
- sb.append(Integer.toString(msg.arg2));
- sb.append(" autojoinAllowed=");
- sb.append(mWifiConfigManager.getEnableAutoJoinWhenAssociated());
- sb.append(" withTraffic=").append(getAllowScansWithTraffic());
- sb.append(" tx=").append(mWifiInfo.txSuccessRate);
- sb.append("/").append(mWifiConfigManager.MAX_TX_PACKET_FOR_FULL_SCANS);
- sb.append(" rx=").append(mWifiInfo.rxSuccessRate);
- sb.append("/").append(mWifiConfigManager.MAX_RX_PACKET_FOR_FULL_SCANS);
- sb.append(" -> ").append(mConnectedModeGScanOffloadStarted);
- break;
case CMD_START_SCAN:
- now = System.currentTimeMillis();
+ now = mClock.getWallClockMillis();
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
@@ -2581,7 +2477,7 @@
sb.append(" freq=").append(mWifiInfo.getFrequency());
sb.append(" rssi=").append(mWifiInfo.getRssi());
}
- if (linkDebouncing) {
+ if (isLinkDebouncing()) {
sb.append(" debounce");
}
break;
@@ -2606,7 +2502,7 @@
netWorkSelectionStatus.getNetworkDisableReasonString());
}
if (config.lastConnected != 0) {
- now = System.currentTimeMillis();
+ now = mClock.getWallClockMillis();
sb.append(" lastconn=").append(now - config.lastConnected).append("(ms)");
}
if (mLastBssid != null) {
@@ -2647,11 +2543,6 @@
if (mWifiScoreReport != null) {
sb.append(mWifiScoreReport.getReport());
}
- if (mConnectedModeGScanOffloadStarted) {
- sb.append(" offload-started periodMilli " + mGScanPeriodMilli);
- } else {
- sb.append(" offload-stopped");
- }
break;
case CMD_AUTO_CONNECT:
case WifiManager.CONNECT_NETWORK:
@@ -2685,7 +2576,7 @@
sb.append(Integer.toString(msg.arg2));
ScanResult result = (ScanResult) msg.obj;
if (result != null) {
- now = System.currentTimeMillis();
+ now = mClock.getWallClockMillis();
sb.append(" bssid=").append(result.BSSID);
sb.append(" rssi=").append(result.level);
sb.append(" freq=").append(result.frequency);
@@ -2890,7 +2781,7 @@
private void handleScreenStateChanged(boolean screenOn) {
mScreenOn = screenOn;
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd(" handleScreenStateChanged Enter: screenOn=" + screenOn
+ " mUserWantsSuspendOpt=" + mUserWantsSuspendOpt
+ " state " + getCurrentState().getName()
@@ -2912,7 +2803,7 @@
}
mScreenBroadcastReceived.set(true);
- getWifiLinkLayerStats(false);
+ getWifiLinkLayerStats();
mOnTimeScreenStateChange = mOnTime;
lastScreenStateChangeTimeStamp = lastLinkLayerStatsUpdate;
@@ -2922,7 +2813,7 @@
mWifiConnectivityManager.handleScreenStateChanged(screenOn);
}
- if (DBG) log("handleScreenStateChanged Exit: " + screenOn);
+ if (mVerboseLoggingEnabled) log("handleScreenStateChanged Exit: " + screenOn);
}
private void checkAndSetConnectivityInstance() {
@@ -2943,7 +2834,7 @@
if (mWifiConnectivityManager != null) {
mWifiConnectivityManager.setUserPreferredBand(band);
}
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd("done set frequency band " + band);
}
} else {
@@ -2952,7 +2843,7 @@
}
private void setSuspendOptimizationsNative(int reason, boolean enabled) {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
log("setSuspendOptimizationsNative: " + reason + " " + enabled
+ " -want " + mUserWantsSuspendOpt.get()
+ " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
@@ -2966,7 +2857,7 @@
mSuspendOptNeedsDisabled &= ~reason;
/* None of dhcp, screen or highperf need it disabled and user wants it enabled */
if (mSuspendOptNeedsDisabled == 0 && mUserWantsSuspendOpt.get()) {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
log("setSuspendOptimizationsNative do it " + reason + " " + enabled
+ " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
+ " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
@@ -2982,13 +2873,13 @@
}
private void setSuspendOptimizations(int reason, boolean enabled) {
- if (DBG) log("setSuspendOptimizations: " + reason + " " + enabled);
+ if (mVerboseLoggingEnabled) log("setSuspendOptimizations: " + reason + " " + enabled);
if (enabled) {
mSuspendOptNeedsDisabled &= ~reason;
} else {
mSuspendOptNeedsDisabled |= reason;
}
- if (DBG) log("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
+ if (mVerboseLoggingEnabled) log("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
}
private void setWifiState(int wifiState) {
@@ -3006,7 +2897,7 @@
mWifiState.set(wifiState);
- if (DBG) log("setWifiState: " + syncGetWifiStateByName());
+ if (mVerboseLoggingEnabled) log("setWifiState: " + syncGetWifiStateByName());
final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
@@ -3031,7 +2922,7 @@
// Update state
mWifiApState.set(wifiApState);
- if (DBG) log("setWifiApState: " + syncGetWifiApStateByName());
+ if (mVerboseLoggingEnabled) log("setWifiApState: " + syncGetWifiApStateByName());
final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
@@ -3069,17 +2960,9 @@
}
synchronized (mScanResultsLock) {
- ScanDetail activeScanDetail = null;
mScanResults = scanResults;
mNumScanResultsReturned = mScanResults.size();
for (ScanDetail resultDetail : mScanResults) {
- if (connected && resultDetail.getNetworkDetail().getBSSID() == activeBssid) {
- if (activeScanDetail == null
- || activeScanDetail.getNetworkDetail().getBSSID() != activeBssid
- || activeScanDetail.getNetworkDetail().getANQPElements() == null) {
- activeScanDetail = resultDetail;
- }
- }
// Cache DTIM values parsed from the beacon frame Traffic Indication Map (TIM)
// Information Element (IE), into the associated WifiConfigurations. Most of the
// time there is no TIM IE in the scan result (Probe Response instead of Beacon
@@ -3098,10 +2981,9 @@
}
}
}
- mWifiConfigManager.setActiveScanDetail(activeScanDetail);
}
- if (linkDebouncing) {
+ if (isLinkDebouncing()) {
// If debouncing, we dont re-select a SSID or BSSID hence
// there is no need to call the network selection code
// in WifiAutoJoinController, instead,
@@ -3140,7 +3022,7 @@
}
}
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd("fetchRssiLinkSpeedAndFrequencyNative rssi=" + newRssi +
" linkspeed=" + newLinkSpeed + " freq=" + newFrequency);
}
@@ -3237,7 +3119,7 @@
}
private void updateLinkProperties(LinkProperties newLp) {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
log("Link configuration changed for netId: " + mLastNetworkId
+ " old: " + mLinkProperties + " new: " + newLp);
}
@@ -3253,7 +3135,7 @@
sendLinkConfigurationChangedBroadcast();
}
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
StringBuilder sb = new StringBuilder();
sb.append("updateLinkProperties nid: " + mLastNetworkId);
sb.append(" state: " + getNetworkDetailedState());
@@ -3292,7 +3174,7 @@
if (route.isDefaultRoute() && route.hasGateway()) {
InetAddress gateway = route.getGateway();
if (gateway instanceof Inet4Address) {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd("updateDefaultRouteMacAddress found Ipv4 default :"
+ gateway.getHostAddress());
}
@@ -3310,7 +3192,7 @@
if (reachable == true) {
address = macAddressFromRoute(gateway.getHostAddress());
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd("updateDefaultRouteMacAddress reachable (tried again) :"
+ gateway.getHostAddress() + " found " + address);
}
@@ -3409,7 +3291,7 @@
private boolean setNetworkDetailedState(NetworkInfo.DetailedState state) {
boolean hidden = false;
- if (linkDebouncing || isRoaming()) {
+ if (isLinkDebouncing() || isRoaming()) {
// There is generally a confusion in the system about colluding
// WiFi Layer 2 state (as reported by supplicant) and the Network state
// which leads to multiple confusion.
@@ -3424,7 +3306,7 @@
//
hidden = true;
}
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
log("setDetailed state, old ="
+ mNetworkInfo.getDetailedState() + " and new state=" + state
+ " hidden=" + hidden);
@@ -3433,7 +3315,7 @@
&& !mWifiInfo.getSSID().equals(WifiSsid.NONE)) {
// Always indicate that SSID has changed
if (!mNetworkInfo.getExtraInfo().equals(mWifiInfo.getSSID())) {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
log("setDetailed state send new extra info" + mWifiInfo.getSSID());
}
mNetworkInfo.setExtraInfo(mWifiInfo.getSSID());
@@ -3471,7 +3353,7 @@
// this implies that wpa_supplicant is already disconnected.
// We should pretend we are still connected when linkDebouncing is on.
if ((stateChangeResult.wifiSsid == null
- || stateChangeResult.wifiSsid.toString().isEmpty()) && linkDebouncing) {
+ || stateChangeResult.wifiSsid.toString().isEmpty()) && isLinkDebouncing()) {
return state;
}
// Network id is only valid when we start connecting
@@ -3483,30 +3365,6 @@
mWifiInfo.setBSSID(stateChangeResult.BSSID);
- if (mWhiteListedSsids != null
- && mWhiteListedSsids.length > 0
- && stateChangeResult.wifiSsid != null) {
- String SSID = stateChangeResult.wifiSsid.toString();
- String currentSSID = mWifiInfo.getSSID();
- if (SSID != null && currentSSID != null && !SSID.equals(WifiSsid.NONE)) {
- // Remove quote before comparing
- if (SSID.length() >= 2 && SSID.charAt(0) == '"'
- && SSID.charAt(SSID.length() - 1) == '"') {
- SSID = SSID.substring(1, SSID.length() - 1);
- }
- if (currentSSID.length() >= 2 && currentSSID.charAt(0) == '"'
- && currentSSID.charAt(currentSSID.length() - 1) == '"') {
- currentSSID = currentSSID.substring(1, currentSSID.length() - 1);
- }
- if ((!SSID.equals(currentSSID)) && (getCurrentState() == mConnectedState)) {
- lastConnectAttemptTimestamp = System.currentTimeMillis();
- targetWificonfiguration =
- mWifiConfigManager.getWifiConfiguration(mWifiInfo.getNetworkId());
- transitionTo(mRoamingState);
- }
- }
- }
-
mWifiInfo.setSSID(stateChangeResult.wifiSsid);
mWifiInfo.setEphemeral(mWifiConfigManager.isEphemeral(mWifiInfo.getNetworkId()));
if (!mWifiInfo.getMeteredHint()) { // don't override the value if already set.
@@ -3523,11 +3381,13 @@
* using the interface, stopping DHCP & disabling interface
*/
private void handleNetworkDisconnect() {
- if (DBG) log("handleNetworkDisconnect: Stopping DHCP and clearing IP"
- + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
- + " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
- + " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
- + " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
+ if (mVerboseLoggingEnabled) {
+ log("handleNetworkDisconnect: Stopping DHCP and clearing IP"
+ + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
+ + " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
+ + " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
+ + " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
+ }
stopRssiMonitoringOffload();
@@ -3538,7 +3398,7 @@
/* Reset data structures */
mWifiScoreReport = null;
mWifiInfo.reset();
- linkDebouncing = false;
+ mIsLinkDebouncing = false;
/* Reset roaming parameters */
mAutoRoaming = false;
@@ -3594,7 +3454,7 @@
*/
// Disable the coexistence mode
mWifiNative.setBluetoothCoexistenceMode(
- mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
+ WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
}
// Disable power save and suspend optimizations during DHCP
@@ -3605,15 +3465,17 @@
mWifiNative.setPowerSave(false);
// Update link layer stats
- getWifiLinkLayerStats(false);
+ getWifiLinkLayerStats();
- /* P2p discovery breaks dhcp, shut it down in order to get through this */
- Message msg = new Message();
- msg.what = WifiP2pServiceImpl.BLOCK_DISCOVERY;
- msg.arg1 = WifiP2pServiceImpl.ENABLED;
- msg.arg2 = DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE;
- msg.obj = WifiStateMachine.this;
- mWifiP2pChannel.sendMessage(msg);
+ if (mWifiP2pChannel != null) {
+ /* P2p discovery breaks dhcp, shut it down in order to get through this */
+ Message msg = new Message();
+ msg.what = WifiP2pServiceImpl.BLOCK_DISCOVERY;
+ msg.arg1 = WifiP2pServiceImpl.ENABLED;
+ msg.arg2 = DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE;
+ msg.obj = WifiStateMachine.this;
+ mWifiP2pChannel.sendMessage(msg);
+ }
}
void handlePostDhcpSetup() {
@@ -3621,12 +3483,11 @@
setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, true);
mWifiNative.setPowerSave(true);
- mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.BLOCK_DISCOVERY,
- WifiP2pServiceImpl.DISABLED);
+ p2pSendMessage(WifiP2pServiceImpl.BLOCK_DISCOVERY, WifiP2pServiceImpl.DISABLED);
// Set the coexistence mode back to its default value
mWifiNative.setBluetoothCoexistenceMode(
- mWifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
+ WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
}
/**
@@ -3638,6 +3499,7 @@
switch (level2FailureCode) {
case WifiMetrics.ConnectionEvent.FAILURE_NONE:
case WifiMetrics.ConnectionEvent.FAILURE_REDUNDANT_CONNECTION_ATTEMPT:
+ case WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED:
// WifiLogger doesn't care about success, or pre-empted connections.
break;
default:
@@ -3646,7 +3508,7 @@
}
private void handleIPv4Success(DhcpResults dhcpResults) {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd("handleIPv4Success <" + dhcpResults.toString() + ">");
logd("link address " + dhcpResults.ipAddress);
}
@@ -3705,7 +3567,7 @@
// TODO: Move this to provisioning failure, not DHCP failure.
// DHCPv4 failure is expected on an IPv6-only network.
mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_DHCP_FAILURE);
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
int count = -1;
WifiConfiguration config = getCurrentWifiConfiguration();
if (config != null) {
@@ -3722,7 +3584,7 @@
mDhcpResults.clear();
}
}
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd("handleIPv4Failure");
}
}
@@ -3751,82 +3613,28 @@
mWifiNative.disconnect();
}
- private int convertFrequencyToChannelNumber(int frequency) {
- if (frequency >= 2412 && frequency <= 2484) {
- return (frequency -2412) / 5 + 1;
- } else if (frequency >= 5170 && frequency <=5825) {
- //DFS is included
- return (frequency -5170) / 5 + 34;
- } else {
- return 0;
- }
- }
-
- private int chooseApChannel(int apBand) {
- int apChannel;
- int[] channel;
-
- if (apBand == 0) {
- ArrayList<Integer> allowed2GChannel =
- mWifiApConfigStore.getAllowed2GChannel();
- if (allowed2GChannel == null || allowed2GChannel.size() == 0) {
- //most safe channel to use
- if (DBG) {
- Log.d(TAG, "No specified 2G allowed channel list");
- }
- apChannel = 6;
- } else {
- int index = mRandom.nextInt(allowed2GChannel.size());
- apChannel = allowed2GChannel.get(index).intValue();
- }
- } else {
- //5G without DFS
- channel = mWifiNative.getChannelsForBand(2);
- if (channel != null && channel.length > 0) {
- apChannel = channel[mRandom.nextInt(channel.length)];
- apChannel = convertFrequencyToChannelNumber(apChannel);
- } else {
- Log.e(TAG, "SoftAp do not get available channel list");
- apChannel = 0;
- }
- }
-
- if (DBG) {
- Log.d(TAG, "SoftAp set on channel " + apChannel);
- }
-
- return apChannel;
- }
-
/* Driver/firmware setup for soft AP. */
- private boolean setupDriverForSoftAp() {
- if (!mWifiNative.loadDriver()) {
- Log.e(TAG, "Failed to load driver for softap");
- return false;
+ private IApInterface setupDriverForSoftAp() {
+ if (mWificond == null) {
+ Log.e(TAG, "Failed to get reference to wificond");
+ return null;
}
- int index = mWifiNative.queryInterfaceIndex(mInterfaceName);
- if (index != -1) {
- if (!mWifiNative.setInterfaceUp(false)) {
- Log.e(TAG, "toggleInterface failed");
- return false;
- }
- } else {
- if (DBG) Log.d(TAG, "No interfaces to bring down");
- }
-
+ IApInterface apInterface = null;
try {
- mNwService.wifiFirmwareReload(mInterfaceName, "AP");
- if (DBG) Log.d(TAG, "Firmware reloaded in AP mode");
- } catch (Exception e) {
- Log.e(TAG, "Failed to reload AP firmware " + e);
+ apInterface = mWificond.createApInterface();
+ } catch (RemoteException e1) { }
+
+ if (apInterface == null) {
+ Log.e(TAG, "Could not get IApInterface instance from wificond");
+ return null;
}
if (!mWifiNative.startHal()) {
/* starting HAL is optional */
Log.e(TAG, "Failed to start HAL");
}
- return true;
+ return apInterface;
}
private byte[] macAddressFromString(String macString) {
@@ -3909,6 +3717,7 @@
--mConnectionRequests;
}
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("mConnectionRequests " + mConnectionRequests);
}
@@ -3946,6 +3755,7 @@
}
}
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("mUntrustedReqCount " + mUntrustedReqCount);
}
@@ -3975,6 +3785,7 @@
*******************************************************/
class DefaultState extends State {
+
@Override
public boolean processMessage(Message message) {
logStateAndMessage(message, this);
@@ -3984,7 +3795,7 @@
AsyncChannel ac = (AsyncChannel) message.obj;
if (ac == mWifiP2pChannel) {
if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- mWifiP2pChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+ p2pSendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
} else {
loge("WifiP2pService connection failure, error=" + message.arg1);
}
@@ -4081,7 +3892,6 @@
case CMD_DISABLE_P2P_RSP:
case WifiMonitor.SUP_REQUEST_IDENTITY:
case CMD_TEST_NETWORK_DISCONNECT:
- case CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER:
case WifiMonitor.SUP_REQUEST_SIM_AUTH:
case CMD_TARGET_BSSID:
case CMD_AUTO_CONNECT:
@@ -4092,7 +3902,6 @@
case CMD_DISCONNECTING_WATCHDOG_TIMER:
case CMD_ROAM_WATCHDOG_TIMER:
case CMD_DISABLE_EPHEMERAL_NETWORK:
- case CMD_UPDATE_ASSOCIATED_SCAN_PERMISSION:
messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
break;
case CMD_SET_SUSPEND_OPT_ENABLED:
@@ -4235,9 +4044,18 @@
class InitialState extends State {
@Override
public void enter() {
+ if (mWificond != null) {
+ try {
+ mWificond.tearDownInterfaces();
+ } catch (RemoteException e) {
+ // There is very little we can do here
+ Log.e(TAG, "Failed to tear down interfaces via wificond");
+ }
+ mWificond = null;
+ }
mWifiNative.stopHal();
mWifiNative.unloadDriver();
- if (mWifiP2pChannel == null) {
+ if (mWifiP2pChannel == null && mWifiP2pServiceImpl != null) {
mWifiP2pChannel = new AsyncChannel();
mWifiP2pChannel.connect(mContext, getHandler(),
mWifiP2pServiceImpl.getP2pStateMachineMessenger());
@@ -4299,7 +4117,7 @@
if (mWifiNative.startSupplicant(mP2pSupported)) {
setSupplicantLogLevel();
setWifiState(WIFI_STATE_ENABLING);
- if (DBG) log("Supplicant start successful");
+ if (mVerboseLoggingEnabled) log("Supplicant start successful");
mWifiMonitor.startMonitoring(mInterfaceName);
transitionTo(mSupplicantStartingState);
} else {
@@ -4312,17 +4130,9 @@
}
break;
case CMD_START_AP:
- if (setupDriverForSoftAp()) {
- transitionTo(mSoftApState);
- } else {
- setWifiApState(WIFI_AP_STATE_FAILED,
- WifiManager.SAP_START_FAILURE_GENERAL);
- /**
- * Transition to InitialState (current state) to reset the
- * driver/HAL back to the initial state.
- */
- transitionTo(mInitialState);
- }
+ // Refresh our reference to wificond. This allows us to tolerate restarts.
+ mWificond = mWifiInjector.makeWificond();
+ transitionTo(mSoftApState);
break;
default:
return NOT_HANDLED;
@@ -4368,7 +4178,7 @@
switch(message.what) {
case WifiMonitor.SUP_CONNECTION_EVENT:
- if (DBG) log("Supplicant connection established");
+ if (mVerboseLoggingEnabled) log("Supplicant connection established");
setWifiState(WIFI_STATE_ENABLED);
mSupplicantRestartCount = 0;
/* Reset the supplicant state to indicate the supplicant
@@ -4384,9 +4194,6 @@
setFrequencyBand();
mWifiNative.enableSaveConfig();
mWifiConfigManager.loadAndEnableAllNetworks();
- if (mWifiConfigManager.mEnableVerboseLogging.get() > 0) {
- enableVerboseLogging(mWifiConfigManager.mEnableVerboseLogging.get());
- }
initializeWpsDetails();
sendSupplicantConnectionChangedBroadcast(true);
@@ -4514,7 +4321,7 @@
}
break;
case CMD_GET_LINK_LAYER_STATS:
- WifiLinkLayerStats stats = getWifiLinkLayerStats(DBG);
+ WifiLinkLayerStats stats = getWifiLinkLayerStats();
replyToMessage(message, message.what, stats);
break;
case CMD_RESET_SIM_NETWORKS:
@@ -4566,7 +4373,7 @@
loge("Supplicant connection received while stopping");
break;
case WifiMonitor.SUP_DISCONNECTION_EVENT:
- if (DBG) log("Supplicant connection lost");
+ if (mVerboseLoggingEnabled) log("Supplicant connection lost");
handleSupplicantConnectionLoss(false);
transitionTo(mInitialState);
break;
@@ -4610,7 +4417,7 @@
switch(message.what) {
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
SupplicantState state = handleSupplicantStateChange(message);
- /* If suplicant is exiting out of INTERFACE_DISABLED state into
+ /* If supplicant is exiting out of INTERFACE_DISABLED state into
* a state that indicates driver has started, it is ready to
* receive driver commands
*/
@@ -4668,7 +4475,7 @@
class DriverStartedState extends State {
@Override
public void enter() {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd("DriverStartedState enter");
}
@@ -4683,7 +4490,7 @@
getHandler().getLooper());
}
- mWifiLogger.startLogging(DBG);
+ mWifiLogger.startLogging(mVerboseLoggingEnabled);
mIsRunning = true;
updateBatteryWorkSource(null);
/**
@@ -4714,19 +4521,18 @@
// Status pulls in the current supplicant state and network connection state
// events over the monitor connection. This helps framework sync up with
// current supplicant state
- // TODO: actually check th supplicant status string and make sure the supplicant
+ // TODO: actually check the supplicant status string and make sure the supplicant
// is in disconnecte4d state.
mWifiNative.status();
// Transitioning to Disconnected state will trigger a scan and subsequently AutoJoin
transitionTo(mDisconnectedState);
- transitionTo(mDisconnectedState);
}
// We may have missed screen update at boot
if (mScreenBroadcastReceived.get() == false) {
PowerManager powerManager = (PowerManager)mContext.getSystemService(
Context.POWER_SERVICE);
- handleScreenStateChanged(powerManager.isScreenOn());
+ handleScreenStateChanged(powerManager.isInteractive());
} else {
// Set the right suspend mode settings
mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0
@@ -4741,14 +4547,25 @@
if (mP2pSupported) {
if (mOperationalMode == CONNECT_MODE) {
- mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
+ p2pSendMessage(WifiStateMachine.CMD_ENABLE_P2P);
} else {
- // P2P statemachine starts in disabled state, and is not enabled until
+ // P2P state machine starts in disabled state, and is not enabled until
// CMD_ENABLE_P2P is sent from here; so, nothing needs to be done to
// keep it disabled.
}
}
+ if (mNanSupported && mWifiNanManager != null) {
+ if (mOperationalMode == CONNECT_MODE) {
+ mWifiNanManager.enableUsage();
+ } else {
+ /*
+ * NAN state machine starts in disabled state. Nothing
+ * needed to keep it disabled.
+ */
+ }
+ }
+
final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED);
@@ -4768,16 +4585,16 @@
break;
case CMD_SET_FREQUENCY_BAND:
int band = message.arg1;
- if (DBG) log("set frequency band " + band);
+ if (mVerboseLoggingEnabled) log("set frequency band " + band);
if (mWifiNative.setBand(band)) {
- if (DBG) logd("did set frequency band " + band);
+ if (mVerboseLoggingEnabled) logd("did set frequency band " + band);
mFrequencyBand.set(band);
// Flush old data - like scan results
mWifiNative.bssFlush();
- if (DBG) logd("done set frequency band " + band);
+ if (mVerboseLoggingEnabled) logd("done set frequency band " + band);
} else {
loge("Failed to set frequency band " + band);
@@ -4789,8 +4606,6 @@
mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
break;
case CMD_STOP_DRIVER:
- int mode = message.arg1;
-
log("stop driver");
mWifiConfigManager.disableAllNetworksNative();
@@ -4878,9 +4693,9 @@
}
return HANDLED;
}
+
@Override
public void exit() {
-
mWifiLogger.stopLogging();
mIsRunning = false;
@@ -4892,6 +4707,10 @@
intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
mBufferedScanMsg.clear();
+
+ if (mNanSupported && mWifiNanManager != null) {
+ mWifiNanManager.disableUsage();
+ }
}
}
@@ -4913,7 +4732,7 @@
mTransitionToState = mDriverStoppingState;
break;
}
- mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ);
+ p2pSendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ);
}
@Override
public boolean processMessage(Message message) {
@@ -5023,7 +4842,7 @@
// Load and re-enable networks when going back to enabled state
// This is essential for networks to show up after restore
mWifiConfigManager.loadAndEnableAllNetworks();
- mWifiP2pChannel.sendMessage(CMD_ENABLE_P2P);
+ p2pSendMessage(CMD_ENABLE_P2P);
} else {
mWifiConfigManager.enableAllNetworks();
}
@@ -5047,7 +4866,7 @@
break;
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
SupplicantState state = handleSupplicantStateChange(message);
- if (DBG) log("SupplicantState= " + state);
+ if (mVerboseLoggingEnabled) log("SupplicantState= " + state);
break;
default:
return NOT_HANDLED;
@@ -5200,7 +5019,7 @@
if (config != null) {
//Here we will clear all disable counters once a network is connected
//records how long this network is connected in future
- config.lastConnected = System.currentTimeMillis();
+ config.lastConnected = mClock.getWallClockMillis();
config.numAssociation++;
WifiConfiguration.NetworkSelectionStatus networkSelectionStatus =
config.getNetworkSelectionStatus();
@@ -5218,7 +5037,7 @@
// hence record the time we were connected last
WifiConfiguration config = mWifiConfigManager.getWifiConfiguration(mLastNetworkId);
if (config != null) {
- config.lastDisconnected = System.currentTimeMillis();
+ config.lastDisconnected = mClock.getWallClockMillis();
if (config.ephemeral) {
// Remove ephemeral WifiConfigurations from file
mWifiConfigManager.forgetNetwork(mLastNetworkId);
@@ -5289,7 +5108,7 @@
}
String getCurrentBSSID() {
- if (linkDebouncing) {
+ if (isLinkDebouncing()) {
return null;
}
return mLastBssid;
@@ -5305,8 +5124,6 @@
}
// Inform metrics that Wifi is Enabled (but not yet connected)
mWifiMetrics.setWifiState(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED);
-
-
}
@Override
@@ -5356,9 +5173,10 @@
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION,
WifiMetricsProto.ConnectionEvent.HLF_NONE);
- mWifiLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(getTargetSsid(),
- bssid,
- WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
+ mWifiInjector.getWifiLastResortWatchdog()
+ .noteConnectionFailureAndTriggerIfNeeded(
+ getTargetSsid(), bssid,
+ WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
break;
case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_AUTH_FAILURE);
@@ -5372,9 +5190,10 @@
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,
WifiMetricsProto.ConnectionEvent.HLF_NONE);
- mWifiLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(getTargetSsid(),
- mTargetRoamBSSID,
- WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
+ mWifiInjector.getWifiLastResortWatchdog()
+ .noteConnectionFailureAndTriggerIfNeeded(
+ getTargetSsid(), mTargetRoamBSSID,
+ WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
break;
case WifiMonitor.SSID_TEMP_DISABLED:
Log.e(TAG, "Supplicant SSID temporary disabled:"
@@ -5386,9 +5205,10 @@
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_SSID_TEMP_DISABLED,
WifiMetricsProto.ConnectionEvent.HLF_NONE);
- mWifiLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(getTargetSsid(),
- mTargetRoamBSSID,
- WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
+ mWifiInjector.getWifiLastResortWatchdog()
+ .noteConnectionFailureAndTriggerIfNeeded(
+ getTargetSsid(), mTargetRoamBSSID,
+ WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
break;
case WifiMonitor.SSID_REENABLED:
Log.d(TAG, "Supplicant SSID reenable:"
@@ -5415,9 +5235,11 @@
// we can figure this from the supplicant state. If supplicant
// state is DISCONNECTED, but the mNetworkInfo says we are not
// disconnected, we need to handle a disconnection
- if (!linkDebouncing && state == SupplicantState.DISCONNECTED &&
+ if (!isLinkDebouncing() && state == SupplicantState.DISCONNECTED &&
mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
- if (DBG) log("Missed CTRL-EVENT-DISCONNECTED, disconnect");
+ if (mVerboseLoggingEnabled) {
+ log("Missed CTRL-EVENT-DISCONNECTED, disconnect");
+ }
handleNetworkDisconnect();
transitionTo(mDisconnectedState);
}
@@ -5484,7 +5306,7 @@
}
// Remember time of last connection attempt
- lastConnectAttemptTimestamp = System.currentTimeMillis();
+ lastConnectAttemptTimestamp = mClock.getWallClockMillis();
mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
// As a courtesy to the caller, trigger a scan now
@@ -5545,7 +5367,7 @@
// networks need to be disabled
if (disableOthers) {
// Remember time of last connection attempt
- lastConnectAttemptTimestamp = System.currentTimeMillis();
+ lastConnectAttemptTimestamp = mClock.getWallClockMillis();
mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
}
// Cancel auto roam requests
@@ -5596,7 +5418,7 @@
case CMD_SAVE_CONFIG:
ok = mWifiConfigManager.saveConfig();
- if (DBG) logd("did save config " + ok);
+ if (mVerboseLoggingEnabled) logd("did save config " + ok);
replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
// Inform the backup manager about a data change
@@ -5679,14 +5501,14 @@
}
break;
case CMD_REASSOCIATE:
- lastConnectAttemptTimestamp = System.currentTimeMillis();
+ lastConnectAttemptTimestamp = mClock.getWallClockMillis();
mWifiNative.reassociate();
break;
case CMD_RELOAD_TLS_AND_RECONNECT:
if (mWifiConfigManager.needsUnlockedKeyStore()) {
logd("Reconnecting to give a chance to un-connected TLS networks");
mWifiNative.disconnect();
- lastConnectAttemptTimestamp = System.currentTimeMillis();
+ lastConnectAttemptTimestamp = mClock.getWallClockMillis();
mWifiNative.reconnect();
}
break;
@@ -5783,7 +5605,7 @@
}
if (mWifiConfigManager.selectNetwork(config, /* updatePriorities = */ false,
lastConnectUid) && mWifiNative.reconnect()) {
- lastConnectAttemptTimestamp = System.currentTimeMillis();
+ lastConnectAttemptTimestamp = mClock.getWallClockMillis();
targetWificonfiguration = mWifiConfigManager.getWifiConfiguration(netId);
config = mWifiConfigManager.getWifiConfiguration(netId);
if (config != null
@@ -5805,7 +5627,7 @@
WifiConfiguration.INVALID_NETWORK_ID);
}
mAutoRoaming = false;
- if (isRoaming() || linkDebouncing) {
+ if (isRoaming() || isLinkDebouncing()) {
transitionTo(mRoamingState);
} else if (didDisconnect) {
transitionTo(mDisconnectingState);
@@ -5946,7 +5768,7 @@
WifiMetricsProto.ConnectionEvent.ROAM_USER_SELECTED);
if (mWifiConfigManager.selectNetwork(config, /* updatePriorities = */ true,
message.sendingUid) && mWifiNative.reconnect()) {
- lastConnectAttemptTimestamp = System.currentTimeMillis();
+ lastConnectAttemptTimestamp = mClock.getWallClockMillis();
targetWificonfiguration = mWifiConfigManager.getWifiConfiguration(netId);
/* The state tracker handles enabling networks upon completion/failure */
@@ -6045,7 +5867,7 @@
replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd("Success save network nid="
+ Integer.toString(result.getNetworkId()));
}
@@ -6167,14 +5989,15 @@
}
return NOT_HANDLED;
case WifiMonitor.NETWORK_CONNECTION_EVENT:
- if (DBG) log("Network connection established");
+ if (mVerboseLoggingEnabled) log("Network connection established");
mLastNetworkId = message.arg1;
mLastBssid = (String) message.obj;
mWifiInfo.setBSSID(mLastBssid);
mWifiInfo.setNetworkId(mLastNetworkId);
- mWifiQualifiedNetworkSelector
- .enableBssidForQualityNetworkSelection(mLastBssid, true);
+ if (mWifiConnectivityManager != null) {
+ mWifiConnectivityManager.trackBssid(mLastBssid, true);
+ }
sendNetworkStateChangeBroadcast(mLastBssid);
transitionTo(mObtainingIpState);
break;
@@ -6188,7 +6011,7 @@
// The side effect of calling handleNetworkDisconnect twice is that a bunch of
// idempotent commands are executed twice (stopping Dhcp, enabling the SPS mode
// at the chip etc...
- if (DBG) log("ConnectModeState: Network connection lost ");
+ if (mVerboseLoggingEnabled) log("ConnectModeState: Network connection lost ");
handleNetworkDisconnect();
transitionTo(mDisconnectedState);
break;
@@ -6258,11 +6081,14 @@
NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {
super(l, c, TAG, ni, nc, lp, score, misc);
}
+
+ @Override
protected void unwanted() {
// Ignore if we're not the current networkAgent.
if (this != mNetworkAgent) return;
- if (DBG) log("WifiNetworkAgent -> Wifi unwanted score "
- + Integer.toString(mWifiInfo.score));
+ if (mVerboseLoggingEnabled) {
+ log("WifiNetworkAgent -> Wifi unwanted score " + Integer.toString(mWifiInfo.score));
+ }
unwantedNetwork(NETWORK_STATUS_UNWANTED_DISCONNECT);
}
@@ -6270,11 +6096,13 @@
protected void networkStatus(int status, String redirectUrl) {
if (this != mNetworkAgent) return;
if (status == NetworkAgent.INVALID_NETWORK) {
- if (DBG) log("WifiNetworkAgent -> Wifi networkStatus invalid, score="
- + Integer.toString(mWifiInfo.score));
+ if (mVerboseLoggingEnabled) {
+ log("WifiNetworkAgent -> Wifi networkStatus invalid, score="
+ + Integer.toString(mWifiInfo.score));
+ }
unwantedNetwork(NETWORK_STATUS_UNWANTED_VALIDATION_FAILED);
} else if (status == NetworkAgent.VALID_NETWORK) {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
log("WifiNetworkAgent -> Wifi networkStatus valid, score= "
+ Integer.toString(mWifiInfo.score));
}
@@ -6412,7 +6240,7 @@
}
HashSet<Integer> freqs = mWifiConfigManager.makeChannelList(config, ONE_HOUR_MILLI);
if (freqs != null && freqs.size() != 0) {
- //if (DBG) {
+ //if (mVerboseLoggingEnabled) {
logd("starting scan for " + config.configKey() + " with " + freqs);
//}
Set<Integer> hiddenNetworkIds = new HashSet<>();
@@ -6428,7 +6256,7 @@
}
return true;
} else {
- if (DBG) logd("no channels for " + config.configKey());
+ if (mVerboseLoggingEnabled) logd("no channels for " + config.configKey());
return false;
}
}
@@ -6443,12 +6271,12 @@
void clearConfigBSSID(WifiConfiguration config, String dbg) {
if (config == null)
return;
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd(dbg + " " + mTargetRoamBSSID + " config " + config.configKey()
+ " config.NetworkSelectionStatus.mNetworkSelectionBSSID "
+ config.getNetworkSelectionStatus().getNetworkSelectionBSSID());
}
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd(dbg + " " + config.SSID
+ " nid=" + Integer.toString(config.networkId));
}
@@ -6489,7 +6317,7 @@
// For paranoia's sake, call handleNetworkDisconnect
// only if BSSID is null or last networkId
// is not invalid.
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
StringBuilder sb = new StringBuilder();
sb.append("leaving L2ConnectedState state nid=" + Integer.toString(mLastNetworkId));
if (mLastBssid !=null) {
@@ -6543,12 +6371,15 @@
break;
case CMD_IP_CONFIGURATION_LOST:
// Get Link layer stats so that we get fresh tx packet counters.
- getWifiLinkLayerStats(true);
+ getWifiLinkLayerStats();
handleIpConfigurationLost();
+ reportConnectionAttemptEnd(
+ WifiMetrics.ConnectionEvent.FAILURE_DHCP,
+ WifiMetricsProto.ConnectionEvent.HLF_NONE);
transitionTo(mDisconnectingState);
break;
case CMD_IP_REACHABILITY_LOST:
- if (DBG && message.obj != null) log((String) message.obj);
+ if (mVerboseLoggingEnabled && message.obj != null) log((String) message.obj);
handleIpReachabilityLost();
transitionTo(mDisconnectingState);
break;
@@ -6586,7 +6417,7 @@
mWifiInfo.setBSSID((String) message.obj);
mLastNetworkId = message.arg1;
mWifiInfo.setNetworkId(mLastNetworkId);
- if(!mLastBssid.equals((String) message.obj)) {
+ if(!mLastBssid.equals(message.obj)) {
mLastBssid = (String) message.obj;
sendNetworkStateChangeBroadcast(mLastBssid);
}
@@ -6594,8 +6425,10 @@
case CMD_RSSI_POLL:
if (message.arg1 == mRssiPollToken) {
if (mWifiConfigManager.mEnableChipWakeUpWhenAssociated.get()) {
- if (DBG) log(" get link layer stats " + mWifiLinkLayerStatsSupported);
- WifiLinkLayerStats stats = getWifiLinkLayerStats(DBG);
+ if (mVerboseLoggingEnabled) {
+ log(" get link layer stats " + mWifiLinkLayerStatsSupported);
+ }
+ WifiLinkLayerStats stats = getWifiLinkLayerStats();
if (stats != null) {
// Sanity check the results provided by driver
if (mWifiInfo.getRssi() != WifiInfo.INVALID_RSSI
@@ -6612,11 +6445,12 @@
mWifiConfigManager,
mNetworkAgent,
mWifiScoreReport,
- mAggressiveHandover);
+ mAggressiveHandover,
+ mVerboseLoggingEnabled);
}
sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
- if (DBG) sendRssiChangeBroadcast(mWifiInfo.getRssi());
+ if (mVerboseLoggingEnabled) sendRssiChangeBroadcast(mWifiInfo.getRssi());
} else {
// Polling has completed
}
@@ -6644,7 +6478,7 @@
replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info);
break;
case CMD_DELAYED_NETWORK_DISCONNECT:
- if (!linkDebouncing && mWifiConfigManager.mEnableLinkDebouncing) {
+ if (!isLinkDebouncing()) {
// Ignore if we are not debouncing
logd("CMD_DELAYED_NETWORK_DISCONNECT and not debouncing - ignore "
@@ -6654,7 +6488,7 @@
logd("CMD_DELAYED_NETWORK_DISCONNECT and debouncing - disconnect "
+ message.arg1);
- linkDebouncing = false;
+ mIsLinkDebouncing = false;
// If we are still debouncing while this message comes,
// it means we were not able to reconnect within the alloted time
// = LINK_FLAPPING_DEBOUNCE_MSEC
@@ -6706,7 +6540,7 @@
class ObtainingIpState extends State {
@Override
public void enter() {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
String key = "";
if (getCurrentWifiConfiguration() != null) {
key = getCurrentWifiConfiguration().configKey();
@@ -6714,13 +6548,12 @@
log("enter ObtainingIpState netId=" + Integer.toString(mLastNetworkId)
+ " " + key + " "
+ " roam=" + mAutoRoaming
- + " static=" + mWifiConfigManager.isUsingStaticIp(mLastNetworkId)
- + " watchdog= " + obtainingIpWatchdogCount);
+ + " static=" + mWifiConfigManager.isUsingStaticIp(mLastNetworkId));
}
// Reset link Debouncing, indicating we have successfully re-connected to the AP
// We might still be roaming
- linkDebouncing = false;
+ mIsLinkDebouncing = false;
// Send event to CM & network change broadcast
setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
@@ -6749,17 +6582,13 @@
if (!mWifiConfigManager.isUsingStaticIp(mLastNetworkId)) {
final IpManager.ProvisioningConfiguration prov =
- mIpManager.buildProvisioningConfiguration()
+ IpManager.buildProvisioningConfiguration()
.withPreDhcpAction()
.withApfCapabilities(mWifiNative.getApfCapabilities())
.build();
mIpManager.startProvisioning(prov);
- obtainingIpWatchdogCount++;
- logd("Start Dhcp Watchdog " + obtainingIpWatchdogCount);
// Get Link layer stats so as we get fresh tx packet counters
- getWifiLinkLayerStats(true);
- sendMessageDelayed(obtainMessage(CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER,
- obtainingIpWatchdogCount, 0), OBTAINING_IP_ADDRESS_GUARD_TIMER_MSEC);
+ getWifiLinkLayerStats();
} else {
StaticIpConfiguration config = mWifiConfigManager.getStaticIpConfiguration(
mLastNetworkId);
@@ -6768,7 +6597,7 @@
sendMessage(CMD_IPV4_PROVISIONING_FAILURE);
} else {
final IpManager.ProvisioningConfiguration prov =
- mIpManager.buildProvisioningConfiguration()
+ IpManager.buildProvisioningConfiguration()
.withStaticConfiguration(config)
.withApfCapabilities(mWifiNative.getApfCapabilities())
.build();
@@ -6807,16 +6636,6 @@
messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
deferMessage(message);
break;
- case CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER:
- if (message.arg1 == obtainingIpWatchdogCount) {
- logd("ObtainingIpAddress: Watchdog Triggered, count="
- + obtainingIpWatchdogCount);
- handleIpConfigurationLost();
- transitionTo(mDisconnectingState);
- break;
- }
- messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
- break;
default:
return NOT_HANDLED;
}
@@ -6831,14 +6650,14 @@
if (mWifiConfigManager.isLastSelectedConfiguration(config)) {
boolean prompt =
mWifiConfigManager.checkConfigOverridePermission(config.lastConnectUid);
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
log("Network selected by UID " + config.lastConnectUid + " prompt=" + prompt);
}
if (prompt) {
// Selected by the user via Settings or QuickSettings. If this network has Internet
// access, switch to it. Otherwise, switch to it only if the user confirms that they
// really want to switch, or has already confirmed and selected "Don't ask again".
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
log("explictlySelected acceptUnvalidated=" + config.noInternetAccessExpected);
}
mNetworkAgent.explicitlySelected(config.noInternetAccessExpected);
@@ -6854,7 +6673,7 @@
boolean mAssociated;
@Override
public void enter() {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
log("RoamingState Enter"
+ " mScreenOn=" + mScreenOn );
}
@@ -6880,7 +6699,9 @@
}
return NOT_HANDLED;
case CMD_UNWANTED_NETWORK:
- if (DBG) log("Roaming and CS doesnt want the network -> ignore");
+ if (mVerboseLoggingEnabled) {
+ log("Roaming and CS doesnt want the network -> ignore");
+ }
return HANDLED;
case CMD_SET_OPERATIONAL_MODE:
if (message.arg1 != CONNECT_MODE) {
@@ -6899,7 +6720,7 @@
if (stateChangeResult.state == SupplicantState.DISCONNECTED
|| stateChangeResult.state == SupplicantState.INACTIVE
|| stateChangeResult.state == SupplicantState.INTERFACE_DISABLED) {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
log("STATE_CHANGE_EVENT in roaming state "
+ stateChangeResult.toString() );
}
@@ -6913,13 +6734,13 @@
// We completed the layer2 roaming part
mAssociated = true;
if (stateChangeResult.BSSID != null) {
- mTargetRoamBSSID = (String) stateChangeResult.BSSID;
+ mTargetRoamBSSID = stateChangeResult.BSSID;
}
}
break;
case CMD_ROAM_WATCHDOG_TIMER:
if (roamWatchdogCount == message.arg1) {
- if (DBG) log("roaming watchdog! -> disconnect");
+ if (mVerboseLoggingEnabled) log("roaming watchdog! -> disconnect");
mWifiMetrics.endConnectionEvent(
WifiMetrics.ConnectionEvent.FAILURE_ROAM_TIMEOUT,
WifiMetricsProto.ConnectionEvent.HLF_NONE);
@@ -6931,7 +6752,9 @@
break;
case WifiMonitor.NETWORK_CONNECTION_EVENT:
if (mAssociated) {
- if (DBG) log("roaming and Network connection established");
+ if (mVerboseLoggingEnabled) {
+ log("roaming and Network connection established");
+ }
mLastNetworkId = message.arg1;
mLastBssid = (String) message.obj;
mWifiInfo.setBSSID(mLastBssid);
@@ -7018,9 +6841,8 @@
class ConnectedState extends State {
@Override
public void enter() {
- String address;
updateDefaultRouteMacAddress(1000);
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
log("Enter ConnectedState "
+ " mScreenOn=" + mScreenOn);
}
@@ -7033,7 +6855,7 @@
lastConnectAttemptTimestamp = 0;
targetWificonfiguration = null;
// Paranoia
- linkDebouncing = false;
+ mIsLinkDebouncing = false;
// Not roaming anymore
mAutoRoaming = false;
@@ -7051,7 +6873,7 @@
mLastDriverRoamAttempt = 0;
mTargetNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
- mWifiLastResortWatchdog.connectedStateTransition(true);
+ mWifiInjector.getWifiLastResortWatchdog().connectedStateTransition(true);
}
@Override
public boolean processMessage(Message message) {
@@ -7059,9 +6881,6 @@
logStateAndMessage(message, this);
switch (message.what) {
- case CMD_UPDATE_ASSOCIATED_SCAN_PERMISSION:
- updateAssociatedScanPermission();
- break;
case CMD_UNWANTED_NETWORK:
if (message.arg1 == NETWORK_STATUS_UNWANTED_DISCONNECT) {
mWifiConfigManager.handleBadNetworkDisconnectReport(
@@ -7121,7 +6940,7 @@
case CMD_ASSOCIATED_BSSID:
// ASSOCIATING to a new BSSID while already connected, indicates
// that driver is roaming
- mLastDriverRoamAttempt = System.currentTimeMillis();
+ mLastDriverRoamAttempt = mClock.getWallClockMillis();
return NOT_HANDLED;
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
long lastRoam = 0;
@@ -7130,7 +6949,7 @@
WifiMetricsProto.ConnectionEvent.HLF_NONE);
if (mLastDriverRoamAttempt != 0) {
// Calculate time since last driver roam attempt
- lastRoam = System.currentTimeMillis() - mLastDriverRoamAttempt;
+ lastRoam = mClock.getWallClockMillis() - mLastDriverRoamAttempt;
mLastDriverRoamAttempt = 0;
}
if (unexpectedDisconnectedReason(message.arg2)) {
@@ -7138,8 +6957,9 @@
WifiLogger.REPORT_REASON_UNEXPECTED_DISCONNECT);
}
config = getCurrentWifiConfiguration();
- if (mScreenOn
- && !linkDebouncing
+ if (mWifiConfigManager.mEnableLinkDebouncing
+ && mScreenOn
+ && !isLinkDebouncing()
&& config != null
&& config.getNetworkSelectionStatus().isNetworkEnabled()
&& !mWifiConfigManager.isLastSelectedConfiguration(config)
@@ -7158,11 +6978,11 @@
// roaming cycle and enter Obtaining IP address
// before signalling the disconnect to ConnectivityService and L3
startScanForConfiguration(getCurrentWifiConfiguration());
- linkDebouncing = true;
+ mIsLinkDebouncing = true;
sendMessageDelayed(obtainMessage(CMD_DELAYED_NETWORK_DISCONNECT,
0, mLastNetworkId), LINK_FLAPPING_DEBOUNCE_MSEC);
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
log("NETWORK_DISCONNECTION_EVENT in connected state"
+ " BSSID=" + mWifiInfo.getBSSID()
+ " RSSI=" + mWifiInfo.getRssi()
@@ -7172,12 +6992,12 @@
}
return HANDLED;
} else {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
log("NETWORK_DISCONNECTION_EVENT in connected state"
+ " BSSID=" + mWifiInfo.getBSSID()
+ " RSSI=" + mWifiInfo.getRssi()
+ " freq=" + mWifiInfo.getFrequency()
- + " was debouncing=" + linkDebouncing
+ + " was debouncing=" + isLinkDebouncing()
+ " reason=" + message.arg2
+ " Network Selection Status=" + (config == null ? "Unavailable"
: config.getNetworkSelectionStatus().getNetworkStatusString()));
@@ -7262,7 +7082,7 @@
ret = mWifiNative.reassociate();
}
if (ret) {
- lastConnectAttemptTimestamp = System.currentTimeMillis();
+ lastConnectAttemptTimestamp = mClock.getWallClockMillis();
targetWificonfiguration = mWifiConfigManager.getWifiConfiguration(netId);
// replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
@@ -7316,8 +7136,7 @@
}
mLastDriverRoamAttempt = 0;
- mWhiteListedSsids = null;
- mWifiLastResortWatchdog.connectedStateTransition(false);
+ mWifiInjector.getWifiLastResortWatchdog().connectedStateTransition(false);
}
}
@@ -7326,7 +7145,7 @@
@Override
public void enter() {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd(" Enter DisconnectingState State screenOn=" + mScreenOn);
}
@@ -7356,7 +7175,7 @@
return HANDLED;
case CMD_DISCONNECTING_WATCHDOG_TIMER:
if (disconnectingWatchdogCount == message.arg1) {
- if (DBG) log("disconnecting watchdog! -> disconnect");
+ if (mVerboseLoggingEnabled) log("disconnecting watchdog! -> disconnect");
handleNetworkDisconnect();
transitionTo(mDisconnectedState);
}
@@ -7384,11 +7203,11 @@
// We dont scan frequently if this is a temporary disconnect
// due to p2p
if (mTemporarilyDisconnectWifi) {
- mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
+ p2pSendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
return;
}
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd(" Enter DisconnectedState screenOn=" + mScreenOn);
}
@@ -7411,7 +7230,7 @@
++mPeriodicScanToken, 0), mNoNetworksPeriodicScan);
}
- mDisconnectedTimeStamp = System.currentTimeMillis();
+ mDisconnectedTimeStamp = mClock.getWallClockMillis();
}
@Override
public boolean processMessage(Message message) {
@@ -7445,7 +7264,7 @@
mOperationalMode = message.arg1;
mWifiConfigManager.disableAllNetworksNative();
if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
- mWifiP2pChannel.sendMessage(CMD_DISABLE_P2P_REQ);
+ p2pSendMessage(CMD_DISABLE_P2P_REQ);
setWifiState(WIFI_STATE_DISABLED);
}
transitionTo(mScanModeState);
@@ -7460,10 +7279,10 @@
break;
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd("SUPPLICANT_STATE_CHANGE_EVENT state=" + stateChangeResult.state +
" -> state= " + WifiInfo.getDetailedStateOf(stateChangeResult.state)
- + " debouncing=" + linkDebouncing);
+ + " debouncing=" + isLinkDebouncing());
}
setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
/* ConnectModeState does the rest of the handling */
@@ -7489,7 +7308,7 @@
defaultInterval);
mWifiNative.setScanInterval((int) scanIntervalMs/1000);
} else if (mWifiConfigManager.getSavedNetworks().size() == 0) {
- if (DBG) log("Turn on scanning after p2p disconnected");
+ if (mVerboseLoggingEnabled) log("Turn on scanning after p2p disconnected");
sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
++mPeriodicScanToken, 0), mNoNetworksPeriodicScan);
}
@@ -7560,7 +7379,9 @@
mSourceMessage = null;
transitionTo(mDisconnectedState);
} else {
- if (DBG) log("Ignore unspecified fail event during WPS connection");
+ if (mVerboseLoggingEnabled) {
+ log("Ignore unspecified fail event during WPS connection");
+ }
}
break;
case WifiMonitor.WPS_TIMEOUT_EVENT:
@@ -7602,18 +7423,20 @@
messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
return HANDLED;
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
- if (DBG) log("Network connection lost");
+ if (mVerboseLoggingEnabled) log("Network connection lost");
handleNetworkDisconnect();
break;
case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
- if (DBG) log("Ignore Assoc reject event during WPS Connection");
+ if (mVerboseLoggingEnabled) {
+ log("Ignore Assoc reject event during WPS Connection");
+ }
break;
case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
// Disregard auth failure events during WPS connection. The
// EAP sequence is retried several times, and there might be
// failures (especially for wps pin). We will get a WPS_XXX
// event at the end of the sequence anyway.
- if (DBG) log("Ignore auth failure during WPS connection");
+ if (mVerboseLoggingEnabled) log("Ignore auth failure during WPS connection");
break;
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
// Throw away supplicant state changes when WPS is running.
@@ -7652,30 +7475,41 @@
@Override
public void enter() {
final Message message = getCurrentMessage();
- if (message.what == CMD_START_AP) {
- WifiConfiguration config = (WifiConfiguration) message.obj;
-
- if (config == null) {
- /**
- * Configuration not provided in the command, fallback to use the current
- * configuration.
- */
- config = mWifiApConfigStore.getApConfiguration();
- } else {
- /* Update AP configuration. */
- mWifiApConfigStore.setApConfiguration(config);
- }
-
- checkAndSetConnectivityInstance();
- mSoftApManager = mFacade.makeSoftApManager(
- mContext, getHandler().getLooper(), mWifiNative, mNwService,
- mCm, mCountryCode.getCurrentCountryCode(),
- mWifiApConfigStore.getAllowed2GChannel(),
- new SoftApListener());
- mSoftApManager.start(config);
- } else {
+ if (message.what != CMD_START_AP) {
throw new RuntimeException("Illegal transition to SoftApState: " + message);
}
+
+ IApInterface apInterface = setupDriverForSoftAp();
+ if (apInterface == null) {
+ setWifiApState(WIFI_AP_STATE_FAILED,
+ WifiManager.SAP_START_FAILURE_GENERAL);
+ /**
+ * Transition to InitialState to reset the
+ * driver/HAL back to the initial state.
+ */
+ transitionTo(mInitialState);
+ return;
+ }
+
+ WifiConfiguration config = (WifiConfiguration) message.obj;
+ if (config == null) {
+ /**
+ * Configuration not provided in the command, fallback to use the current
+ * configuration.
+ */
+ config = mWifiApConfigStore.getApConfiguration();
+ } else {
+ /* Update AP configuration. */
+ mWifiApConfigStore.setApConfiguration(config);
+ }
+
+ checkAndSetConnectivityInstance();
+ mSoftApManager = mWifiInjector.makeSoftApManager(
+ mWifiNative, mNwService,
+ mCm, mCountryCode.getCurrentCountryCode(),
+ mWifiApConfigStore.getAllowed2GChannel(),
+ new SoftApListener(), apInterface);
+ mSoftApManager.start(config);
}
@Override
@@ -7745,7 +7579,7 @@
/**
* @param wifiCredentialEventType WIFI_CREDENTIAL_SAVED or WIFI_CREDENTIAL_FORGOT
- * @param msg Must have a WifiConfiguration obj to succeed
+ * @param config Must have a WifiConfiguration object to succeed
*/
private void broadcastWifiCredentialChanged(int wifiCredentialEventType,
WifiConfiguration config) {
@@ -7808,54 +7642,6 @@
return sb.toString();
}
- private static byte[] concat(byte[] array1, byte[] array2, byte[] array3) {
-
- int len = array1.length + array2.length + array3.length;
-
- if (array1.length != 0) {
- len++; /* add another byte for size */
- }
-
- if (array2.length != 0) {
- len++; /* add another byte for size */
- }
-
- if (array3.length != 0) {
- len++; /* add another byte for size */
- }
-
- byte[] result = new byte[len];
-
- int index = 0;
- if (array1.length != 0) {
- result[index] = (byte) (array1.length & 0xFF);
- index++;
- for (byte b : array1) {
- result[index] = b;
- index++;
- }
- }
-
- if (array2.length != 0) {
- result[index] = (byte) (array2.length & 0xFF);
- index++;
- for (byte b : array2) {
- result[index] = b;
- index++;
- }
- }
-
- if (array3.length != 0) {
- result[index] = (byte) (array3.length & 0xFF);
- index++;
- for (byte b : array3) {
- result[index] = b;
- index++;
- }
- }
- return result;
- }
-
private static byte[] concatHex(byte[] array1, byte[] array2) {
int len = array1.length + array2.length;
@@ -7901,13 +7687,13 @@
rand, android.util.Base64.NO_WRAP);
// Try USIM first for authentication.
- String tmResponse = tm.getIccAuthentication(tm.APPTYPE_USIM,
- tm.AUTHTYPE_EAP_SIM, base64Challenge);
+ String tmResponse = tm.getIccAuthentication(TelephonyManager.APPTYPE_USIM,
+ TelephonyManager.AUTHTYPE_EAP_SIM, base64Challenge);
if (tmResponse == null) {
/* Then, in case of failure, issue may be due to sim type, retry as a simple sim
*/
- tmResponse = tm.getIccAuthentication(tm.APPTYPE_SIM,
- tm.AUTHTYPE_EAP_SIM, base64Challenge);
+ tmResponse = tm.getIccAuthentication(TelephonyManager.APPTYPE_SIM,
+ TelephonyManager.AUTHTYPE_EAP_SIM, base64Challenge);
}
logv("Raw Response - " + tmResponse);
@@ -8003,8 +7789,8 @@
TelephonyManager tm = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
if (tm != null) {
- tmResponse = tm.getIccAuthentication(tm.APPTYPE_USIM,
- tm.AUTHTYPE_EAP_AKA, base64Challenge);
+ tmResponse = tm.getIccAuthentication(TelephonyManager.APPTYPE_USIM,
+ TelephonyManager.AUTHTYPE_EAP_AKA, base64Challenge);
logv("Raw Response - " + tmResponse);
} else {
loge("could not get telephony manager");
@@ -8144,7 +7930,7 @@
}
private static String getLinkPropertiesSummary(LinkProperties lp) {
- List<String> attributes = new ArrayList(6);
+ List<String> attributes = new ArrayList<>(6);
if (lp.hasIPv4Address()) {
attributes.add("v4");
}
@@ -8202,4 +7988,16 @@
}
return null;
}
+
+ private void p2pSendMessage(int what) {
+ if (mWifiP2pChannel != null) {
+ mWifiP2pChannel.sendMessage(what);
+ }
+ }
+
+ private void p2pSendMessage(int what, int arg1) {
+ if (mWifiP2pChannel != null) {
+ mWifiP2pChannel.sendMessage(what, arg1);
+ }
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiSupplicantControl.java
similarity index 72%
rename from service/java/com/android/server/wifi/WifiConfigStore.java
rename to service/java/com/android/server/wifi/WifiSupplicantControl.java
index b693e23..d05f3ef 100644
--- a/service/java/com/android/server/wifi/WifiConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiSupplicantControl.java
@@ -20,18 +20,12 @@
import android.net.IpConfiguration.IpAssignment;
import android.net.IpConfiguration.ProxySettings;
import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.Status;
import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiSsid;
import android.net.wifi.WpsInfo;
import android.net.wifi.WpsResult;
import android.os.FileObserver;
-import android.os.Process;
-import android.security.Credentials;
-import android.security.KeyChain;
-import android.security.KeyStore;
import android.text.TextUtils;
-import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Log;
import android.util.SparseArray;
@@ -49,32 +43,18 @@
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
-import java.security.PrivateKey;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
- * This class provides the API's to save/load/modify network configurations from a persistent
- * config database.
- * We use wpa_supplicant as our config database currently, but will be migrating to a different
- * one sometime in the future.
- * We use keystore for certificate/key management operations.
- *
- * NOTE: This class should only be used from WifiConfigManager!!!
+ * This class provides methods to send control commands to wpa_supplicant from WifiConfigManager.
+ * NOTE: This class should only be used from WifiConfigManager!
*/
-public class WifiConfigStore {
-
- public static final String TAG = "WifiConfigStore";
+public class WifiSupplicantControl {
// This is the only variable whose contents will not be interpreted by wpa_supplicant. We use it
// to store metadata that allows us to correlate a wpa_supplicant.conf entry with additional
// information about the same network stored in other files. The metadata is stored as a
@@ -85,40 +65,23 @@
public static final String ID_STRING_KEY_CONFIG_KEY = "configKey";
public static final String SUPPLICANT_CONFIG_FILE = "/data/misc/wifi/wpa_supplicant.conf";
public static final String SUPPLICANT_CONFIG_FILE_BACKUP = SUPPLICANT_CONFIG_FILE + ".tmp";
-
// Value stored by supplicant to requirePMF
public static final int STORED_VALUE_FOR_REQUIRE_PMF = 2;
-
- private static final boolean DBG = true;
- private static boolean VDBG = false;
-
+ private static final String TAG = "WifiSupplicantControl";
private final LocalLog mLocalLog;
private final WpaConfigFileObserver mFileObserver;
private final Context mContext;
private final WifiNative mWifiNative;
- private final KeyStore mKeyStore;
- private final boolean mShowNetworks;
- private final HashSet<String> mBssidBlacklist = new HashSet<String>();
- private final BackupManagerProxy mBackupManagerProxy;
+ private boolean mVerboseLoggingEnabled = false;
- WifiConfigStore(Context context, WifiNative wifiNative, KeyStore keyStore, LocalLog localLog,
- boolean showNetworks, boolean verboseDebug) {
+ WifiSupplicantControl(Context context, WifiNative wifiNative, LocalLog localLog) {
mContext = context;
mWifiNative = wifiNative;
- mKeyStore = keyStore;
- mShowNetworks = showNetworks;
- mBackupManagerProxy = new BackupManagerProxy();
- if (mShowNetworks) {
- mLocalLog = localLog;
- mFileObserver = new WpaConfigFileObserver();
- mFileObserver.startWatching();
- } else {
- mLocalLog = null;
- mFileObserver = null;
- }
- VDBG = verboseDebug;
+ mLocalLog = localLog;
+ mFileObserver = new WpaConfigFileObserver();
+ mFileObserver.startWatching();
}
private static String removeDoubleQuotes(String string) {
@@ -136,7 +99,9 @@
* Also transform the internal string format that uses _ (for bewildering
* reasons) into a wpa_supplicant adjusted value, that uses - as a separator
* (most of the time at least...).
- * @param set a bit set with a one for each corresponding string to be included from strings.
+ *
+ * @param set a bit set with a one for each corresponding string to be included from
+ * strings.
* @param strings the set of string literals to concatenate strinfs from.
* @return A wpa_supplicant formatted value.
*/
@@ -146,8 +111,10 @@
/**
* Same as makeString with an exclusion parameter.
- * @param set a bit set with a one for each corresponding string to be included from strings.
- * @param strings the set of string literals to concatenate strinfs from.
+ *
+ * @param set a bit set with a one for each corresponding string to be included from
+ * strings.
+ * @param strings the set of string literals to concatenate strinfs from.
* @param exception literal string to be excluded from the _ to - transformation.
* @return A wpa_supplicant formatted value.
*/
@@ -159,9 +126,7 @@
BitSet trimmedSet = set.get(0, strings.length);
List<String> valueSet = new ArrayList<>();
- for (int bit = trimmedSet.nextSetBit(0);
- bit >= 0;
- bit = trimmedSet.nextSetBit(bit+1)) {
+ for (int bit = trimmedSet.nextSetBit(0); bit >= 0; bit = trimmedSet.nextSetBit(bit + 1)) {
String currentName = strings[bit];
if (exception != null && currentName.equals(exception)) {
valueSet.add(currentName);
@@ -184,34 +149,6 @@
return Utils.toHex(removeDoubleQuotes(str).getBytes(StandardCharsets.UTF_8));
}
- // Certificate and private key management for EnterpriseConfig
- private static boolean needsKeyStore(WifiEnterpriseConfig config) {
- return (!(config.getClientCertificate() == null && config.getCaCertificate() == null));
- }
-
- private static boolean isHardwareBackedKey(PrivateKey key) {
- return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm());
- }
-
- private static boolean hasHardwareBackedKey(Certificate certificate) {
- return KeyChain.isBoundKeyAlgorithm(certificate.getPublicKey().getAlgorithm());
- }
-
- private static boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) {
- java.lang.String client = config.getClientCertificateAlias();
- if (!TextUtils.isEmpty(client)) {
- // a valid client certificate is configured
-
- // BUGBUG: keyStore.get() never returns certBytes; because it is not
- // taking WIFI_UID as a parameter. It always looks for certificate
- // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that
- // all certificates need software keystore until we get the get() API
- // fixed.
- return true;
- }
- return false;
- }
-
private int lookupString(String string, String[] strings) {
int size = strings.length;
@@ -251,7 +188,7 @@
if (config == null) {
return;
}
- if (VDBG) localLog("readNetworkVariables: " + config.networkId);
+ if (mVerboseLoggingEnabled) localLog("readNetworkVariables: " + config.networkId);
int netId = config.networkId;
if (netId < 0) {
return;
@@ -376,7 +313,7 @@
return lastPriority;
}
String[] lines = listStr.split("\n");
- if (mShowNetworks) {
+ if (mVerboseLoggingEnabled) {
localLog("loadNetworks: ");
for (String net : lines) {
localLog(net);
@@ -422,7 +359,7 @@
config.setIpAssignment(IpAssignment.DHCP);
config.setProxySettings(ProxySettings.NONE);
if (!WifiServiceImpl.isValid(config)) {
- if (mShowNetworks) {
+ if (mVerboseLoggingEnabled) {
localLog("Ignoring network " + config.networkId + " because configuration "
+ "loaded from wpa_supplicant.conf is not valid.");
}
@@ -442,7 +379,7 @@
final WifiConfiguration duplicateConfig = configs.put(configKey, config);
if (duplicateConfig != null) {
// The network is already known. Overwrite the duplicate entry.
- if (mShowNetworks) {
+ if (mVerboseLoggingEnabled) {
localLog("Replacing duplicate network " + duplicateConfig.networkId
+ " with " + config.networkId + ".");
}
@@ -458,132 +395,8 @@
}
/**
- * Install keys for given enterprise network.
- *
- * @param existingConfig Existing config corresponding to the network already stored in our
- * database. This maybe null if it's a new network.
- * @param config Config corresponding to the network.
- * @return true if successful, false otherwise.
- */
- private boolean installKeys(WifiEnterpriseConfig existingConfig, WifiEnterpriseConfig config,
- String name) {
- boolean ret = true;
- String privKeyName = Credentials.USER_PRIVATE_KEY + name;
- String userCertName = Credentials.USER_CERTIFICATE + name;
- if (config.getClientCertificate() != null) {
- byte[] privKeyData = config.getClientPrivateKey().getEncoded();
- if (DBG) {
- if (isHardwareBackedKey(config.getClientPrivateKey())) {
- Log.d(TAG, "importing keys " + name + " in hardware backed store");
- } else {
- Log.d(TAG, "importing keys " + name + " in software backed store");
- }
- }
- ret = mKeyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID,
- KeyStore.FLAG_NONE);
-
- if (!ret) {
- return ret;
- }
-
- ret = putCertInKeyStore(userCertName, config.getClientCertificate());
- if (!ret) {
- // Remove private key installed
- mKeyStore.delete(privKeyName, Process.WIFI_UID);
- return ret;
- }
- }
-
- X509Certificate[] caCertificates = config.getCaCertificates();
- Set<String> oldCaCertificatesToRemove = new ArraySet<String>();
- if (existingConfig != null && existingConfig.getCaCertificateAliases() != null) {
- oldCaCertificatesToRemove.addAll(
- Arrays.asList(existingConfig.getCaCertificateAliases()));
- }
- List<String> caCertificateAliases = null;
- if (caCertificates != null) {
- caCertificateAliases = new ArrayList<String>();
- for (int i = 0; i < caCertificates.length; i++) {
- String alias = caCertificates.length == 1 ? name
- : String.format("%s_%d", name, i);
-
- oldCaCertificatesToRemove.remove(alias);
- ret = putCertInKeyStore(Credentials.CA_CERTIFICATE + alias, caCertificates[i]);
- if (!ret) {
- // Remove client key+cert
- if (config.getClientCertificate() != null) {
- mKeyStore.delete(privKeyName, Process.WIFI_UID);
- mKeyStore.delete(userCertName, Process.WIFI_UID);
- }
- // Remove added CA certs.
- for (String addedAlias : caCertificateAliases) {
- mKeyStore.delete(Credentials.CA_CERTIFICATE + addedAlias, Process.WIFI_UID);
- }
- return ret;
- } else {
- caCertificateAliases.add(alias);
- }
- }
- }
- // Remove old CA certs.
- for (String oldAlias : oldCaCertificatesToRemove) {
- mKeyStore.delete(Credentials.CA_CERTIFICATE + oldAlias, Process.WIFI_UID);
- }
- // Set alias names
- if (config.getClientCertificate() != null) {
- config.setClientCertificateAlias(name);
- config.resetClientKeyEntry();
- }
-
- if (caCertificates != null) {
- config.setCaCertificateAliases(
- caCertificateAliases.toArray(new String[caCertificateAliases.size()]));
- config.resetCaCertificate();
- }
- return ret;
- }
-
- private boolean putCertInKeyStore(String name, Certificate cert) {
- try {
- byte[] certData = Credentials.convertToPem(cert);
- if (DBG) Log.d(TAG, "putting certificate " + name + " in keystore");
- return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE);
-
- } catch (IOException e1) {
- return false;
- } catch (CertificateException e2) {
- return false;
- }
- }
-
- /**
- * Remove enterprise keys from the network config.
- *
- * @param config Config corresponding to the network.
- */
- private void removeKeys(WifiEnterpriseConfig config) {
- String client = config.getClientCertificateAlias();
- // a valid client certificate is configured
- if (!TextUtils.isEmpty(client)) {
- if (DBG) Log.d(TAG, "removing client private key and user cert");
- mKeyStore.delete(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
- mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
- }
-
- String[] aliases = config.getCaCertificateAliases();
- // a valid ca certificate is configured
- if (aliases != null) {
- for (String ca : aliases) {
- if (!TextUtils.isEmpty(ca)) {
- if (DBG) Log.d(TAG, "removing CA cert: " + ca);
- mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
- }
- }
- }
- }
-
- /**
* Update the network metadata info stored in wpa_supplicant network extra field.
+ *
* @param config Config corresponding to the network.
* @return true if successful, false otherwise.
*/
@@ -612,7 +425,7 @@
if (config == null) {
return false;
}
- if (VDBG) localLog("saveNetwork: " + netId);
+ if (mVerboseLoggingEnabled) localLog("saveNetwork: " + netId);
if (config.SSID != null && !mWifiNative.setNetworkVariable(
netId,
WifiConfiguration.ssidVarName,
@@ -750,53 +563,31 @@
}
/**
- * Update/Install keys for given enterprise network.
+ * Save an enterprise network configuration to wpa_supplicant.
*
- * @param config Config corresponding to the network.
- * @param existingConfig Existing config corresponding to the network already stored in our
- * database. This maybe null if it's a new network.
+ * @param config Config corresponding to the network.
* @return true if successful, false otherwise.
*/
- private boolean updateNetworkKeys(WifiConfiguration config, WifiConfiguration existingConfig) {
- WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
- if (needsKeyStore(enterpriseConfig)) {
- try {
- /* config passed may include only fields being updated.
- * In order to generate the key id, fetch uninitialized
- * fields from the currently tracked configuration
- */
- String keyId = config.getKeyIdForCredentials(existingConfig);
-
- if (!installKeys(existingConfig != null
- ? existingConfig.enterpriseConfig : null, enterpriseConfig, keyId)) {
- loge(config.SSID + ": failed to install keys");
- return false;
- }
- } catch (IllegalStateException e) {
- loge(config.SSID + " invalid config for key installation: " + e.getMessage());
- return false;
- }
- }
- if (!enterpriseConfig.saveToSupplicant(
- new SupplicantSaver(config.networkId, config.SSID))) {
- removeKeys(enterpriseConfig);
+ public boolean saveEnterpriseConfiguration(WifiConfiguration config) {
+ if (config == null || config.enterpriseConfig == null) {
return false;
}
- return true;
+ if (mVerboseLoggingEnabled) localLog("saveEnterpriseConfiguration: " + config.networkId);
+ return config.enterpriseConfig.saveToSupplicant(
+ new WifiSupplicantControl.SupplicantSaver(config.networkId, config.SSID));
}
/**
* Add or update a network configuration to wpa_supplicant.
*
* @param config Config corresponding to the network.
- * @param existingConfig Existing config corresponding to the network saved in our database.
* @return true if successful, false otherwise.
*/
- public boolean addOrUpdateNetwork(WifiConfiguration config, WifiConfiguration existingConfig) {
+ public boolean addOrUpdateNetwork(WifiConfiguration config) {
if (config == null) {
return false;
}
- if (VDBG) localLog("addOrUpdateNetwork: " + config.networkId);
+ if (mVerboseLoggingEnabled) localLog("addOrUpdateNetwork: " + config.networkId);
int netId = config.networkId;
boolean newNetwork = false;
/*
@@ -823,12 +614,6 @@
}
return false;
}
- if (config.enterpriseConfig != null
- && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
- return updateNetworkKeys(config, existingConfig);
- }
- // Stage the backup of the SettingsProvider package which backs this up
- mBackupManagerProxy.notifyDataChanged();
return true;
}
@@ -842,17 +627,11 @@
if (config == null) {
return false;
}
- if (VDBG) localLog("removeNetwork: " + config.networkId);
+ if (mVerboseLoggingEnabled) localLog("removeNetwork: " + config.networkId);
if (!mWifiNative.removeNetwork(config.networkId)) {
loge("Remove network in wpa_supplicant failed on " + config.networkId);
return false;
}
- // Remove any associated keys
- if (config.enterpriseConfig != null) {
- removeKeys(config.enterpriseConfig);
- }
- // Stage the backup of the SettingsProvider package which backs this up
- mBackupManagerProxy.notifyDataChanged();
return true;
}
@@ -862,17 +641,15 @@
* @param config Config corresponding to the network.
* @return true if successful, false otherwise.
*/
- public boolean selectNetwork(WifiConfiguration config, Collection<WifiConfiguration> configs) {
+ public boolean selectNetwork(WifiConfiguration config) {
if (config == null) {
return false;
}
- if (VDBG) localLog("selectNetwork: " + config.networkId);
+ if (mVerboseLoggingEnabled) localLog("selectNetwork: " + config.networkId);
if (!mWifiNative.selectNetwork(config.networkId)) {
loge("Select network in wpa_supplicant failed on " + config.networkId);
return false;
}
- config.status = Status.ENABLED;
- markAllNetworksDisabledExcept(config.networkId, configs);
return true;
}
@@ -886,12 +663,11 @@
if (config == null) {
return false;
}
- if (VDBG) localLog("disableNetwork: " + config.networkId);
+ if (mVerboseLoggingEnabled) localLog("disableNetwork: " + config.networkId);
if (!mWifiNative.disableNetwork(config.networkId)) {
loge("Disable network in wpa_supplicant failed on " + config.networkId);
return false;
}
- config.status = Status.DISABLED;
return true;
}
@@ -905,7 +681,7 @@
if (config == null) {
return false;
}
- if (VDBG) localLog("setNetworkPriority: " + config.networkId);
+ if (mVerboseLoggingEnabled) localLog("setNetworkPriority: " + config.networkId);
if (!mWifiNative.setNetworkVariable(config.networkId,
WifiConfiguration.priorityVarName, Integer.toString(priority))) {
loge("Set priority of network in wpa_supplicant failed on " + config.networkId);
@@ -925,7 +701,7 @@
if (config == null) {
return false;
}
- if (VDBG) localLog("setNetworkSSID: " + config.networkId);
+ if (mVerboseLoggingEnabled) localLog("setNetworkSSID: " + config.networkId);
if (!mWifiNative.setNetworkVariable(config.networkId, WifiConfiguration.ssidVarName,
encodeSSID(ssid))) {
loge("Set SSID of network in wpa_supplicant failed on " + config.networkId);
@@ -949,7 +725,7 @@
&& config.SSID == null)) {
return false;
}
- if (VDBG) localLog("setNetworkBSSID: " + config.networkId);
+ if (mVerboseLoggingEnabled) localLog("setNetworkBSSID: " + config.networkId);
if (!mWifiNative.setNetworkVariable(config.networkId, WifiConfiguration.bssidVarName,
bssid)) {
loge("Set BSSID of network in wpa_supplicant failed on " + config.networkId);
@@ -960,6 +736,25 @@
}
/**
+ * Get BSSID for a network in wpa_supplicant.
+ *
+ * @param config Config corresponding to the network.
+ * @return BSSID for the network, if it exists, null otherwise.
+ */
+ public String getNetworkBSSID(WifiConfiguration config) {
+ // Sanity check the config is valid
+ if (config == null
+ || (config.networkId == WifiConfiguration.INVALID_NETWORK_ID
+ && config.SSID == null)) {
+ return null;
+ }
+ if (mVerboseLoggingEnabled) localLog("getNetworkBSSID: " + config.networkId);
+ String bssid =
+ mWifiNative.getNetworkVariable(config.networkId, WifiConfiguration.bssidVarName);
+ return (TextUtils.isEmpty(bssid) ? null : bssid);
+ }
+
+ /**
* Enable/Disable HS20 parameter in wpa_supplicant.
*
* @param enable Enable/Disable the parameter.
@@ -975,7 +770,7 @@
* @return true if successful, false otherwise.
*/
public boolean disableAllNetworks(Collection<WifiConfiguration> configs) {
- if (VDBG) localLog("disableAllNetworks");
+ if (mVerboseLoggingEnabled) localLog("disableAllNetworks");
boolean networkDisabled = false;
for (WifiConfiguration enabled : configs) {
if (disableNetwork(enabled)) {
@@ -1006,16 +801,16 @@
reader = new BufferedReader(new FileReader(SUPPLICANT_CONFIG_FILE));
result = readNetworkVariablesFromReader(reader, key);
} catch (FileNotFoundException e) {
- if (VDBG) loge("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e);
+ if (mVerboseLoggingEnabled) loge("Could not open " + SUPPLICANT_CONFIG_FILE + ", " + e);
} catch (IOException e) {
- if (VDBG) loge("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e);
+ if (mVerboseLoggingEnabled) loge("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e);
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
- if (VDBG) {
+ if (mVerboseLoggingEnabled) {
loge("Could not close reader for " + SUPPLICANT_CONFIG_FILE + ", " + e);
}
}
@@ -1028,13 +823,13 @@
* readNetworkVariablesFromSupplicantFile() for testing.
*
* @param reader The reader to read the network variables from.
- * @param key The parameter to be parsed.
+ * @param key The parameter to be parsed.
* @return Map of corresponding configKey to the value of the param requested.
*/
public Map<String, String> readNetworkVariablesFromReader(BufferedReader reader, String key)
throws IOException {
Map<String, String> result = new HashMap<>();
- if (VDBG) localLog("readNetworkVariablesFromReader key=" + key);
+ if (mVerboseLoggingEnabled) localLog("readNetworkVariablesFromReader key=" + key);
boolean found = false;
String configKey = null;
String value = null;
@@ -1054,20 +849,19 @@
try {
// Trim the quotes wrapping the id_str value.
final String encodedExtras = trimmedLine.substring(
- 8, trimmedLine.length() -1);
+ 8, trimmedLine.length() - 1);
final JSONObject json =
new JSONObject(URLDecoder.decode(encodedExtras, "UTF-8"));
- if (json.has(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY)) {
+ if (json.has(ID_STRING_KEY_CONFIG_KEY)) {
final Object configKeyFromJson =
- json.get(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY);
+ json.get(ID_STRING_KEY_CONFIG_KEY);
if (configKeyFromJson instanceof String) {
configKey = (String) configKeyFromJson;
}
}
} catch (JSONException e) {
- if (VDBG) {
- loge("Could not get "+ WifiConfigStore.ID_STRING_KEY_CONFIG_KEY
- + ", " + e);
+ if (mVerboseLoggingEnabled) {
+ loge("Could not get " + ID_STRING_KEY_CONFIG_KEY + ", " + e);
}
}
}
@@ -1088,7 +882,7 @@
* @param configs List of all the networks.
*/
public void resetSimNetworks(Collection<WifiConfiguration> configs) {
- if (VDBG) localLog("resetSimNetworks");
+ if (mVerboseLoggingEnabled) localLog("resetSimNetworks");
for (WifiConfiguration config : configs) {
if (TelephonyUtil.isSimConfig(config)) {
String currentIdentity = TelephonyUtil.getSimIdentity(mContext,
@@ -1115,11 +909,10 @@
}
/**
- * Clear BSSID blacklist in wpa_supplicant.
+ * Clear BSSID blacklist in wpa_supplicant & HAL.
*/
public void clearBssidBlacklist() {
- if (VDBG) localLog("clearBlacklist");
- mBssidBlacklist.clear();
+ if (mVerboseLoggingEnabled) localLog("clearBlacklist");
mWifiNative.clearBlacklist();
mWifiNative.setBssidBlacklist(null);
}
@@ -1127,44 +920,15 @@
/**
* Add a BSSID to the blacklist.
*
- * @param bssid bssid to be added.
+ * @param bssid to be added.
+ * @param bssidList entire BSSID list.
*/
- public void blackListBssid(String bssid) {
- if (bssid == null) {
- return;
- }
- if (VDBG) localLog("blackListBssid: " + bssid);
- mBssidBlacklist.add(bssid);
+ public void blackListBssid(String bssid, String[] bssidList) {
+ if (mVerboseLoggingEnabled) localLog("blackListBssid: " + bssid);
// Blacklist at wpa_supplicant
mWifiNative.addToBlacklist(bssid);
// Blacklist at firmware
- String[] list = mBssidBlacklist.toArray(new String[mBssidBlacklist.size()]);
- mWifiNative.setBssidBlacklist(list);
- }
-
- /**
- * Checks if the provided bssid is blacklisted or not.
- *
- * @param bssid bssid to be checked.
- * @return true if present, false otherwise.
- */
- public boolean isBssidBlacklisted(String bssid) {
- return mBssidBlacklist.contains(bssid);
- }
-
- /* Mark all networks except specified netId as disabled */
- private void markAllNetworksDisabledExcept(int netId, Collection<WifiConfiguration> configs) {
- for (WifiConfiguration config : configs) {
- if (config != null && config.networkId != netId) {
- if (config.status != Status.DISABLED) {
- config.status = Status.DISABLED;
- }
- }
- }
- }
-
- private void markAllNetworksDisabled(Collection<WifiConfiguration> configs) {
- markAllNetworksDisabledExcept(WifiConfiguration.INVALID_NETWORK_ID, configs);
+ mWifiNative.setBssidBlacklist(bssidList);
}
/**
@@ -1174,12 +938,9 @@
* @param config WPS configuration
* @return Wps result containing status and pin
*/
- public WpsResult startWpsWithPinFromAccessPoint(WpsInfo config,
- Collection<WifiConfiguration> configs) {
+ public WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) {
WpsResult result = new WpsResult();
if (mWifiNative.startWpsRegistrar(config.BSSID, config.pin)) {
- /* WPS leaves all networks disabled */
- markAllNetworksDisabled(configs);
result.status = WpsResult.Status.SUCCESS;
} else {
loge("Failed to start WPS pin method configuration");
@@ -1194,13 +955,10 @@
*
* @return WpsResult indicating status and pin
*/
- public WpsResult startWpsWithPinFromDevice(WpsInfo config,
- Collection<WifiConfiguration> configs) {
+ public WpsResult startWpsWithPinFromDevice(WpsInfo config) {
WpsResult result = new WpsResult();
result.pin = mWifiNative.startWpsPinDisplay(config.BSSID);
- /* WPS leaves all networks disabled */
if (!TextUtils.isEmpty(result.pin)) {
- markAllNetworksDisabled(configs);
result.status = WpsResult.Status.SUCCESS;
} else {
loge("Failed to start WPS pin method configuration");
@@ -1215,12 +973,9 @@
* @param config WPS configuration
* @return WpsResult indicating status and pin
*/
- public WpsResult startWpsPbc(WpsInfo config,
- Collection<WifiConfiguration> configs) {
+ public WpsResult startWpsPbc(WpsInfo config) {
WpsResult result = new WpsResult();
if (mWifiNative.startWpsPbc(config.BSSID)) {
- /* WPS leaves all networks disabled */
- markAllNetworksDisabled(configs);
result.status = WpsResult.Status.SUCCESS;
} else {
loge("Failed to start WPS push button configuration");
@@ -1229,19 +984,19 @@
return result;
}
- protected void logd(String s) {
+ private void logd(String s) {
Log.d(TAG, s);
}
- protected void logi(String s) {
+ private void logi(String s) {
Log.i(TAG, s);
}
- protected void loge(String s) {
+ private void loge(String s) {
loge(s, false);
}
- protected void loge(String s, boolean stack) {
+ private void loge(String s, boolean stack) {
if (stack) {
Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
+ " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
@@ -1252,7 +1007,7 @@
}
}
- protected void log(String s) {
+ private void log(String s) {
Log.d(TAG, s);
}
@@ -1267,6 +1022,13 @@
Log.d(TAG, s);
}
+ /**
+ * Enable verbose logging.
+ */
+ public void enableVerboseLogging(boolean verbose) {
+ mVerboseLoggingEnabled = verbose;
+ }
+
private class SupplicantSaver implements WifiEnterpriseConfig.SupplicantSaver {
private final int mNetId;
private final String mSetterSSID;
@@ -1335,7 +1097,7 @@
}
}
- // TODO(rpius): Remove this.
+ // TODO(rpius): Remove this (see b/27377614).
private class WpaConfigFileObserver extends FileObserver {
WpaConfigFileObserver() {
@@ -1346,7 +1108,9 @@
public void onEvent(int event, String path) {
if (event == CLOSE_WRITE) {
File file = new File(SUPPLICANT_CONFIG_FILE);
- if (VDBG) localLog("wpa_supplicant.conf changed; new size = " + file.length());
+ if (mVerboseLoggingEnabled) {
+ localLog("wpa_supplicant.conf changed; new size = " + file.length());
+ }
}
}
}
diff --git a/service/java/com/android/server/wifi/WifiTrafficPoller.java b/service/java/com/android/server/wifi/WifiTrafficPoller.java
index 336e0d7..d05353b 100644
--- a/service/java/com/android/server/wifi/WifiTrafficPoller.java
+++ b/service/java/com/android/server/wifi/WifiTrafficPoller.java
@@ -41,9 +41,7 @@
/* Polls for traffic stats and notifies the clients */
final class WifiTrafficPoller {
- private boolean DBG = false;
- private boolean VDBG = false;
-
+ private static final boolean DBG = false;
private static final String TAG = "WifiTrafficPoller";
/**
* Interval in milliseconds between polling for traffic
@@ -71,6 +69,8 @@
private NetworkInfo mNetworkInfo;
private final String mInterface;
+ private boolean mVerboseLoggingEnabled = false;
+
WifiTrafficPoller(Context context, Looper looper, String iface) {
mInterface = iface;
mTrafficHandler = new TrafficHandler(looper);
@@ -110,10 +110,10 @@
}
void enableVerboseLogging(int verbose) {
- if (verbose > 0 ) {
- DBG = true;
+ if (verbose > 0) {
+ mVerboseLoggingEnabled = true;
} else {
- DBG = false;
+ mVerboseLoggingEnabled = false;
}
}
@@ -126,8 +126,8 @@
switch (msg.what) {
case ENABLE_TRAFFIC_STATS_POLL:
mEnableTrafficStatsPoll = (msg.arg1 == 1);
- if (DBG) {
- Log.e(TAG, "ENABLE_TRAFFIC_STATS_POLL "
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "ENABLE_TRAFFIC_STATS_POLL "
+ mEnableTrafficStatsPoll + " Token "
+ Integer.toString(mTrafficStatsPollToken));
}
@@ -139,8 +139,8 @@
}
break;
case TRAFFIC_STATS_POLL:
- if (VDBG) {
- Log.e(TAG, "TRAFFIC_STATS_POLL "
+ if (DBG) {
+ Log.d(TAG, "TRAFFIC_STATS_POLL "
+ mEnableTrafficStatsPoll + " Token "
+ Integer.toString(mTrafficStatsPollToken)
+ " num clients " + mClients.size());
@@ -153,8 +153,8 @@
break;
case ADD_CLIENT:
mClients.add((Messenger) msg.obj);
- if (DBG) {
- Log.e(TAG, "ADD_CLIENT: "
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "ADD_CLIENT: "
+ Integer.toString(mClients.size()));
}
break;
@@ -187,8 +187,8 @@
mTxPkts = TrafficStats.getTxPackets(mInterface);
mRxPkts = TrafficStats.getRxPackets(mInterface);
- if (VDBG) {
- Log.e(TAG, " packet count Tx="
+ if (DBG) {
+ Log.d(TAG, " packet count Tx="
+ Long.toString(mTxPkts)
+ " Rx="
+ Long.toString(mRxPkts));
@@ -206,7 +206,7 @@
if (dataActivity != mDataActivity && mScreenOn.get()) {
mDataActivity = dataActivity;
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
Log.e(TAG, "notifying of data activity "
+ Integer.toString(mDataActivity));
}
diff --git a/service/java/com/android/server/wifi/hotspot2/ANQPData.java b/service/java/com/android/server/wifi/hotspot2/ANQPData.java
index 164ea20..f3d67cc 100644
--- a/service/java/com/android/server/wifi/hotspot2/ANQPData.java
+++ b/service/java/com/android/server/wifi/hotspot2/ANQPData.java
@@ -46,7 +46,7 @@
mClock = clock;
mNetwork = network;
mANQPElements = anqpElements != null ? new HashMap<>(anqpElements) : null;
- mCtime = mClock.currentTimeMillis();
+ mCtime = mClock.getWallClockMillis();
mRetry = 0;
if (anqpElements == null) {
mExpiry = mCtime + ANQP_HOLDOFF_TIME;
@@ -63,7 +63,7 @@
mClock = clock;
mNetwork = network;
mANQPElements = null;
- mCtime = mClock.currentTimeMillis();
+ mCtime = mClock.getWallClockMillis();
if (existing == null) {
mRetry = 0;
mExpiry = mCtime + ANQP_HOLDOFF_TIME;
@@ -100,7 +100,7 @@
}
public boolean expired() {
- return expired(mClock.currentTimeMillis());
+ return expired(mClock.getWallClockMillis());
}
public boolean expired(long at) {
@@ -120,7 +120,7 @@
protected boolean isValid(NetworkDetail nwk) {
return mANQPElements != null &&
nwk.getAnqpDomainID() == mNetwork.getAnqpDomainID() &&
- mExpiry > mClock.currentTimeMillis();
+ mExpiry > mClock.getWallClockMillis();
}
private int getRetry() {
@@ -136,7 +136,7 @@
else {
sb.append(", ").append(mANQPElements.size()).append(" elements, ");
}
- long now = mClock.currentTimeMillis();
+ long now = mClock.getWallClockMillis();
sb.append(Utils.toHMS(now-mCtime)).append(" old, expires in ").
append(Utils.toHMS(mExpiry-now)).append(' ');
if (brief) {
diff --git a/service/java/com/android/server/wifi/hotspot2/AnqpCache.java b/service/java/com/android/server/wifi/hotspot2/AnqpCache.java
index a6cd42e..c655427 100644
--- a/service/java/com/android/server/wifi/hotspot2/AnqpCache.java
+++ b/service/java/com/android/server/wifi/hotspot2/AnqpCache.java
@@ -25,7 +25,7 @@
public AnqpCache(Clock clock) {
mClock = clock;
mANQPCache = new HashMap<>();
- mLastSweep = mClock.currentTimeMillis();
+ mLastSweep = mClock.getWallClockMillis();
}
private static class CacheKey {
@@ -172,7 +172,7 @@
public void clear(boolean all, boolean debug) {
if (DBG) Log.d(Utils.hs2LogTag(getClass()), "Clearing ANQP cache: all: " + all);
- long now = mClock.currentTimeMillis();
+ long now = mClock.getWallClockMillis();
synchronized (mANQPCache) {
if (all) {
mANQPCache.clear();
@@ -197,7 +197,7 @@
}
public void dump(PrintWriter out) {
- out.println("Last sweep " + Utils.toHMS(mClock.currentTimeMillis() - mLastSweep) + " ago.");
+ out.println("Last sweep " + Utils.toHMS(mClock.getWallClockMillis() - mLastSweep) + " ago.");
for (ANQPData anqpData : mANQPCache.values()) {
out.println(anqpData.toString(false));
}
diff --git a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
index 402c0a8..5abf164 100644
--- a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
+++ b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
@@ -24,9 +24,7 @@
public class NetworkDetail {
- //turn off when SHIP
- private static final boolean DBG = true;
- private static final boolean VDBG = false;
+ private static final boolean DBG = false;
private static final String TAG = "NetworkDetail:";
@@ -290,7 +288,7 @@
mMaxRate = 0;
Log.w("WifiMode", mSSID + ", Invalid SupportedRates!!!");
}
- if (VDBG) {
+ if (DBG) {
Log.d(TAG, mSSID + "ChannelWidth is: " + mChannelWidth + " PrimaryFreq: " + mPrimaryFreq
+ " mCenterfreq0: " + mCenterfreq0 + " mCenterfreq1: " + mCenterfreq1
+ (extendedCapabilities.is80211McRTTResponder ? "Support RTT reponder"
diff --git a/service/java/com/android/server/wifi/hotspot2/SupplicantBridge.java b/service/java/com/android/server/wifi/hotspot2/SupplicantBridge.java
index af2befb..9c35645 100644
--- a/service/java/com/android/server/wifi/hotspot2/SupplicantBridge.java
+++ b/service/java/com/android/server/wifi/hotspot2/SupplicantBridge.java
@@ -179,18 +179,25 @@
}
if (scanDetail == null) {
+ // Icon queries are not held on the request map, so a null scanDetail is very likely
+ // the result of an Icon query. Notify the OSU app if the query was unsuccessful,
+ // else bail out.
if (!success) {
mCallbacks.notifyIconFailed(bssid);
}
return;
+ } else if (!success) {
+ // If there is an associated ScanDetail, notify of a failed regular ANQP query.
+ mCallbacks.notifyANQPResponse(scanDetail, null);
+ return;
}
String bssData = mSupplicantHook.scanResult(scanDetail.getBSSIDString());
+ Map<Constants.ANQPElementType, ANQPElement> elements = null;
try {
- Map<Constants.ANQPElementType, ANQPElement> elements = parseWPSData(bssData);
- Log.d(Utils.hs2LogTag(getClass()), String.format("%s ANQP response for %012x: %s",
- success ? "successful" : "failed", bssid, elements));
- mCallbacks.notifyANQPResponse(scanDetail, success ? elements : null);
+ elements = parseWPSData(bssData);
+ Log.d(Utils.hs2LogTag(getClass()),
+ String.format("Successful ANQP response for %012x: %s", bssid, elements));
}
catch (IOException ioe) {
Log.e(Utils.hs2LogTag(getClass()), "Failed to parse ANQP: " +
@@ -200,7 +207,7 @@
Log.e(Utils.hs2LogTag(getClass()), "Failed to parse ANQP: " +
rte.toString() + ": " + bssData, rte);
}
- mCallbacks.notifyANQPResponse(scanDetail, null);
+ mCallbacks.notifyANQPResponse(scanDetail, elements);
}
private static String escapeSSID(NetworkDetail networkDetail) {
diff --git a/service/java/com/android/server/wifi/nan/WifiNanClientState.java b/service/java/com/android/server/wifi/nan/WifiNanClientState.java
index 0707270..74fa71d 100644
--- a/service/java/com/android/server/wifi/nan/WifiNanClientState.java
+++ b/service/java/com/android/server/wifi/nan/WifiNanClientState.java
@@ -16,17 +16,26 @@
package com.android.server.wifi.nan;
+import android.net.wifi.RttManager;
import android.net.wifi.nan.ConfigRequest;
-import android.net.wifi.nan.IWifiNanEventListener;
-import android.net.wifi.nan.IWifiNanSessionListener;
-import android.net.wifi.nan.WifiNanEventListener;
+import android.net.wifi.nan.IWifiNanEventCallback;
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Arrays;
+/**
+ * Manages the service-side NAN state of an individual "client". A client
+ * corresponds to a single instantiation of the WifiNanManager - there could be
+ * multiple ones per UID/process (each of which is a separate client with its
+ * own session namespace). The client state is primarily: (1) callback (a
+ * singleton per client) through which NAN-wide events are called, and (2) a set
+ * of discovery sessions (publish and/or subscribe) which are created through
+ * this client and whose lifetime is tied to the lifetime of the client.
+ */
public class WifiNanClientState {
private static final String TAG = "WifiNanClientState";
private static final boolean DBG = false;
@@ -35,40 +44,56 @@
/* package */ static final int CLUSTER_CHANGE_EVENT_STARTED = 0;
/* package */ static final int CLUSTER_CHANGE_EVENT_JOINED = 1;
- private IWifiNanEventListener mListener;
- private int mEvents;
+ private IWifiNanEventCallback mCallback;
private final SparseArray<WifiNanSessionState> mSessions = new SparseArray<>();
- private int mUid;
+ private int mClientId;
private ConfigRequest mConfigRequest;
+ private int mUid;
- public WifiNanClientState(int uid, IWifiNanEventListener listener, int events) {
+ private static final byte[] ALL_ZERO_MAC = new byte[] {0, 0, 0, 0, 0, 0};
+ private byte[] mLastDiscoveryInterfaceMac = ALL_ZERO_MAC;
+
+ public WifiNanClientState(int clientId, int uid, IWifiNanEventCallback callback,
+ ConfigRequest configRequest) {
+ mClientId = clientId;
mUid = uid;
- mListener = listener;
- mEvents = events;
+ mCallback = callback;
+ mConfigRequest = configRequest;
}
+ /**
+ * Destroy the current client - corresponds to a disconnect() request from
+ * the client. Destroys all discovery sessions belonging to this client.
+ */
public void destroy() {
- mListener = null;
for (int i = 0; i < mSessions.size(); ++i) {
- mSessions.valueAt(i).destroy();
+ mSessions.valueAt(i).terminate();
}
mSessions.clear();
mConfigRequest = null;
}
- public void setConfigRequest(ConfigRequest configRequest) {
- mConfigRequest = configRequest;
- }
-
public ConfigRequest getConfigRequest() {
return mConfigRequest;
}
+ public int getClientId() {
+ return mClientId;
+ }
+
public int getUid() {
return mUid;
}
+ /**
+ * Searches the discovery sessions of this client and returns the one
+ * corresponding to the publish/subscribe ID. Used on callbacks from HAL to
+ * map callbacks to the correct discovery session.
+ *
+ * @param pubSubId The publish/subscribe match session ID.
+ * @return NAN session corresponding to the requested ID.
+ */
public WifiNanSessionState getNanSessionStateForPubSubId(int pubSubId) {
for (int i = 0; i < mSessions.size(); ++i) {
WifiNanSessionState session = mSessions.valueAt(i);
@@ -80,98 +105,159 @@
return null;
}
- public void createSession(int sessionId, IWifiNanSessionListener listener, int events) {
- WifiNanSessionState session = mSessions.get(sessionId);
- if (session != null) {
- Log.e(TAG, "createSession: sessionId already exists (replaced) - " + sessionId);
+ /**
+ * Add the session to the client database.
+ *
+ * @param session Session to be added.
+ */
+ public void addSession(WifiNanSessionState session) {
+ int sessionId = session.getSessionId();
+ if (mSessions.get(sessionId) != null) {
+ Log.w(TAG, "createSession: sessionId already exists (replaced) - " + sessionId);
}
- mSessions.put(sessionId, new WifiNanSessionState(sessionId, listener, events));
+ mSessions.put(sessionId, session);
}
- public void destroySession(int sessionId) {
- WifiNanSessionState session = mSessions.get(sessionId);
- if (session == null) {
- Log.e(TAG, "destroySession: sessionId doesn't exist - " + sessionId);
+ /**
+ * Remove the specified session from the client database - without doing a
+ * terminate on the session. The assumption is that it is already
+ * terminated.
+ *
+ * @param sessionId The session ID of the session to be removed.
+ */
+ public void removeSession(int sessionId) {
+ if (mSessions.get(sessionId) == null) {
+ Log.e(TAG, "removeSession: sessionId doesn't exist - " + sessionId);
return;
}
mSessions.delete(sessionId);
- session.destroy();
}
+ /**
+ * Destroy the discovery session: terminates discovery and frees up
+ * resources.
+ *
+ * @param sessionId The session ID of the session to be destroyed.
+ */
+ public void terminateSession(int sessionId) {
+ WifiNanSessionState session = mSessions.get(sessionId);
+ if (session == null) {
+ Log.e(TAG, "terminateSession: sessionId doesn't exist - " + sessionId);
+ return;
+ }
+
+ session.terminate();
+ mSessions.delete(sessionId);
+ }
+
+ /**
+ * Retrieve a session.
+ *
+ * @param sessionId Session ID of the session to be retrieved.
+ * @return Session or null if there's no session corresponding to the
+ * sessionId.
+ */
public WifiNanSessionState getSession(int sessionId) {
return mSessions.get(sessionId);
}
- public void onConfigCompleted(ConfigRequest completedConfig) {
- if (mListener != null && (mEvents & WifiNanEventListener.LISTEN_CONFIG_COMPLETED) != 0) {
+ /**
+ * Called to dispatch the NAN interface address change to the client - as an
+ * identity change (interface address information not propagated to client -
+ * privacy concerns).
+ *
+ * @param mac The new MAC address of the discovery interface - optionally propagated to the
+ * client.
+ */
+ public void onInterfaceAddressChange(byte[] mac) {
+ if (mConfigRequest.mEnableIdentityChangeCallback && !Arrays.equals(mac,
+ mLastDiscoveryInterfaceMac)) {
try {
- mListener.onConfigCompleted(completedConfig);
- } catch (RemoteException e) {
- Log.w(TAG, "onConfigCompleted: RemoteException - ignored: " + e);
- }
- }
- }
-
- public void onConfigFailed(ConfigRequest failedConfig, int reason) {
- if (mListener != null && (mEvents & WifiNanEventListener.LISTEN_CONFIG_FAILED) != 0) {
- try {
- mListener.onConfigFailed(failedConfig, reason);
- } catch (RemoteException e) {
- Log.w(TAG, "onConfigFailed: RemoteException - ignored: " + e);
- }
- }
- }
-
- public int onNanDown(int reason) {
- if (mListener != null && (mEvents & WifiNanEventListener.LISTEN_NAN_DOWN) != 0) {
- try {
- mListener.onNanDown(reason);
- } catch (RemoteException e) {
- Log.w(TAG, "onNanDown: RemoteException - ignored: " + e);
- }
-
- return 1;
- }
-
- return 0;
- }
-
- public int onInterfaceAddressChange(byte[] mac) {
- if (mListener != null && (mEvents & WifiNanEventListener.LISTEN_IDENTITY_CHANGED) != 0) {
- try {
- mListener.onIdentityChanged();
+ // TODO: b/30000323 - resolve privacy concerns
+ mCallback.onIdentityChanged(mac);
} catch (RemoteException e) {
Log.w(TAG, "onIdentityChanged: RemoteException - ignored: " + e);
}
-
- return 1;
}
- return 0;
+ mLastDiscoveryInterfaceMac = mac;
}
- public int onClusterChange(int flag, byte[] mac) {
- if (mListener != null && (mEvents & WifiNanEventListener.LISTEN_IDENTITY_CHANGED) != 0) {
+ /**
+ * Called to dispatch the NAN cluster change (due to joining of a new
+ * cluster or starting a cluster) to the client - as an identity change
+ * (interface address information not propagated to client - privacy
+ * concerns). Dispatched if the client registered for the identity changed
+ * event.
+ *
+ * @param mac The cluster ID of the cluster started or joined.
+ * @param currentDiscoveryInterfaceMac The MAC address of the discovery interface.
+ */
+ public void onClusterChange(int flag, byte[] mac, byte[] currentDiscoveryInterfaceMac) {
+ if (mConfigRequest.mEnableIdentityChangeCallback && !Arrays.equals(
+ currentDiscoveryInterfaceMac, mLastDiscoveryInterfaceMac)) {
try {
- mListener.onIdentityChanged();
+ // TODO: b/30000323 - resolve privacy concerns
+ mCallback.onIdentityChanged(currentDiscoveryInterfaceMac);
} catch (RemoteException e) {
Log.w(TAG, "onIdentityChanged: RemoteException - ignored: " + e);
}
-
- return 1;
}
- return 0;
+ mLastDiscoveryInterfaceMac = currentDiscoveryInterfaceMac;
}
+ /**
+ * Called on RTT success - forwards call to client.
+ */
+ public void onRangingSuccess(int rangingId, RttManager.ParcelableRttResults results) {
+ if (VDBG) {
+ Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId + ", results=" + results);
+ }
+ try {
+ mCallback.onRangingSuccess(rangingId, results);
+ } catch (RemoteException e) {
+ Log.w(TAG, "onRangingSuccess: RemoteException - ignored: " + e);
+ }
+ }
+
+ /**
+ * Called on RTT failure - forwards call to client.
+ */
+ public void onRangingFailure(int rangingId, int reason, String description) {
+ if (VDBG) {
+ Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId + ", reason=" + reason
+ + ", description=" + description);
+ }
+ try {
+ mCallback.onRangingFailure(rangingId, reason, description);
+ } catch (RemoteException e) {
+ Log.w(TAG, "onRangingFailure: RemoteException - ignored: " + e);
+ }
+ }
+
+ /**
+ * Called on RTT operation aborted - forwards call to client.
+ */
+ public void onRangingAborted(int rangingId) {
+ if (VDBG) Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId);
+ try {
+ mCallback.onRangingAborted(rangingId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "onRangingAborted: RemoteException - ignored: " + e);
+ }
+ }
+
+ /**
+ * Dump the internal state of the class.
+ */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("NanClientState:");
- pw.println(" mUid: " + mUid);
+ pw.println(" mClientId: " + mClientId);
pw.println(" mConfigRequest: " + mConfigRequest);
- pw.println(" mListener: " + mListener);
- pw.println(" mEvents: " + mEvents);
+ pw.println(" mCallback: " + mCallback);
pw.println(" mSessions: [" + mSessions + "]");
for (int i = 0; i < mSessions.size(); ++i) {
mSessions.valueAt(i).dump(fd, pw, args);
diff --git a/service/java/com/android/server/wifi/nan/WifiNanDataPathStateManager.java b/service/java/com/android/server/wifi/nan/WifiNanDataPathStateManager.java
new file mode 100644
index 0000000..c3a9b9b
--- /dev/null
+++ b/service/java/com/android/server/wifi/nan/WifiNanDataPathStateManager.java
@@ -0,0 +1,1002 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi.nan;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkAgent;
+import android.net.NetworkCapabilities;
+import android.net.NetworkFactory;
+import android.net.NetworkInfo;
+import android.net.NetworkRequest;
+import android.net.RouteInfo;
+import android.net.UidRange;
+import android.net.wifi.nan.WifiNanManager;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import libcore.util.HexEncoding;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Manages NAN data-path lifetime: interface creation/deletion, data-path setup and tear-down.
+ * The NAN network configuration is:
+ * - transport = TRANSPORT_WIFI_NAN
+ * - capabilities = NET_CAPABILITY_NOT_VPN
+ * - network specifier generated by WifiNanSession.createNetworkSpecifier(...) or
+ * WifiNanManager.createNetworkSpecifier(...).
+ * The network specifier is encoded as a JSON string with the following key combos:
+ * TYPE_1A: type, role, client_id, session_id, peer_id, token
+ * TYPE_1B: type, role, client_id, session_id, peer_id [only permitted for RESPONDER]
+ * TYPE_2A: type, role, client_id, peer_mac, token
+ * TYPE_2B: type, role, client_id, peer_mac [only permitted for RESPONDER]
+ * TYPE_2C: type, role, client_id, token [only permitted for RESPONDER]
+ * TYPE_2D: type, role, client_id [only permitted for RESPONDER]
+ */
+public class WifiNanDataPathStateManager {
+ private static final String TAG = "WifiNanDataPathStateMgr";
+
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false; // STOPSHIP if true
+
+ private static final String NAN_INTERFACE_PREFIX = "nan";
+ private static final String NETWORK_TAG = "WIFI_NAN_FACTORY";
+ private static final String AGENT_TAG_PREFIX = "WIFI_NAN_AGENT_";
+ private static final int NETWORK_FACTORY_SCORE_AVAIL = 1;
+ private static final int NETWORK_FACTORY_BANDWIDTH_AVAIL = 1;
+ private static final int NETWORK_FACTORY_SIGNAL_STRENGTH_AVAIL = 1;
+
+ private final WifiNanStateManager mMgr;
+ private final NetworkInterfaceWrapper mNiWrapper = new NetworkInterfaceWrapper();
+ private final NetworkCapabilities mNetworkCapabilitiesFilter = new NetworkCapabilities();
+ private final Set<String> mInterfaces = new HashSet<>();
+ private final Map<String, NanNetworkRequestInformation> mNetworkRequestsCache = new HashMap<>();
+ private Context mContext;
+ private Looper mLooper;
+ private WifiNanNetworkFactory mNetworkFactory;
+ private INetworkManagementService mNwService;
+
+ public WifiNanDataPathStateManager(WifiNanStateManager mgr) {
+ mMgr = mgr;
+ }
+
+ /**
+ * Initialize the NAN data-path state manager. Specifically register the network factory with
+ * connectivity service.
+ */
+ public void start(Context context, Looper looper) {
+ if (VDBG) Log.v(TAG, "start");
+
+ mContext = context;
+ mLooper = looper;
+
+ mNetworkCapabilitiesFilter.clearAll();
+ mNetworkCapabilitiesFilter.addTransportType(NetworkCapabilities.TRANSPORT_WIFI_NAN);
+ mNetworkCapabilitiesFilter
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED);
+ mNetworkCapabilitiesFilter
+ .setNetworkSpecifier(NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER);
+ mNetworkCapabilitiesFilter.setLinkUpstreamBandwidthKbps(NETWORK_FACTORY_BANDWIDTH_AVAIL);
+ mNetworkCapabilitiesFilter.setLinkDownstreamBandwidthKbps(NETWORK_FACTORY_BANDWIDTH_AVAIL);
+ mNetworkCapabilitiesFilter.setSignalStrength(NETWORK_FACTORY_SIGNAL_STRENGTH_AVAIL);
+
+ mNetworkFactory = new WifiNanNetworkFactory(looper, context, mNetworkCapabilitiesFilter);
+ mNetworkFactory.setScoreFilter(NETWORK_FACTORY_SCORE_AVAIL);
+ mNetworkFactory.register();
+
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ mNwService = INetworkManagementService.Stub.asInterface(b);
+ }
+
+ private Map.Entry<String, NanNetworkRequestInformation> getNetworkRequestByNdpId(int ndpId) {
+ for (Map.Entry<String, NanNetworkRequestInformation> entry : mNetworkRequestsCache
+ .entrySet()) {
+ if (entry.getValue().ndpId == ndpId) {
+ return entry;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Create all NAN data-path interfaces which are possible on the device - based on the
+ * capabilities of the firmware.
+ */
+ public void createAllInterfaces() {
+ if (VDBG) Log.v(TAG, "createAllInterfaces");
+
+ if (mMgr.mCapabilities == null) {
+ Log.e(TAG, "createAllInterfaces: capabilities aren't initialized yet!");
+ return;
+ }
+
+ for (int i = 0; i < mMgr.mCapabilities.maxNdiInterfaces; ++i) {
+ String name = NAN_INTERFACE_PREFIX + i;
+ if (mInterfaces.contains(name)) {
+ Log.e(TAG, "createAllInterfaces(): interface already up, " + name
+ + ", possibly failed to delete - deleting/creating again to be safe");
+ mMgr.deleteDataPathInterface(name);
+
+ // critical to remove so that don't get infinite loop if the delete fails again
+ mInterfaces.remove(name);
+ }
+
+ mMgr.createDataPathInterface(name);
+ }
+ }
+
+ /**
+ * Delete all NAN data-path interfaces which are currently up.
+ */
+ public void deleteAllInterfaces() {
+ if (VDBG) Log.v(TAG, "deleteAllInterfaces");
+
+ for (String name : mInterfaces) {
+ mMgr.deleteDataPathInterface(name);
+ }
+ }
+
+ /**
+ * Called when firmware indicates the an interface was created.
+ */
+ public void onInterfaceCreated(String interfaceName) {
+ if (VDBG) Log.v(TAG, "onInterfaceCreated: interfaceName=" + interfaceName);
+
+ if (mInterfaces.contains(interfaceName)) {
+ Log.w(TAG, "onInterfaceCreated: already contains interface -- " + interfaceName);
+ }
+
+ mInterfaces.add(interfaceName);
+ }
+
+ /**
+ * Called when firmware indicates the an interface was deleted.
+ */
+ public void onInterfaceDeleted(String interfaceName) {
+ if (VDBG) Log.v(TAG, "onInterfaceDeleted: interfaceName=" + interfaceName);
+
+ if (!mInterfaces.contains(interfaceName)) {
+ Log.w(TAG, "onInterfaceDeleted: interface not on list -- " + interfaceName);
+ }
+
+ mInterfaces.remove(interfaceName);
+ }
+
+ /**
+ * Response to initiating data-path request. Indicates that request is successful (not
+ * complete!) and is now in progress.
+ *
+ * @param networkSpecifier The network specifier provided as part of the initiate request.
+ * @param ndpId The ID assigned to the data-path.
+ */
+ public void onDataPathInitiateSuccess(String networkSpecifier, int ndpId) {
+ if (VDBG) {
+ Log.v(TAG,
+ "onDataPathInitiateSuccess: networkSpecifier=" + networkSpecifier + ", ndpId="
+ + ndpId);
+ }
+
+ NanNetworkRequestInformation nnri = mNetworkRequestsCache.get(networkSpecifier);
+ if (nnri == null) {
+ Log.w(TAG, "onDataPathInitiateSuccess: network request not found for networkSpecifier="
+ + networkSpecifier);
+ mMgr.endDataPath(ndpId);
+ return;
+ }
+
+ if (nnri.state != NanNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_REQUEST_RESPONSE) {
+ Log.w(TAG, "onDataPathInitiateSuccess: network request in incorrect state: state="
+ + nnri.state);
+ mNetworkRequestsCache.remove(networkSpecifier);
+ mMgr.endDataPath(ndpId);
+ return;
+ }
+
+ nnri.state = NanNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_CONFIRM;
+ nnri.ndpId = ndpId;
+ }
+
+ /**
+ * Response to an attempt to set up a data-path (on the initiator side).
+ *
+ * @param networkSpecifier The network specifier provided as part of the initiate request.
+ * @param reason Failure reason.
+ */
+ public void onDataPathInitiateFail(String networkSpecifier, int reason) {
+ if (VDBG) {
+ Log.v(TAG,
+ "onDataPathInitiateFail: networkSpecifier=" + networkSpecifier + ", reason="
+ + reason);
+ }
+
+ NanNetworkRequestInformation nnri = mNetworkRequestsCache.remove(networkSpecifier);
+ if (nnri == null) {
+ Log.w(TAG, "onDataPathInitiateFail: network request not found for networkSpecifier="
+ + networkSpecifier);
+ return;
+ }
+
+ if (nnri.state != NanNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_REQUEST_RESPONSE) {
+ Log.w(TAG, "onDataPathInitiateFail: network request in incorrect state: state="
+ + nnri.state);
+ }
+
+ mNetworkRequestsCache.remove(networkSpecifier);
+ }
+
+
+ /**
+ * Notification (unsolicited/asynchronous) that a peer has requested to set up a data-path
+ * connection with us.
+ *
+ * @param pubSubId The ID of the discovery session context for the data-path - or 0 if not
+ * related to a discovery session.
+ * @param mac The discovery MAC address of the peer.
+ * @param ndpId The locally assigned ID for the data-path.
+ * @param message A message sent from the peer as part of the data-path setup process.
+ * @param messageLength The length of the {@code message}.
+ * @return The network specifier of the data-path (or null if none/error)
+ */
+ public String onDataPathRequest(int pubSubId, byte[] mac, int ndpId, byte[] message, int
+ messageLength) {
+ if (VDBG) {
+ Log.v(TAG,
+ "onDataPathRequest: pubSubId=" + pubSubId + ", mac=" + String.valueOf(
+ HexEncoding.encode(mac)) + ", ndpId=" + ndpId + ", messageLength="
+ + messageLength);
+ }
+
+ String networkSpecifier = null;
+ NanNetworkRequestInformation nnri = null;
+ for (Map.Entry<String, NanNetworkRequestInformation> entry : mNetworkRequestsCache
+ .entrySet()) {
+ /*
+ * Checking that the incoming request (from the Initiator) matches the request
+ * we (the Responder) already have set up. The rules are:
+ * - The discovery session (pub/sub ID) must match.
+ * - The token (if specified - i.e. non-null) must match. A null token == accept any
+ * Initiator token.
+ * - The peer MAC address (if specified - i.e. non-null) must match. A null peer MAC ==
+ * accept (otherwise matching) requests from any peer MAC.
+ */
+ if (entry.getValue().pubSubId != 0 && entry.getValue().pubSubId != pubSubId) {
+ continue;
+ }
+
+ if (entry.getValue().token != null && !Arrays.equals(entry.getValue().token,
+ Arrays.copyOfRange(message, 0, messageLength))) {
+ continue;
+ }
+
+ if (entry.getValue().peerDiscoveryMac != null && !Arrays.equals(
+ entry.getValue().peerDiscoveryMac, mac)) {
+ continue;
+ }
+
+ networkSpecifier = entry.getKey();
+ nnri = entry.getValue();
+ break;
+ }
+
+ if (nnri == null) {
+ Log.w(TAG, "onDataPathRequest: can't find a request with specified pubSubId=" + pubSubId
+ + ", mac=" + String.valueOf(HexEncoding.encode(mac)) + ", message=" + message
+ + ", messageLength=" + messageLength);
+ if (DBG) {
+ Log.d(TAG, "onDataPathRequest: network request cache = " + mNetworkRequestsCache);
+ }
+ mMgr.respondToDataPathRequest(false, ndpId, "", "");
+ // TODO: validate respond with REJECT (when respond has a meaning)
+ return null;
+ }
+
+ if (nnri.state != NanNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_REQUEST) {
+ Log.w(TAG, "onDataPathRequest: request " + networkSpecifier + " is incorrect state="
+ + nnri.state);
+ mMgr.respondToDataPathRequest(false, ndpId, "", "");
+ // TODO: validate respond with REJECT (when respond has a meaning)
+ mNetworkRequestsCache.remove(networkSpecifier);
+ return null;
+ }
+
+ nnri.state = NanNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_CONFIRM;
+ nnri.ndpId = ndpId;
+ nnri.interfaceName = selectInterfaceForRequest(nnri);
+ mMgr.respondToDataPathRequest(true, ndpId, nnri.interfaceName, "");
+
+ return networkSpecifier;
+ // TODO: validate respond with ACCEPT (when respond has a meaning)
+ }
+
+ /**
+ * Notification (unsolicited/asynchronous) that the data-path (which we've been setting up)
+ * is possibly (if {@code accept} is {@code true}) ready for use from the firmware's
+ * perspective - now can do L3 configuration.
+ *
+ * @param ndpId Id of the data-path
+ * @param mac The MAC address of the peer's data-path (not discovery interface). Only
+ * valid
+ * if {@code accept} is {@code true}.
+ * @param accept Indicates whether the data-path setup has succeeded (been accepted) or
+ * failed (been rejected).
+ * @param reason If {@code accept} is {@code false} provides a reason code for the
+ * rejection/failure.
+ * @param message The message provided by the peer as part of the data-path setup
+ * process.
+ * @param messageLength Length of the {@code message}.
+ * @return The network specifier of the data-path or a null if none/error.
+ */
+ public String onDataPathConfirm(int ndpId, byte[] mac, boolean accept, int reason,
+ byte[] message, int messageLength) {
+ if (VDBG) {
+ Log.v(TAG, "onDataPathConfirm: ndpId=" + ndpId + ", mac=" + String.valueOf(
+ HexEncoding.encode(mac)) + ", accept=" + accept + ", reason=" + reason
+ + ", messageLength=" + messageLength);
+ }
+
+ Map.Entry<String, NanNetworkRequestInformation> nnriE = getNetworkRequestByNdpId(ndpId);
+ if (nnriE == null) {
+ Log.w(TAG, "onDataPathConfirm: network request not found for ndpId=" + ndpId);
+ if (accept) {
+ mMgr.endDataPath(ndpId);
+ }
+ return null;
+ }
+
+ String networkSpecifier = nnriE.getKey();
+ NanNetworkRequestInformation nnri = nnriE.getValue();
+
+ // validate state
+ if (nnri.role == WifiNanManager.WIFI_NAN_DATA_PATH_ROLE_INITIATOR
+ && nnri.state != NanNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_CONFIRM) {
+ Log.w(TAG, "onDataPathConfirm: INITIATOR in invalid state=" + nnri.state);
+ mNetworkRequestsCache.remove(networkSpecifier);
+ if (accept) {
+ mMgr.endDataPath(ndpId);
+ }
+ return networkSpecifier;
+ }
+ if (nnri.role == WifiNanManager.WIFI_NAN_DATA_PATH_ROLE_RESPONDER
+ && nnri.state != NanNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_CONFIRM) {
+ Log.w(TAG, "onDataPathConfirm: RESPONDER in invalid state=" + nnri.state);
+ mNetworkRequestsCache.remove(networkSpecifier);
+ if (accept) {
+ mMgr.endDataPath(ndpId);
+ }
+ return networkSpecifier;
+ }
+
+ if (accept) {
+ nnri.state = (nnri.role == WifiNanManager.WIFI_NAN_DATA_PATH_ROLE_INITIATOR)
+ ? NanNetworkRequestInformation.STATE_INITIATOR_CONFIRMED
+ : NanNetworkRequestInformation.STATE_RESPONDER_CONFIRMED;
+ nnri.peerDataMac = mac;
+
+ NetworkInfo networkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0,
+ NETWORK_TAG, "");
+ NetworkCapabilities networkCapabilities = new NetworkCapabilities(
+ mNetworkCapabilitiesFilter);
+ LinkProperties linkProperties = new LinkProperties();
+
+ try {
+ // TODO: b/29608448 to track other IPv6 related optimizations and move to IpManager
+ mNwService.setInterfaceUp(nnri.interfaceName);
+ mNwService.enableIpv6(nnri.interfaceName);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onDataPathConfirm: ACCEPT nnri=" + nnri + ": can't configure network - "
+ + e);
+ mMgr.endDataPath(ndpId);
+ return networkSpecifier;
+ }
+
+ if (!mNiWrapper.configureAgentProperties(nnri, networkSpecifier, ndpId, networkInfo,
+ networkCapabilities, linkProperties)) {
+ return networkSpecifier;
+ }
+
+ nnri.networkAgent = new WifiNanNetworkAgent(mLooper, mContext,
+ AGENT_TAG_PREFIX + nnri.ndpId, networkInfo, networkCapabilities, linkProperties,
+ NETWORK_FACTORY_SCORE_AVAIL, networkSpecifier, ndpId);
+ } else {
+ if (DBG) {
+ Log.d(TAG, "onDataPathConfirm: data-path for networkSpecifier=" + networkSpecifier
+ + " rejected - reason=" + reason);
+ }
+ mNetworkRequestsCache.remove(networkSpecifier);
+ }
+
+ return networkSpecifier;
+ }
+
+ /**
+ * Notification (unsolicited/asynchronous) from the firmware that the specified data-path has
+ * been terminated.
+ *
+ * @param ndpId The ID of the terminated data-path.
+ */
+ public void onDataPathEnd(int ndpId) {
+ if (VDBG) Log.v(TAG, "onDataPathEnd: ndpId=" + ndpId);
+
+ Map.Entry<String, NanNetworkRequestInformation> nnriE = getNetworkRequestByNdpId(ndpId);
+ if (nnriE == null) {
+ if (DBG) {
+ Log.d(TAG, "onDataPathEnd: network request not found for ndpId=" + ndpId);
+ }
+ return;
+ }
+
+ tearDownInterface(nnriE.getValue());
+ mNetworkRequestsCache.remove(nnriE.getKey());
+ }
+
+ /**
+ * Called whenever NAN comes down. Clean up all pending and up network requeests and agents.
+ */
+ public void onNanDownCleanupDataPaths() {
+ if (VDBG) Log.v(TAG, "onNanDownCleanupDataPaths");
+
+ for (NanNetworkRequestInformation nnri : mNetworkRequestsCache.values()) {
+ tearDownInterface(nnri);
+ }
+ mNetworkRequestsCache.clear();
+ }
+
+ /**
+ * Called when timed-out waiting for confirmation of the data-path setup (i.e.
+ * onDataPathConfirm). Started on the initiator when executing the request for the data-path
+ * and on the responder when received a request for data-path (in both cases only on success
+ * - i.e. when we're proceeding with data-path setup).
+ */
+ public void handleDataPathTimeout(String networkSpecifier) {
+ if (VDBG) Log.v(TAG, "handleDataPathTimeout: networkSpecifier=" + networkSpecifier);
+
+ NanNetworkRequestInformation nnri = mNetworkRequestsCache.remove(networkSpecifier);
+ if (nnri == null) {
+ if (DBG) {
+ Log.d(TAG,
+ "handleDataPathTimeout: network request not found for networkSpecifier="
+ + networkSpecifier);
+ }
+ return;
+ }
+
+ mMgr.endDataPath(nnri.ndpId);
+ }
+
+ private class WifiNanNetworkFactory extends NetworkFactory {
+ WifiNanNetworkFactory(Looper looper, Context context, NetworkCapabilities filter) {
+ super(looper, context, NETWORK_TAG, filter);
+ }
+
+ @Override
+ public boolean acceptRequest(NetworkRequest request, int score) {
+ if (VDBG) {
+ Log.v(TAG, "WifiNanNetworkFactory.acceptRequest: request=" + request + ", score="
+ + score);
+ }
+
+ if (!mMgr.isUsageEnabled()) {
+ if (VDBG) {
+ Log.v(TAG, "WifiNanNetworkFactory.acceptRequest: request=" + request
+ + " -- NAN disabled");
+ }
+ return false;
+ }
+
+ if (mInterfaces.isEmpty()) {
+ Log.w(TAG, "WifiNanNetworkFactory.acceptRequest: request=" + request
+ + " -- No NAN interfaces are up");
+ return false;
+ }
+
+ String networkSpecifier = request.networkCapabilities.getNetworkSpecifier();
+ if (TextUtils.isEmpty(networkSpecifier)) {
+ Log.w(TAG, "WifiNanNetworkFactory.acceptRequest: request=" + request
+ + " - empty (or null) NetworkSpecifier");
+ return false;
+ }
+
+ // look up specifier - are we being called again?
+ NanNetworkRequestInformation nnri = mNetworkRequestsCache.get(networkSpecifier);
+ if (nnri != null) {
+ if (DBG) {
+ Log.d(TAG, "WifiNanNetworkFactory.acceptRequest: request=" + request
+ + " - already in cache!?");
+ }
+
+ // seems to happen after a network agent is created - trying to rematch all
+ // requests again!?
+ return true;
+ }
+
+ // parse network specifier (JSON) & cache
+ // TODO: validate that the client ID actually comes from the correct process and is
+ // not faked?
+ nnri = NanNetworkRequestInformation.parseNetworkSpecifier(networkSpecifier, mMgr);
+ if (nnri == null) {
+ Log.e(TAG, "WifiNanNetworkFactory.acceptRequest: request=" + request
+ + " - can't parse network specifier");
+ return false;
+ }
+ mNetworkRequestsCache.put(networkSpecifier, nnri);
+
+ return true;
+ }
+
+ @Override
+ protected void needNetworkFor(NetworkRequest networkRequest, int score) {
+ if (VDBG) {
+ Log.v(TAG, "WifiNanNetworkFactory.needNetworkFor: networkRequest=" + networkRequest
+ + ", score=" + score);
+ }
+
+ String networkSpecifier = networkRequest.networkCapabilities.getNetworkSpecifier();
+ NanNetworkRequestInformation nnri = mNetworkRequestsCache.get(networkSpecifier);
+ if (nnri == null) {
+ Log.e(TAG, "WifiNanNetworkFactory.needNetworkFor: networkRequest=" + networkRequest
+ + " not in cache!?");
+ return;
+ }
+
+ if (nnri.role == WifiNanManager.WIFI_NAN_DATA_PATH_ROLE_INITIATOR) {
+ if (nnri.state != NanNetworkRequestInformation.STATE_INITIATOR_IDLE) {
+ if (DBG) {
+ Log.d(TAG, "WifiNanNetworkFactory.needNetworkFor: networkRequest="
+ + networkRequest + " - already in progress");
+ // TODO: understand how/when can be called again/while in progress (seems
+ // to be related to score re-calculation after a network agent is created)
+ }
+ return;
+ }
+
+ nnri.interfaceName = selectInterfaceForRequest(nnri);
+ mMgr.initiateDataPathSetup(networkSpecifier, nnri.peerId,
+ WifiNanNative.CHANNEL_REQUEST_TYPE_REQUESTED, selectChannelForRequest(nnri),
+ nnri.peerDiscoveryMac, nnri.interfaceName, nnri.token);
+ nnri.state = NanNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_REQUEST_RESPONSE;
+ } else {
+ if (nnri.state != NanNetworkRequestInformation.STATE_RESPONDER_IDLE) {
+ if (DBG) {
+ Log.d(TAG, "WifiNanNetworkFactory.needNetworkFor: networkRequest="
+ + networkRequest + " - already in progress");
+ // TODO: understand how/when can be called again/while in progress (seems
+ // to be related to score re-calculation after a network agent is created)
+ }
+ return;
+ }
+
+ nnri.state = NanNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_REQUEST;
+ }
+ }
+
+ @Override
+ protected void releaseNetworkFor(NetworkRequest networkRequest) {
+ if (VDBG) {
+ Log.v(TAG, "WifiNanNetworkFactory.releaseNetworkFor: networkRequest="
+ + networkRequest);
+ }
+
+ String networkSpecifier = networkRequest.networkCapabilities.getNetworkSpecifier();
+ NanNetworkRequestInformation nnri = mNetworkRequestsCache.get(networkSpecifier);
+ if (nnri == null) {
+ Log.e(TAG,
+ "WifiNanNetworkFactory.releaseNetworkFor: networkRequest=" + networkRequest
+ + " not in cache!?");
+ return;
+ }
+
+ if (nnri.networkAgent != null) {
+ if (VDBG) {
+ Log.v(TAG, "WifiNanNetworkFactory.releaseNetworkFor: networkRequest="
+ + networkRequest + ", nnri=" + nnri
+ + ": agent already created - deferring ending data-path to agent"
+ + ".unwanted()");
+ }
+ return;
+ }
+
+ if (nnri.role == WifiNanManager.WIFI_NAN_DATA_PATH_ROLE_INITIATOR && nnri.state
+ > NanNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_REQUEST_RESPONSE) {
+ mMgr.endDataPath(nnri.ndpId);
+ }
+ if (nnri.role == WifiNanManager.WIFI_NAN_DATA_PATH_ROLE_RESPONDER
+ && nnri.state > NanNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_REQUEST) {
+ mMgr.endDataPath(nnri.ndpId);
+ }
+
+ // Will get a callback (on both initiator and responder) when data-path actually
+ // terminated. At that point will inform the agent and will clear the cache.
+ }
+ }
+
+ private class WifiNanNetworkAgent extends NetworkAgent {
+ private NetworkInfo mNetworkInfo;
+ private String mNetworkSpecifier;
+ private int mNdpId;
+
+ WifiNanNetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
+ NetworkCapabilities nc, LinkProperties lp, int score, String networkSpecifier,
+ int ndpId) {
+ super(looper, context, logTag, ni, nc, lp, score);
+
+ mNetworkInfo = ni;
+ mNetworkSpecifier = networkSpecifier;
+ mNdpId = ndpId;
+ }
+
+ @Override
+ protected void unwanted() {
+ if (VDBG) {
+ Log.v(TAG, "WifiNanNetworkAgent.unwanted: networkSpecifier=" + mNetworkSpecifier
+ + ", ndpId=" + mNdpId);
+ }
+
+ mMgr.endDataPath(mNdpId);
+
+ // Will get a callback (on both initiator and responder) when data-path actually
+ // terminated. At that point will inform the agent and will clear the cache.
+ }
+
+ void reconfigureAgentAsDisconnected() {
+ if (VDBG) {
+ Log.v(TAG, "WifiNanNetworkAgent.reconfigureAgentAsDisconnected: networkSpecifier="
+ + mNetworkSpecifier + ", ndpId=" + mNdpId);
+ }
+
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, "");
+ sendNetworkInfo(mNetworkInfo);
+ }
+ }
+
+ private void tearDownInterface(NanNetworkRequestInformation nnri) {
+ if (VDBG) Log.v(TAG, "tearDownInterface: nnri=" + nnri);
+
+ if (nnri.interfaceName != null && !nnri.interfaceName.isEmpty()) {
+ try {
+ // TODO: b/29608448 replace with IpManager
+ mNwService.setInterfaceDown(nnri.interfaceName);
+ } catch (RemoteException e) {
+ Log.e(TAG,
+ "tearDownInterface: nnri=" + nnri + ": can't bring interface down - " + e);
+ }
+ }
+
+ if (nnri.networkAgent != null) {
+ nnri.networkAgent.reconfigureAgentAsDisconnected();
+ }
+ }
+
+ /**
+ * Select one of the existing interfaces for the new network request.
+ *
+ * TODO: for now there is only a single interface - simply pick it.
+ */
+ private String selectInterfaceForRequest(NanNetworkRequestInformation req) {
+ Iterator<String> it = mInterfaces.iterator();
+ if (it.hasNext()) {
+ return it.next();
+ }
+
+ Log.e(TAG, "selectInterfaceForRequest: req=" + req + " - but no interfaces available!");
+
+ return "";
+ }
+
+ /**
+ * Select a channel for the network request.
+ *
+ * TODO: for now simply select channel 6
+ */
+ private int selectChannelForRequest(NanNetworkRequestInformation req) {
+ return 2437;
+ }
+
+ /**
+ * NAN network request. State object: contains network request information/state through its
+ * lifetime.
+ */
+ @VisibleForTesting
+ public static class NanNetworkRequestInformation {
+ static final int STATE_INITIATOR_IDLE = 100;
+ static final int STATE_INITIATOR_WAIT_FOR_REQUEST_RESPONSE = 101;
+ static final int STATE_INITIATOR_WAIT_FOR_CONFIRM = 102;
+ static final int STATE_INITIATOR_CONFIRMED = 103;
+
+ static final int STATE_RESPONDER_IDLE = 200;
+ static final int STATE_RESPONDER_WAIT_FOR_REQUEST = 201;
+ static final int STATE_RESPONDER_WAIT_FOR_RESPOND_RESPONSE = 202;
+ static final int STATE_RESPONDER_WAIT_FOR_CONFIRM = 203;
+ static final int STATE_RESPONDER_CONFIRMED = 204;
+
+ public int state;
+ public int role;
+
+ public int uid;
+ public String interfaceName;
+ public int pubSubId = 0;
+ public int peerId = 0;
+ public byte[] peerDiscoveryMac = null;
+ public byte[] token = null;
+ public int ndpId;
+ public byte[] peerDataMac;
+
+ public WifiNanNetworkAgent networkAgent;
+
+ static NanNetworkRequestInformation parseNetworkSpecifier(String networkSpecifier,
+ WifiNanStateManager mgr) {
+ int type, role, uid, clientId, sessionId = 0, peerId = 0, pubSubId = 0;
+ byte[] peerMac = null;
+ byte[] token = null;
+
+ if (VDBG) {
+ Log.v(TAG, "parseNetworkSpecifier: networkSpecifier=" + networkSpecifier);
+ }
+
+ try {
+ JSONObject jsonObject = new JSONObject(networkSpecifier);
+
+ // type: always present
+ type = jsonObject.getInt(WifiNanManager.NETWORK_SPECIFIER_KEY_TYPE);
+ if (type < 0 || type > WifiNanManager.NETWORK_SPECIFIER_TYPE_MAX_VALID) {
+ Log.e(TAG, "parseNetworkSpecifier: networkSpecifier=" + networkSpecifier
+ + ", invalid 'type' value");
+ return null;
+ }
+
+ // role: always present
+ role = jsonObject.getInt(WifiNanManager.NETWORK_SPECIFIER_KEY_ROLE);
+ if (role != WifiNanManager.WIFI_NAN_DATA_PATH_ROLE_INITIATOR
+ && role != WifiNanManager.WIFI_NAN_DATA_PATH_ROLE_RESPONDER) {
+ Log.e(TAG, "parseNetworkSpecifier: networkSpecifier=" + networkSpecifier
+ + " -- invalid 'role' value");
+ return null;
+ }
+
+ if (role == WifiNanManager.WIFI_NAN_DATA_PATH_ROLE_INITIATOR
+ && type != WifiNanManager.NETWORK_SPECIFIER_TYPE_1A
+ && type != WifiNanManager.NETWORK_SPECIFIER_TYPE_2A) {
+ Log.e(TAG, "parseNetworkSpecifier: networkSpecifier=" + networkSpecifier
+ + " -- invalid 'type' value for INITIATOR (only 1A and 2A are "
+ + "permitted)");
+ return null;
+ }
+
+ // clientId: always present
+ clientId = jsonObject.getInt(WifiNanManager.NETWORK_SPECIFIER_KEY_CLIENT_ID);
+
+ // sessionId: present for types 1A, 1B, 1C, 1D
+ if (type == WifiNanManager.NETWORK_SPECIFIER_TYPE_1A
+ || type == WifiNanManager.NETWORK_SPECIFIER_TYPE_1B
+ || type == WifiNanManager.NETWORK_SPECIFIER_TYPE_1C
+ || type == WifiNanManager.NETWORK_SPECIFIER_TYPE_1D) {
+ sessionId = jsonObject.getInt(WifiNanManager.NETWORK_SPECIFIER_KEY_SESSION_ID);
+ }
+
+ // peer Id: present for types 1A, 1B
+ if (type == WifiNanManager.NETWORK_SPECIFIER_TYPE_1A
+ || type == WifiNanManager.NETWORK_SPECIFIER_TYPE_1B) {
+ peerId = jsonObject.getInt(WifiNanManager.NETWORK_SPECIFIER_KEY_PEER_ID);
+ }
+
+ // peerMac: present for types 2A, 2B
+ if (type == WifiNanManager.NETWORK_SPECIFIER_TYPE_2A
+ || type == WifiNanManager.NETWORK_SPECIFIER_TYPE_2B) {
+ try {
+ peerMac = HexEncoding.decode(jsonObject.getString(
+ WifiNanManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(),
+ false);
+ if (peerMac == null || peerMac.length != 6) {
+ Log.e(TAG, "parseNetworkSpecifier: networkSpecifier=" + networkSpecifier
+ + " -- invalid peer MAC address - null or not 6 bytes");
+ return null;
+ }
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "parseNetworkSpecifier: networkSpecifier="
+ + networkSpecifier + " -- invalid peer MAC address -- e=" + e);
+ return null;
+ }
+ }
+
+ // token: present for types 1A, 1C, 2A, 2C
+ if (type == WifiNanManager.NETWORK_SPECIFIER_TYPE_1A
+ || type == WifiNanManager.NETWORK_SPECIFIER_TYPE_1C
+ || type == WifiNanManager.NETWORK_SPECIFIER_TYPE_2A
+ || type == WifiNanManager.NETWORK_SPECIFIER_TYPE_2C) {
+ token = Base64.decode(
+ jsonObject.getString(WifiNanManager.NETWORK_SPECIFIER_KEY_TOKEN),
+ Base64.DEFAULT);
+ }
+ } catch (JSONException e) {
+ Log.e(TAG, "parseNetworkSpecifier: networkSpecifier=" + networkSpecifier
+ + " -- invalid JSON format -- e=" + e);
+ return null;
+ }
+
+ // look up network specifier information in NAN state manager
+ WifiNanClientState client = mgr.getClient(clientId);
+ if (client == null) {
+ Log.e(TAG, "parseNetworkSpecifier: networkSpecifier=" + networkSpecifier
+ + " -- not client with this id -- clientId=" + clientId);
+ return null;
+ }
+ uid = client.getUid();
+
+ if (type == WifiNanManager.NETWORK_SPECIFIER_TYPE_1A
+ || type == WifiNanManager.NETWORK_SPECIFIER_TYPE_1B) {
+ WifiNanSessionState session = client.getSession(sessionId);
+ if (session == null) {
+ Log.e(TAG,
+ "parseNetworkSpecifier: networkSpecifier=" + networkSpecifier
+ + " -- not session with this id -- sessionId=" + sessionId);
+ return null;
+ }
+ pubSubId = session.getPubSubId();
+ String peerMacStr = session.getMac(peerId, null);
+ if (peerMacStr == null) {
+ Log.e(TAG,
+ "parseNetworkSpecifier: networkSpecifier=" + networkSpecifier
+ + " -- no MAC address associated with this peer id -- peerId="
+ + peerId);
+ return null;
+ }
+ try {
+ peerMac = HexEncoding.decode(peerMacStr.toCharArray(), false);
+ if (peerMac == null || peerMac.length != 6) {
+ Log.e(TAG, "parseNetworkSpecifier: networkSpecifier="
+ + networkSpecifier + " -- invalid peer MAC address");
+ return null;
+ }
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG,
+ "parseNetworkSpecifier: networkSpecifier=" + networkSpecifier
+ + " -- invalid peer MAC address -- e=" + e);
+ return null;
+ }
+ }
+
+ // create container and populate
+ NanNetworkRequestInformation nnri = new NanNetworkRequestInformation();
+ nnri.state = (role == WifiNanManager.WIFI_NAN_DATA_PATH_ROLE_INITIATOR)
+ ? NanNetworkRequestInformation.STATE_INITIATOR_IDLE
+ : NanNetworkRequestInformation.STATE_RESPONDER_IDLE;
+ nnri.role = role;
+ nnri.uid = uid;
+ nnri.pubSubId = pubSubId;
+ nnri.peerId = peerId;
+ nnri.peerDiscoveryMac = peerMac;
+ nnri.token = token;
+
+ return nnri;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("NanNetworkRequestInformation: ");
+ sb.append("state=").append(state).append(", role=").append(role).append(
+ ", uid=").append(uid).append(", interfaceName=").append(interfaceName).append(
+ ", pubSubId=").append(pubSubId).append(", peerDiscoveryMac=").append(
+ peerDiscoveryMac == null ? ""
+ : String.valueOf(HexEncoding.encode(peerDiscoveryMac))).append(
+ ", token=").append(token).append(", ndpId=").append(ndpId).append(
+ ", peerDataMac=").append(
+ peerDataMac == null ? "" : String.valueOf(HexEncoding.encode(peerDataMac)));
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Enables mocking.
+ */
+ @VisibleForTesting
+ public class NetworkInterfaceWrapper {
+ /**
+ * Configures network agent properties: link-local address, connected status, interface
+ * name. Delegated to enable mocking.
+ */
+ public boolean configureAgentProperties(NanNetworkRequestInformation nnri,
+ String networkSpecifier, int ndpId, NetworkInfo networkInfo,
+ NetworkCapabilities networkCapabilities, LinkProperties linkProperties) {
+ // find link-local address
+ InetAddress linkLocal = null;
+ NetworkInterface ni;
+ try {
+ ni = NetworkInterface.getByName(nnri.interfaceName);
+ } catch (SocketException e) {
+ Log.e(TAG, "onDataPathConfirm: ACCEPT nnri=" + nnri
+ + ": can't get network interface - " + e);
+ mMgr.endDataPath(ndpId);
+ return false;
+ }
+ if (ni == null) {
+ Log.e(TAG, "onDataPathConfirm: ACCEPT nnri=" + nnri
+ + ": can't get network interface (null)");
+ mMgr.endDataPath(ndpId);
+ return false;
+ }
+ Enumeration<InetAddress> addresses = ni.getInetAddresses();
+ while (addresses.hasMoreElements()) {
+ InetAddress ip = addresses.nextElement();
+ if (ip instanceof Inet6Address && ip.isLinkLocalAddress()) {
+ linkLocal = ip;
+ break;
+ }
+ }
+
+ if (linkLocal == null) {
+ Log.e(TAG, "onDataPathConfirm: ACCEPT nnri=" + nnri + ": no link local addresses");
+ mMgr.endDataPath(ndpId);
+ return false;
+ }
+
+ // configure agent
+ networkInfo.setIsAvailable(true);
+ networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+
+ networkCapabilities.setNetworkSpecifier(networkSpecifier);
+
+ linkProperties.setInterfaceName(nnri.interfaceName);
+ linkProperties.addLinkAddress(new LinkAddress(linkLocal, 64));
+ linkProperties.addRoute(
+ new RouteInfo(new IpPrefix("fe80::/64"), null, nnri.interfaceName));
+
+ return true;
+ }
+ }
+
+ /**
+ * Dump the internal state of the class.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("WifiNanDataPathStateManager:");
+ pw.println(" mInterfaces: " + mInterfaces);
+ pw.println(" mNetworkCapabilitiesFilter: " + mNetworkCapabilitiesFilter);
+ pw.println(" mNetworkRequestsCache: " + mNetworkRequestsCache);
+ pw.println(" mNetworkFactory:");
+ mNetworkFactory.dump(fd, pw, args);
+ }
+}
diff --git a/service/java/com/android/server/wifi/nan/WifiNanNative.java b/service/java/com/android/server/wifi/nan/WifiNanNative.java
index 8715719..8ecba96 100644
--- a/service/java/com/android/server/wifi/nan/WifiNanNative.java
+++ b/service/java/com/android/server/wifi/nan/WifiNanNative.java
@@ -17,23 +17,22 @@
package com.android.server.wifi.nan;
import android.net.wifi.nan.ConfigRequest;
-import android.net.wifi.nan.PublishData;
-import android.net.wifi.nan.PublishSettings;
-import android.net.wifi.nan.SubscribeData;
-import android.net.wifi.nan.SubscribeSettings;
-import android.net.wifi.nan.WifiNanSessionListener;
+import android.net.wifi.nan.PublishConfig;
+import android.net.wifi.nan.SubscribeConfig;
+import android.net.wifi.nan.WifiNanEventCallback;
+import android.net.wifi.nan.WifiNanSessionCallback;
import android.util.Log;
import com.android.server.wifi.WifiNative;
import libcore.util.HexEncoding;
+import java.util.Arrays;
+
/**
* Native calls to access the Wi-Fi NAN HAL.
*
* Relies on WifiNative to perform the actual HAL registration.
- *
- * {@hide}
*/
public class WifiNanNative {
private static final String TAG = "WifiNanNative";
@@ -42,12 +41,18 @@
private static final int WIFI_SUCCESS = 0;
- private static boolean sNanNativeInit = false;
-
private static WifiNanNative sWifiNanNativeSingleton;
+ private boolean mNativeHandlersIsInitialized = false;
+
private static native int registerNanNatives();
+ /**
+ * Returns the singleton WifiNanNative used to manage the actual NAN HAL
+ * interface.
+ *
+ * @return Singleton object.
+ */
public static WifiNanNative getInstance() {
// dummy reference - used to make sure that WifiNative is loaded before
// us since it is the one to load the shared library and starts its
@@ -66,6 +71,10 @@
return sWifiNanNativeSingleton;
}
+ /**
+ * A container class for NAN (vendor) implementation capabilities (or
+ * limitations). Filled-in by the firmware.
+ */
public static class Capabilities {
public int maxConcurrentNanClusters;
public int maxPublishes;
@@ -79,6 +88,7 @@
public int maxNdiInterfaces;
public int maxNdpSessions;
public int maxAppInfoLen;
+ public int maxQueuedTransmitMessages;
@Override
public String toString() {
@@ -89,233 +99,513 @@
+ ", maxServiceSpecificInfoLen=" + maxServiceSpecificInfoLen
+ ", maxVsaDataLen=" + maxVsaDataLen + ", maxMeshDataLen=" + maxMeshDataLen
+ ", maxNdiInterfaces=" + maxNdiInterfaces + ", maxNdpSessions="
- + maxNdpSessions + ", maxAppInfoLen=" + maxAppInfoLen + "]";
+ + maxNdpSessions + ", maxAppInfoLen=" + maxAppInfoLen
+ + ", maxQueuedTransmitMessages=" + maxQueuedTransmitMessages + "]";
}
}
- /* package */ static native int initNanHandlersNative(Object cls, int iface);
+ /* package */ static native int initNanHandlersNative(Class<WifiNative> cls, int iface);
- private static native int getCapabilitiesNative(short transactionId, Object cls, int iface);
-
- private boolean isNanInit(boolean tryToInit) {
- if (!tryToInit || sNanNativeInit) {
- return sNanNativeInit;
- }
-
- if (DBG) Log.d(TAG, "isNanInit: trying to init");
+ private boolean isNanInit() {
synchronized (WifiNative.sLock) {
- boolean halStarted = WifiNative.getWlanNativeInterface().isHalStarted();
- if (!halStarted) {
- halStarted = WifiNative.getWlanNativeInterface().startHal();
- }
- if (halStarted) {
+ if (!WifiNative.getWlanNativeInterface().isHalStarted()) {
+ /*
+ * We should never start the HAL - that's done at a higher level
+ * by the Wi-Fi state machine.
+ */
+ mNativeHandlersIsInitialized = false;
+ return false;
+ } else if (!mNativeHandlersIsInitialized) {
int ret = initNanHandlersNative(WifiNative.class, WifiNative.sWlan0Index);
if (DBG) Log.d(TAG, "initNanHandlersNative: res=" + ret);
- sNanNativeInit = ret == WIFI_SUCCESS;
+ mNativeHandlersIsInitialized = ret == WIFI_SUCCESS;
- if (sNanNativeInit) {
- ret = getCapabilitiesNative(
- WifiNanStateManager.getInstance().createNextTransactionId(),
- WifiNative.class,
- WifiNative.sWlan0Index);
- if (DBG) Log.d(TAG, "getCapabilitiesNative: res=" + ret);
- }
-
- return sNanNativeInit;
+ return mNativeHandlersIsInitialized;
} else {
- Log.w(TAG, "isNanInit: HAL not initialized");
- return false;
+ return true;
}
}
}
+ /**
+ * Tell the NAN JNI to re-initialize the NAN callback pointers next time it starts up.
+ */
+ public void deInitNan() {
+ mNativeHandlersIsInitialized = false;
+ }
+
private WifiNanNative() {
// do nothing
}
- private static native int enableAndConfigureNative(short transactionId, Object cls, int iface,
- ConfigRequest configRequest);
+ private static native int getCapabilitiesNative(short transactionId, Class<WifiNative> cls,
+ int iface);
- public void enableAndConfigure(short transactionId, ConfigRequest configRequest) {
- boolean success;
-
- if (VDBG) Log.d(TAG, "enableAndConfigure: configRequest=" + configRequest);
- if (isNanInit(true)) {
+ /**
+ * Query the NAN firmware's capabilities.
+ *
+ * @param transactionId Transaction ID for the transaction - used in the async callback to
+ * match with the original request.
+ */
+ public boolean getCapabilities(short transactionId) {
+ if (VDBG) Log.d(TAG, "getCapabilities");
+ if (isNanInit()) {
int ret;
synchronized (WifiNative.sLock) {
- ret = enableAndConfigureNative(transactionId, WifiNative.class,
- WifiNative.sWlan0Index, configRequest);
+ ret = getCapabilitiesNative(transactionId, WifiNative.class,
+ WifiNative.sWlan0Index);
}
- if (DBG) Log.d(TAG, "enableAndConfigureNative: ret=" + ret);
- success = ret == WIFI_SUCCESS;
+ if (ret != WIFI_SUCCESS) {
+ Log.w(TAG, "getCapabilities: HAL API returned non-success -- " + ret);
+ }
+ return ret == WIFI_SUCCESS;
} else {
- Log.w(TAG, "enableAndConfigure: NanInit fails");
- success = false;
+ Log.w(TAG, "getCapabilities: cannot initialize NAN");
+ return false;
}
-
-
- // TODO: do something on !success - send failure message back
}
- private static native int disableNative(short transactionId, Object cls, int iface);
+ private static native int enableAndConfigureNative(short transactionId, Class<WifiNative> cls,
+ int iface, ConfigRequest configRequest);
- public void disable(short transactionId) {
- boolean success;
+ private static native int updateConfigurationNative(short transactionId, Class<WifiNative> cls,
+ int iface, ConfigRequest configRequest);
+ /**
+ * Enable and configure NAN.
+ *
+ * @param transactionId Transaction ID for the transaction - used in the
+ * async callback to match with the original request.
+ * @param configRequest Requested NAN configuration.
+ * @param initialConfiguration Specifies whether initial configuration
+ * (true) or an update (false) to the configuration.
+ */
+ public boolean enableAndConfigure(short transactionId, ConfigRequest configRequest,
+ boolean initialConfiguration) {
+ if (VDBG) Log.d(TAG, "enableAndConfigure: configRequest=" + configRequest);
+ if (isNanInit()) {
+ int ret;
+ if (initialConfiguration) {
+ synchronized (WifiNative.sLock) {
+ ret = enableAndConfigureNative(transactionId, WifiNative.class,
+ WifiNative.sWlan0Index, configRequest);
+ }
+ if (ret != WIFI_SUCCESS) {
+ Log.w(TAG, "enableAndConfigureNative: HAL API returned non-success -- " + ret);
+ }
+ } else {
+ synchronized (WifiNative.sLock) {
+ ret = updateConfigurationNative(transactionId, WifiNative.class,
+ WifiNative.sWlan0Index, configRequest);
+ }
+ if (ret != WIFI_SUCCESS) {
+ Log.w(TAG, "updateConfigurationNative: HAL API returned non-success -- " + ret);
+ }
+ }
+ return ret == WIFI_SUCCESS;
+ } else {
+ Log.w(TAG, "enableAndConfigure: NanInit fails");
+ return false;
+ }
+ }
+
+ private static native int disableNative(short transactionId, Class<WifiNative> cls, int iface);
+
+ /**
+ * Disable NAN.
+ *
+ * @param transactionId transactionId Transaction ID for the transaction -
+ * used in the async callback to match with the original request.
+ */
+ public boolean disable(short transactionId) {
if (VDBG) Log.d(TAG, "disableNan");
- if (isNanInit(true)) {
+ if (isNanInit()) {
int ret;
synchronized (WifiNative.sLock) {
ret = disableNative(transactionId, WifiNative.class, WifiNative.sWlan0Index);
}
- if (DBG) Log.d(TAG, "disableNative: ret=" + ret);
- success = ret == WIFI_SUCCESS;
+ if (ret != WIFI_SUCCESS) {
+ Log.w(TAG, "disableNative: HAL API returned non-success -- " + ret);
+ }
+ return ret == WIFI_SUCCESS;
} else {
Log.w(TAG, "disable: cannot initialize NAN");
- success = false;
+ return false;
}
-
- // TODO: do something on !success - send failure message back
}
- private static native int publishNative(short transactionId, int publishId, Object cls,
- int iface,
- PublishData publishData, PublishSettings publishSettings);
+ private static native int publishNative(short transactionId, int publishId,
+ Class<WifiNative> cls, int iface, PublishConfig publishConfig);
- public void publish(short transactionId, int publishId, PublishData publishData,
- PublishSettings publishSettings) {
- boolean success;
-
+ /**
+ * Start or modify a service publish session.
+ *
+ * @param transactionId transactionId Transaction ID for the transaction -
+ * used in the async callback to match with the original request.
+ * @param publishId ID of the requested session - 0 to request a new publish
+ * session.
+ * @param publishConfig Configuration of the discovery session.
+ */
+ public boolean publish(short transactionId, int publishId, PublishConfig publishConfig) {
if (VDBG) {
- Log.d(TAG, "publish: transactionId=" + transactionId + ",data='" + publishData
- + "', settings=" + publishSettings);
+ Log.d(TAG, "publish: transactionId=" + transactionId + ", config=" + publishConfig);
}
- if (isNanInit(true)) {
+ if (isNanInit()) {
int ret;
synchronized (WifiNative.sLock) {
ret = publishNative(transactionId, publishId, WifiNative.class,
- WifiNative.sWlan0Index, publishData, publishSettings);
+ WifiNative.sWlan0Index, publishConfig);
}
- if (DBG) Log.d(TAG, "publishNative: ret=" + ret);
- success = ret == WIFI_SUCCESS;
+ if (ret != WIFI_SUCCESS) {
+ Log.w(TAG, "publishNative: HAL API returned non-success -- " + ret);
+ }
+ return ret == WIFI_SUCCESS;
} else {
Log.w(TAG, "publish: cannot initialize NAN");
- success = false;
+ return false;
}
-
- // TODO: do something on !success - send failure message back
}
- private static native int subscribeNative(short transactionId, int subscribeId, Object cls,
- int iface, SubscribeData subscribeData, SubscribeSettings subscribeSettings);
+ private static native int subscribeNative(short transactionId, int subscribeId,
+ Class<WifiNative> cls, int iface, SubscribeConfig subscribeConfig);
- public void subscribe(short transactionId, int subscribeId, SubscribeData subscribeData,
- SubscribeSettings subscribeSettings) {
- boolean success;
-
+ /**
+ * Start or modify a service subscription session.
+ *
+ * @param transactionId transactionId Transaction ID for the transaction -
+ * used in the async callback to match with the original request.
+ * @param subscribeId ID of the requested session - 0 to request a new
+ * subscribe session.
+ * @param subscribeConfig Configuration of the discovery session.
+ */
+ public boolean subscribe(short transactionId, int subscribeId,
+ SubscribeConfig subscribeConfig) {
if (VDBG) {
- Log.d(TAG, "subscribe: transactionId=" + transactionId + ", data='" + subscribeData
- + "', settings=" + subscribeSettings);
+ Log.d(TAG, "subscribe: transactionId=" + transactionId + ", config=" + subscribeConfig);
}
- if (isNanInit(true)) {
+ if (isNanInit()) {
int ret;
synchronized (WifiNative.sLock) {
ret = subscribeNative(transactionId, subscribeId, WifiNative.class,
- WifiNative.sWlan0Index, subscribeData, subscribeSettings);
+ WifiNative.sWlan0Index, subscribeConfig);
}
- if (DBG) Log.d(TAG, "subscribeNative: ret=" + ret);
- success = ret == WIFI_SUCCESS;
+ if (ret != WIFI_SUCCESS) {
+ Log.w(TAG, "subscribeNative: HAL API returned non-success -- " + ret);
+ }
+ return ret == WIFI_SUCCESS;
} else {
Log.w(TAG, "subscribe: cannot initialize NAN");
- success = false;
+ return false;
}
-
- // TODO: do something on !success - send failure message back
}
- private static native int sendMessageNative(short transactionId, Object cls, int iface,
- int pubSubId, int requestorInstanceId, byte[] dest, byte[] message, int messageLength);
+ private static native int sendMessageNative(short transactionId, Class<WifiNative> cls,
+ int iface, int pubSubId, int requestorInstanceId, byte[] dest, byte[] message,
+ int messageLength);
- public void sendMessage(short transactionId, int pubSubId, int requestorInstanceId, byte[] dest,
- byte[] message, int messageLength) {
- boolean success;
-
+ /**
+ * Send a message through an existing discovery session.
+ *
+ * @param transactionId transactionId Transaction ID for the transaction -
+ * used in the async callback to match with the original request.
+ * @param pubSubId ID of the existing publish/subscribe session.
+ * @param requestorInstanceId ID of the peer to communicate with - obtained
+ * through a previous discovery (match) operation with that peer.
+ * @param dest MAC address of the peer to communicate with - obtained
+ * together with requestorInstanceId.
+ * @param message Message.
+ * @param messageLength Message byte array length.
+ * @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,
+ byte[] dest, byte[] message, int messageLength, int messageId) {
if (VDBG) {
Log.d(TAG,
"sendMessage: transactionId=" + transactionId + ", pubSubId=" + pubSubId
+ ", requestorInstanceId=" + requestorInstanceId + ", dest="
+ String.valueOf(HexEncoding.encode(dest)) + ", messageLength="
- + messageLength);
+ + messageLength + ", messageId=" + messageId);
}
- if (isNanInit(true)) {
+ if (isNanInit()) {
int ret;
synchronized (WifiNative.sLock) {
ret = sendMessageNative(transactionId, WifiNative.class, WifiNative.sWlan0Index,
pubSubId, requestorInstanceId, dest, message, messageLength);
}
- if (DBG) Log.d(TAG, "sendMessageNative: ret=" + ret);
- success = ret == WIFI_SUCCESS;
+ if (ret != WIFI_SUCCESS) {
+ Log.w(TAG, "sendMessageNative: HAL API returned non-success -- " + ret);
+ }
+ return ret == WIFI_SUCCESS;
} else {
Log.w(TAG, "sendMessage: cannot initialize NAN");
- success = false;
+ return false;
}
-
- // TODO: do something on !success - send failure message back
}
- private static native int stopPublishNative(short transactionId, Object cls, int iface,
- int pubSubId);
+ private static native int stopPublishNative(short transactionId, Class<WifiNative> cls,
+ int iface, int pubSubId);
- public void stopPublish(short transactionId, int pubSubId) {
- boolean success;
-
+ /**
+ * Terminate a publish discovery session.
+ *
+ * @param transactionId transactionId Transaction ID for the transaction -
+ * used in the async callback to match with the original request.
+ * @param pubSubId ID of the publish/subscribe session - obtained when
+ * creating a session.
+ */
+ public boolean stopPublish(short transactionId, int pubSubId) {
if (VDBG) {
Log.d(TAG, "stopPublish: transactionId=" + transactionId + ", pubSubId=" + pubSubId);
}
- if (isNanInit(true)) {
+ if (isNanInit()) {
int ret;
synchronized (WifiNative.sLock) {
ret = stopPublishNative(transactionId, WifiNative.class, WifiNative.sWlan0Index,
pubSubId);
}
- if (DBG) Log.d(TAG, "stopPublishNative: ret=" + ret);
- success = ret == WIFI_SUCCESS;
+ if (ret != WIFI_SUCCESS) {
+ Log.w(TAG, "stopPublishNative: HAL API returned non-success -- " + ret);
+ }
+ return ret == WIFI_SUCCESS;
} else {
Log.w(TAG, "stopPublish: cannot initialize NAN");
- success = false;
+ return false;
}
-
- // TODO: do something on !success - send failure message back
}
- private static native int stopSubscribeNative(short transactionId, Object cls, int iface,
- int pubSubId);
+ private static native int stopSubscribeNative(short transactionId, Class<WifiNative> cls,
+ int iface, int pubSubId);
- public void stopSubscribe(short transactionId, int pubSubId) {
- boolean success;
-
+ /**
+ * Terminate a subscribe discovery session.
+ *
+ * @param transactionId transactionId Transaction ID for the transaction -
+ * used in the async callback to match with the original request.
+ * @param pubSubId ID of the publish/subscribe session - obtained when
+ * creating a session.
+ */
+ public boolean stopSubscribe(short transactionId, int pubSubId) {
if (VDBG) {
Log.d(TAG, "stopSubscribe: transactionId=" + transactionId + ", pubSubId=" + pubSubId);
}
- if (isNanInit(true)) {
+ if (isNanInit()) {
int ret;
synchronized (WifiNative.sLock) {
ret = stopSubscribeNative(transactionId, WifiNative.class, WifiNative.sWlan0Index,
pubSubId);
}
- if (DBG) Log.d(TAG, "stopSubscribeNative: ret=" + ret);
- success = ret == WIFI_SUCCESS;
+ if (ret != WIFI_SUCCESS) {
+ Log.w(TAG, "stopSubscribeNative: HAL API returned non-success -- " + ret);
+ }
+ return ret == WIFI_SUCCESS;
} else {
Log.w(TAG, "stopSubscribe: cannot initialize NAN");
- success = false;
+ return false;
+ }
+ }
+
+ private static native int createNanNetworkInterfaceNative(short transactionId,
+ Class<WifiNative> cls, int iface,
+ String interfaceName);
+
+ /**
+ * Create a NAN network interface. This only creates the Linux interface - it doesn't actually
+ * create the data connection.
+ *
+ * @param transactionId Transaction ID for the transaction - used in the async callback to
+ * match with the original request.
+ * @param interfaceName The name of the interface, e.g. "nan0".
+ */
+ public boolean createNanNetworkInterface(short transactionId, String interfaceName) {
+ if (VDBG) {
+ Log.v(TAG, "createNanNetworkInterface: transactionId=" + transactionId + ", "
+ + "interfaceName=" + interfaceName);
}
- // TODO: do something on !success - send failure message back
+ if (isNanInit()) {
+ int ret;
+ synchronized (WifiNative.sLock) {
+ ret = createNanNetworkInterfaceNative(transactionId, WifiNative.class, WifiNative
+ .sWlan0Index, interfaceName);
+ }
+ if (ret != WIFI_SUCCESS) {
+ Log.w(TAG,
+ "createNanNetworkInterfaceNative: HAL API returned non-success -- " + ret);
+ }
+ return ret == WIFI_SUCCESS;
+ } else {
+ Log.w(TAG, "createNanNetworkInterface: cannot initialize NAN");
+ return false;
+ }
+ }
+
+ private static native int deleteNanNetworkInterfaceNative(short transactionId,
+ Class<WifiNative> cls, int iface,
+ String interfaceName);
+
+ /**
+ * Deletes a NAN network interface. The data connection can (should?) be torn down previously.
+ *
+ * @param transactionId Transaction ID for the transaction - used in the async callback to
+ * match with the original request.
+ * @param interfaceName The name of the interface, e.g. "nan0".
+ */
+ public boolean deleteNanNetworkInterface(short transactionId, String interfaceName) {
+ if (VDBG) {
+ Log.v(TAG, "deleteNanNetworkInterface: transactionId=" + transactionId + ", "
+ + "interfaceName=" + interfaceName);
+ }
+
+ if (isNanInit()) {
+ int ret;
+ synchronized (WifiNative.sLock) {
+ ret = deleteNanNetworkInterfaceNative(transactionId, WifiNative.class, WifiNative
+ .sWlan0Index, interfaceName);
+ }
+ if (ret != WIFI_SUCCESS) {
+ Log.w(TAG,
+ "deleteNanNetworkInterfaceNative: HAL API returned non-success -- " + ret);
+ }
+ return ret == WIFI_SUCCESS;
+ } else {
+ Log.w(TAG, "deleteNanNetworkInterface: cannot initialize NAN");
+ return false;
+ }
+ }
+
+ private static native int initiateDataPathNative(short transactionId, Class<WifiNative> cls,
+ int iface, int peerId, int
+ channelRequestType, int channel,
+ byte[] peer, String interfaceName, byte[]
+ message, int messageLength);
+
+ public static final int CHANNEL_REQUEST_TYPE_NONE = 0;
+ public static final int CHANNEL_REQUEST_TYPE_REQUESTED = 1;
+ public static final int CHANNEL_REQUEST_TYPE_REQUIRED = 2;
+
+ /**
+ * Initiates setting up a data-path between device and peer.
+ *
+ * @param transactionId Transaction ID for the transaction - used in the async callback to
+ * match with the original request.
+ * @param peerId ID of the peer ID to associate the data path with. A value of 0
+ * indicates that not associated with an existing session.
+ * @param channelRequestType Indicates whether the specified channel is available, if available
+ * requested or forced (resulting in failure if cannot be
+ * accommodated).
+ * @param channel The channel on which to set up the data-path.
+ * @param peer The MAC address of the peer to create a connection with.
+ * @param interfaceName The interface on which to create the data connection.
+ * @param message An arbitrary byte array to forward to the peer as part of the data path
+ * request.
+ * @param messageLength The size of the message.
+ */
+ public boolean initiateDataPath(short transactionId, int peerId, int channelRequestType,
+ int channel, byte[] peer, String interfaceName, byte[]
+ message, int messageLength) {
+ if (VDBG) {
+ Log.v(TAG, "initiateDataPath: transactionId=" + transactionId + ", peerId=" + peerId
+ + ", channelRequestType=" + channelRequestType + ", channel=" + channel
+ + ", peer=" + String.valueOf(HexEncoding.encode(peer)) + ", interfaceName="
+ + interfaceName + ", " + "messageLength=" + messageLength);
+ }
+
+ if (isNanInit()) {
+ int ret;
+ synchronized (WifiNative.sLock) {
+ ret = initiateDataPathNative(transactionId, WifiNative.class, WifiNative
+ .sWlan0Index, peerId, channelRequestType, channel, peer, interfaceName,
+ message, messageLength);
+ }
+ if (ret != WIFI_SUCCESS) {
+ Log.w(TAG, "initiateDataPathNative: HAL API returned non-success -- " + ret);
+ }
+ return ret == WIFI_SUCCESS;
+ } else {
+ Log.w(TAG, "initiateDataPath: cannot initialize NAN");
+ return false;
+ }
+ }
+
+ private static native int respondToDataPathRequestNative(short transactionId,
+ Class<WifiNative> cls, int iface,
+ boolean accept, int ndpId, String
+ interfaceName, byte[]
+ message, int messageLength);
+
+ /**
+ * Responds to a data request from a peer.
+ *
+ * @param transactionId Transaction ID for the transaction - used in the async callback to
+ * match with the original request.
+ * @param accept Accept (true) or reject (false) the original call.
+ * @param ndpId The NDP (NAN data path) ID. Obtained from the request callback.
+ * @param interfaceName The interface on which the data path will be setup. Obtained from the
+ * request callback.
+ * @param message An arbitrary byte array to forward to the peer in the respond message.
+ * @param messageLength The length of the message array.
+ */
+ public boolean respondToDataPathRequest(short transactionId, boolean accept, int ndpId,
+ String interfaceName, byte[] message, int
+ messageLength) {
+ if (VDBG) {
+ Log.v(TAG, "respondToDataPathRequest: transactionId=" + transactionId + ", accept="
+ + accept + ", int ndpId=" + ndpId + ", interfaceName=" + interfaceName + ", "
+ + "messageLength=" + messageLength);
+ }
+
+ if (isNanInit()) {
+ int ret;
+ synchronized (WifiNative.sLock) {
+ ret = respondToDataPathRequestNative(transactionId, WifiNative.class, WifiNative
+ .sWlan0Index, accept, ndpId, interfaceName, message, messageLength);
+ }
+ if (ret != WIFI_SUCCESS) {
+ Log.w(TAG,
+ "respondToDataPathRequestNative: HAL API returned non-success -- " + ret);
+ }
+ return ret == WIFI_SUCCESS;
+ } else {
+ Log.w(TAG, "respondToDataPathRequest: cannot initialize NAN");
+ return false;
+ }
+ }
+
+ private static native int endDataPathNative(short transactionId, Class<WifiNative> cls,
+ int iface, int ndpId);
+
+ /**
+ * Terminate an existing data-path (does not delete the interface).
+ *
+ * @param transactionId Transaction ID for the transaction - used in the async callback to
+ * match with the original request.
+ * @param ndpId The NDP (NAN data path) ID to be terminated.
+ */
+ public boolean endDataPath(short transactionId, int ndpId) {
+ if (VDBG) {
+ Log.v(TAG, "endDataPath: transactionId=" + transactionId + ", ndpId=" + ndpId);
+ }
+
+ if (isNanInit()) {
+ int ret;
+ synchronized (WifiNative.sLock) {
+ ret = endDataPathNative(transactionId, WifiNative.class, WifiNative.sWlan0Index,
+ ndpId);
+ }
+ if (ret != WIFI_SUCCESS) {
+ Log.w(TAG, "endDataPathNative: HAL API returned non-success -- " + ret);
+ }
+ return ret == WIFI_SUCCESS;
+ } else {
+ Log.w(TAG, "endDataPath: cannot initialize NAN");
+ return false;
+ }
}
// EVENTS
@@ -327,9 +617,16 @@
public static final int NAN_RESPONSE_TRANSMIT_FOLLOWUP = 4;
public static final int NAN_RESPONSE_SUBSCRIBE = 5;
public static final int NAN_RESPONSE_SUBSCRIBE_CANCEL = 6;
+ public static final int NAN_RESPONSE_CONFIG = 8;
public static final int NAN_RESPONSE_GET_CAPABILITIES = 12;
+ public static final int NAN_RESPONSE_DP_INTERFACE_CREATE = 13;
+ public static final int NAN_RESPONSE_DP_INTERFACE_DELETE = 14;
+ public static final int NAN_RESPONSE_DP_INITIATOR_RESPONSE = 15;
+ public static final int NAN_RESPONSE_DP_RESPONDER_RESPONSE = 16;
+ public static final int NAN_RESPONSE_DP_END = 17;
// direct copy from wifi_nan.h: need to keep in sync
+ /* NAN Protocol Response Codes */
public static final int NAN_STATUS_SUCCESS = 0;
public static final int NAN_STATUS_TIMEOUT = 1;
public static final int NAN_STATUS_DE_FAILURE = 2;
@@ -350,8 +647,14 @@
public static final int NAN_STATUS_INVALID_TLV_VALUE = 17;
public static final int NAN_STATUS_INVALID_TX_PRIORITY = 18;
public static final int NAN_STATUS_INVALID_CONNECTION_MAP = 19;
+ public static final int NAN_STATUS_INVALID_TCA_ID = 20;
+ public static final int NAN_STATUS_INVALID_STATS_ID = 21;
+ public static final int NAN_STATUS_NAN_NOT_ALLOWED = 22;
+ public static final int NAN_STATUS_NO_OTA_ACK = 23;
+ public static final int NAN_STATUS_TX_FAIL = 24;
+ public static final int NAN_STATUS_ALREADY_ENABLED = 25;
- // NAN Configuration Response codes
+ /* NAN Configuration Response codes */
public static final int NAN_STATUS_INVALID_RSSI_CLOSE_VALUE = 4096;
public static final int NAN_STATUS_INVALID_RSSI_MIDDLE_VALUE = 4097;
public static final int NAN_STATUS_INVALID_HOP_COUNT_LIMIT = 4098;
@@ -374,8 +677,12 @@
public static final int NAN_STATUS_INVALID_POST_NAN_DISCOVERY_BITMAP_VALUE = 4115;
public static final int NAN_STATUS_MISSING_FUTHER_AVAILABILITY_MAP = 4116;
public static final int NAN_STATUS_INVALID_BAND_CONFIG_FLAGS = 4117;
+ public static final int NAN_STATUS_INVALID_RANDOM_FACTOR_UPDATE_TIME_VALUE = 4118;
+ public static final int NAN_STATUS_INVALID_ONGOING_SCAN_PERIOD = 4119;
+ public static final int NAN_STATUS_INVALID_DW_INTERVAL_VALUE = 4120;
+ public static final int NAN_STATUS_INVALID_DB_INTERVAL_VALUE = 4121;
- // publish/subscribe termination reasons
+ /* publish/subscribe termination reasons */
public static final int NAN_TERMINATED_REASON_INVALID = 8192;
public static final int NAN_TERMINATED_REASON_TIMEOUT = 8193;
public static final int NAN_TERMINATED_REASON_USER_REQUEST = 8194;
@@ -387,31 +694,34 @@
public static final int NAN_TERMINATED_REASON_POST_DISC_LEN_EXCEEDED = 8200;
public static final int NAN_TERMINATED_REASON_FURTHER_AVAIL_MAP_EMPTY = 8201;
- private static int translateHalStatusToPublicStatus(int halStatus) {
+ /* 9000-9500 NDP Status type */
+ public static final int NAN_STATUS_NDP_UNSUPPORTED_CONCURRENCY = 9000;
+ public static final int NAN_STATUS_NDP_NAN_DATA_IFACE_CREATE_FAILED = 9001;
+ public static final int NAN_STATUS_NDP_NAN_DATA_IFACE_DELETE_FAILED = 9002;
+ public static final int NAN_STATUS_NDP_DATA_INITIATOR_REQUEST_FAILED = 9003;
+ public static final int NAN_STATUS_NDP_DATA_RESPONDER_REQUEST_FAILED = 9004;
+ public static final int NAN_STATUS_NDP_INVALID_SERVICE_INSTANCE_ID = 9005;
+ public static final int NAN_STATUS_NDP_INVALID_NDP_INSTANCE_ID = 9006;
+ public static final int NAN_STATUS_NDP_INVALID_RESPONSE_CODE = 9007;
+ public static final int NAN_STATUS_NDP_INVALID_APP_INFO_LEN = 9008;
+
+ /* OTA failures and timeouts during negotiation */
+ public static final int NAN_STATUS_NDP_MGMT_FRAME_REQUEST_FAILED = 9009;
+ public static final int NAN_STATUS_NDP_MGMT_FRAME_RESPONSE_FAILED = 9010;
+ public static final int NAN_STATUS_NDP_MGMT_FRAME_CONFIRM_FAILED = 9011;
+ public static final int NAN_STATUS_NDP_END_FAILED = 9012;
+ public static final int NAN_STATUS_NDP_MGMT_FRAME_END_REQUEST_FAILED = 9013;
+
+ /* 9500 onwards vendor specific error codes */
+ public static final int NAN_STATUS_NDP_VENDOR_SPECIFIC_ERROR = 9500;
+
+ private static int translateHalStatusToNanEventCallbackReason(int halStatus) {
switch (halStatus) {
- case NAN_STATUS_NO_SPACE_AVAILABLE:
- return WifiNanSessionListener.FAIL_REASON_NO_RESOURCES;
-
- case NAN_STATUS_TIMEOUT:
- case NAN_STATUS_DE_FAILURE:
- case NAN_STATUS_DISABLE_IN_PROGRESS:
- return WifiNanSessionListener.FAIL_REASON_OTHER;
-
- case NAN_STATUS_INVALID_MSG_VERSION:
- case NAN_STATUS_INVALID_MSG_LEN:
- case NAN_STATUS_INVALID_MSG_ID:
- case NAN_STATUS_INVALID_HANDLE:
- case NAN_STATUS_INVALID_PUBLISH_TYPE:
- case NAN_STATUS_INVALID_TX_TYPE:
- case NAN_STATUS_INVALID_MATCH_ALGORITHM:
- case NAN_STATUS_INVALID_TLV_LEN:
- case NAN_STATUS_INVALID_TLV_TYPE:
- case NAN_STATUS_MISSING_TLV_TYPE:
- case NAN_STATUS_INVALID_TOTAL_TLVS_LEN:
- case NAN_STATUS_INVALID_MATCH_HANDLE:
- case NAN_STATUS_INVALID_TLV_VALUE:
- case NAN_STATUS_INVALID_TX_PRIORITY:
- case NAN_STATUS_INVALID_CONNECTION_MAP:
+ case NAN_STATUS_SUCCESS:
+ /*
+ * TODO: b/27914592 all of these codes will be cleaned-up/reduced.
+ */
+ return WifiNanEventCallback.REASON_OTHER;
case NAN_STATUS_INVALID_RSSI_CLOSE_VALUE:
case NAN_STATUS_INVALID_RSSI_MIDDLE_VALUE:
case NAN_STATUS_INVALID_HOP_COUNT_LIMIT:
@@ -434,13 +744,22 @@
case NAN_STATUS_INVALID_POST_NAN_DISCOVERY_BITMAP_VALUE:
case NAN_STATUS_MISSING_FUTHER_AVAILABILITY_MAP:
case NAN_STATUS_INVALID_BAND_CONFIG_FLAGS:
- return WifiNanSessionListener.FAIL_REASON_INVALID_ARGS;
+ case NAN_STATUS_INVALID_RANDOM_FACTOR_UPDATE_TIME_VALUE:
+ case NAN_STATUS_INVALID_ONGOING_SCAN_PERIOD:
+ case NAN_STATUS_INVALID_DW_INTERVAL_VALUE:
+ case NAN_STATUS_INVALID_DB_INTERVAL_VALUE:
+ return WifiNanEventCallback.REASON_INVALID_ARGS;
+ }
- // publish/subscribe termination reasons
+ return WifiNanEventCallback.REASON_OTHER;
+ }
+
+ private static int translateHalStatusToNanSessionCallbackTerminate(int halStatus) {
+ switch (halStatus) {
case NAN_TERMINATED_REASON_TIMEOUT:
case NAN_TERMINATED_REASON_USER_REQUEST:
case NAN_TERMINATED_REASON_COUNT_REACHED:
- return WifiNanSessionListener.TERMINATE_REASON_DONE;
+ return WifiNanSessionCallback.TERMINATE_REASON_DONE;
case NAN_TERMINATED_REASON_INVALID:
case NAN_TERMINATED_REASON_FAILURE:
@@ -449,10 +768,46 @@
case NAN_TERMINATED_REASON_POST_DISC_ATTR_EXPIRED:
case NAN_TERMINATED_REASON_POST_DISC_LEN_EXCEEDED:
case NAN_TERMINATED_REASON_FURTHER_AVAIL_MAP_EMPTY:
- return WifiNanSessionListener.TERMINATE_REASON_FAIL;
+ return WifiNanSessionCallback.TERMINATE_REASON_FAIL;
}
- return WifiNanSessionListener.FAIL_REASON_OTHER;
+ return WifiNanSessionCallback.TERMINATE_REASON_FAIL;
+ }
+
+ private static int translateHalStatusToNanSessionCallbackReason(int halStatus) {
+ switch (halStatus) {
+ case NAN_STATUS_TIMEOUT:
+ case NAN_STATUS_DE_FAILURE:
+ case NAN_STATUS_INVALID_MSG_VERSION:
+ case NAN_STATUS_INVALID_MSG_LEN:
+ case NAN_STATUS_INVALID_MSG_ID:
+ case NAN_STATUS_INVALID_HANDLE:
+ return WifiNanSessionCallback.REASON_OTHER;
+ case NAN_STATUS_NO_SPACE_AVAILABLE:
+ return WifiNanSessionCallback.REASON_NO_RESOURCES;
+ case NAN_STATUS_INVALID_PUBLISH_TYPE:
+ case NAN_STATUS_INVALID_TX_TYPE:
+ case NAN_STATUS_INVALID_MATCH_ALGORITHM:
+ return WifiNanSessionCallback.REASON_INVALID_ARGS;
+ case NAN_STATUS_DISABLE_IN_PROGRESS:
+ case NAN_STATUS_INVALID_TLV_LEN:
+ case NAN_STATUS_INVALID_TLV_TYPE:
+ case NAN_STATUS_MISSING_TLV_TYPE:
+ case NAN_STATUS_INVALID_TOTAL_TLVS_LEN:
+ case NAN_STATUS_INVALID_MATCH_HANDLE:
+ case NAN_STATUS_INVALID_TLV_VALUE:
+ case NAN_STATUS_INVALID_TX_PRIORITY:
+ case NAN_STATUS_INVALID_CONNECTION_MAP:
+ case NAN_STATUS_INVALID_TCA_ID:
+ case NAN_STATUS_INVALID_STATS_ID:
+ case NAN_STATUS_NAN_NOT_ALLOWED:
+ return WifiNanSessionCallback.REASON_OTHER;
+ case NAN_STATUS_NO_OTA_ACK:
+ case NAN_STATUS_TX_FAIL:
+ return WifiNanSessionCallback.REASON_TX_FAIL;
+ }
+
+ return WifiNanSessionCallback.REASON_OTHER;
}
// callback from native
@@ -463,14 +818,17 @@
"onNanNotifyResponse: transactionId=" + transactionId + ", responseType="
+ responseType + ", status=" + status + ", value=" + value);
}
+ WifiNanStateManager stateMgr = WifiNanStateManager.getInstance();
switch (responseType) {
case NAN_RESPONSE_ENABLED:
+ /* fall through */
+ case NAN_RESPONSE_CONFIG:
if (status == NAN_STATUS_SUCCESS) {
- WifiNanStateManager.getInstance().onConfigCompleted(transactionId);
+ stateMgr.onConfigSuccessResponse(transactionId);
} else {
- WifiNanStateManager.getInstance().onConfigFailed(transactionId,
- translateHalStatusToPublicStatus(status));
+ stateMgr.onConfigFailedResponse(transactionId,
+ translateHalStatusToNanEventCallbackReason(status));
}
break;
case NAN_RESPONSE_PUBLISH_CANCEL:
@@ -481,10 +839,10 @@
break;
case NAN_RESPONSE_TRANSMIT_FOLLOWUP:
if (status == NAN_STATUS_SUCCESS) {
- WifiNanStateManager.getInstance().onMessageSendSuccess(transactionId);
+ stateMgr.onMessageSendQueuedSuccessResponse(transactionId);
} else {
- WifiNanStateManager.getInstance().onMessageSendFail(transactionId,
- translateHalStatusToPublicStatus(status));
+ stateMgr.onMessageSendQueuedFailResponse(transactionId,
+ translateHalStatusToNanSessionCallbackReason(status));
}
break;
case NAN_RESPONSE_SUBSCRIBE_CANCEL:
@@ -493,9 +851,43 @@
+ status + ", value=" + value);
}
break;
+ case NAN_RESPONSE_DP_INTERFACE_CREATE:
+ if (status != NAN_STATUS_SUCCESS) {
+ Log.e(TAG,
+ "onNanNotifyResponse: NAN_RESPONSE_DP_INTERFACE_CREATE error - status="
+ + status + ", value=" + value);
+ }
+ stateMgr.onCreateDataPathInterfaceResponse(transactionId,
+ status == NAN_STATUS_SUCCESS, status);
+ break;
+ case NAN_RESPONSE_DP_INTERFACE_DELETE:
+ if (status != NAN_STATUS_SUCCESS) {
+ Log.e(TAG,
+ "onNanNotifyResponse: NAN_RESPONSE_DP_INTERFACE_DELETE error - status="
+ + status + ", value=" + value);
+ }
+ stateMgr.onDeleteDataPathInterfaceResponse(transactionId,
+ status == NAN_STATUS_SUCCESS, status);
+ break;
+ case NAN_RESPONSE_DP_RESPONDER_RESPONSE:
+ if (status != NAN_STATUS_SUCCESS) {
+ Log.e(TAG,
+ "onNanNotifyResponse: NAN_RESPONSE_DP_RESPONDER_RESPONSE error - "
+ + "status=" + status + ", value=" + value);
+ }
+ stateMgr.onRespondToDataPathSetupRequestResponse(transactionId,
+ status == NAN_STATUS_SUCCESS, status);
+ break;
+ case NAN_RESPONSE_DP_END:
+ if (status != NAN_STATUS_SUCCESS) {
+ Log.e(TAG, "onNanNotifyResponse: NAN_RESPONSE_DP_END error - status=" + status
+ + ", value=" + value);
+ }
+ stateMgr.onEndDataPathResponse(transactionId, status == NAN_STATUS_SUCCESS,
+ status);
+ break;
default:
- WifiNanStateManager.getInstance().onUnknownTransaction(responseType, transactionId,
- translateHalStatusToPublicStatus(status));
+ Log.e(TAG, "onNanNotifyResponse: unclassified responseType=" + responseType);
break;
}
}
@@ -512,23 +904,25 @@
switch (responseType) {
case NAN_RESPONSE_PUBLISH:
if (status == NAN_STATUS_SUCCESS) {
- WifiNanStateManager.getInstance().onPublishSuccess(transactionId, pubSubId);
+ WifiNanStateManager.getInstance().onSessionConfigSuccessResponse(transactionId,
+ true, pubSubId);
} else {
- WifiNanStateManager.getInstance().onPublishFail(transactionId,
- translateHalStatusToPublicStatus(status));
+ WifiNanStateManager.getInstance().onSessionConfigFailResponse(transactionId,
+ true, translateHalStatusToNanSessionCallbackReason(status));
}
break;
case NAN_RESPONSE_SUBSCRIBE:
if (status == NAN_STATUS_SUCCESS) {
- WifiNanStateManager.getInstance().onSubscribeSuccess(transactionId, pubSubId);
+ WifiNanStateManager.getInstance().onSessionConfigSuccessResponse(transactionId,
+ false, pubSubId);
} else {
- WifiNanStateManager.getInstance().onSubscribeFail(transactionId,
- translateHalStatusToPublicStatus(status));
+ WifiNanStateManager.getInstance().onSessionConfigFailResponse(transactionId,
+ false, translateHalStatusToNanSessionCallbackReason(status));
}
break;
default:
- WifiNanStateManager.getInstance().onUnknownTransaction(responseType, transactionId,
- translateHalStatusToPublicStatus(status));
+ Log.wtf(TAG, "onNanNotifyResponsePublishSubscribe: unclassified responseType="
+ + responseType);
break;
}
}
@@ -536,18 +930,34 @@
private static void onNanNotifyResponseCapabilities(short transactionId, int status, int value,
Capabilities capabilities) {
if (VDBG) {
- Log.v(TAG, "onNanNotifyResponsePublishSubscribe: transactionId=" + transactionId
+ Log.v(TAG, "onNanNotifyResponseCapabilities: transactionId=" + transactionId
+ ", status=" + status + ", value=" + value + ", capabilities=" + capabilities);
}
if (status == NAN_STATUS_SUCCESS) {
- WifiNanStateManager.getInstance().onCapabilitiesUpdate(transactionId, capabilities);
+ WifiNanStateManager.getInstance().onCapabilitiesUpdateResponse(transactionId,
+ capabilities);
} else {
Log.e(TAG,
"onNanNotifyResponseCapabilities: error status=" + status + ", value=" + value);
}
}
+ private static void onNanNotifyResponseDataPathInitiate(short transactionId, int status,
+ int value, int ndpId) {
+ if (VDBG) {
+ Log.v(TAG,
+ "onNanNotifyResponseDataPathInitiate: transactionId=" + transactionId
+ + ", status=" + status + ", value=" + value + ", ndpId=" + ndpId);
+ }
+ if (status == NAN_STATUS_SUCCESS) {
+ WifiNanStateManager.getInstance().onInitiateDataPathResponseSuccess(transactionId,
+ ndpId);
+ } else {
+ WifiNanStateManager.getInstance().onInitiateDataPathResponseFail(transactionId, status);
+ }
+ }
+
public static final int NAN_EVENT_ID_DISC_MAC_ADDR = 0;
public static final int NAN_EVENT_ID_STARTED_CLUSTER = 1;
public static final int NAN_EVENT_ID_JOINED_CLUSTER = 2;
@@ -560,13 +970,13 @@
}
if (eventType == NAN_EVENT_ID_DISC_MAC_ADDR) {
- WifiNanStateManager.getInstance().onInterfaceAddressChange(mac);
+ WifiNanStateManager.getInstance().onInterfaceAddressChangeNotification(mac);
} else if (eventType == NAN_EVENT_ID_STARTED_CLUSTER) {
- WifiNanStateManager.getInstance()
- .onClusterChange(WifiNanClientState.CLUSTER_CHANGE_EVENT_STARTED, mac);
+ WifiNanStateManager.getInstance().onClusterChangeNotification(
+ WifiNanClientState.CLUSTER_CHANGE_EVENT_STARTED, mac);
} else if (eventType == NAN_EVENT_ID_JOINED_CLUSTER) {
- WifiNanStateManager.getInstance()
- .onClusterChange(WifiNanClientState.CLUSTER_CHANGE_EVENT_JOINED, mac);
+ WifiNanStateManager.getInstance().onClusterChangeNotification(
+ WifiNanClientState.CLUSTER_CHANGE_EVENT_JOINED, mac);
} else {
Log.w(TAG, "onDiscoveryEngineEvent: invalid eventType=" + eventType);
}
@@ -579,11 +989,12 @@
if (VDBG) {
Log.v(TAG, "onMatchEvent: pubSubId=" + pubSubId + ", requestorInstanceId="
+ requestorInstanceId + ", mac=" + String.valueOf(HexEncoding.encode(mac))
- + ", serviceSpecificInfo=" + serviceSpecificInfo + ", matchFilterLength="
- + matchFilterLength + ", matchFilter=" + matchFilter);
+ + ", serviceSpecificInfo=" + Arrays.toString(serviceSpecificInfo)
+ + ", matchFilterLength=" + matchFilterLength + ", matchFilter="
+ + Arrays.toString(matchFilter));
}
- WifiNanStateManager.getInstance().onMatch(pubSubId, requestorInstanceId, mac,
+ WifiNanStateManager.getInstance().onMatchNotification(pubSubId, requestorInstanceId, mac,
serviceSpecificInfo, serviceSpecificInfoLength, matchFilter, matchFilterLength);
}
@@ -591,8 +1002,8 @@
private static void onPublishTerminated(int publishId, int status) {
if (VDBG) Log.v(TAG, "onPublishTerminated: publishId=" + publishId + ", status=" + status);
- WifiNanStateManager.getInstance().onPublishTerminated(publishId,
- translateHalStatusToPublicStatus(status));
+ WifiNanStateManager.getInstance().onSessionTerminatedNotification(publishId,
+ translateHalStatusToNanSessionCallbackTerminate(status), true);
}
// callback from native
@@ -601,8 +1012,8 @@
Log.v(TAG, "onSubscribeTerminated: subscribeId=" + subscribeId + ", status=" + status);
}
- WifiNanStateManager.getInstance().onSubscribeTerminated(subscribeId,
- translateHalStatusToPublicStatus(status));
+ WifiNanStateManager.getInstance().onSessionTerminatedNotification(subscribeId,
+ translateHalStatusToNanSessionCallbackTerminate(status), false);
}
// callback from native
@@ -614,14 +1025,62 @@
+ ", messageLength=" + messageLength);
}
- WifiNanStateManager.getInstance().onMessageReceived(pubSubId, requestorInstanceId, mac,
- message, messageLength);
+ WifiNanStateManager.getInstance().onMessageReceivedNotification(pubSubId,
+ requestorInstanceId, mac, message, messageLength);
}
// callback from native
private static void onDisabledEvent(int status) {
if (VDBG) Log.v(TAG, "onDisabledEvent: status=" + status);
- WifiNanStateManager.getInstance().onNanDown(translateHalStatusToPublicStatus(status));
+ WifiNanStateManager.getInstance()
+ .onNanDownNotification(translateHalStatusToNanEventCallbackReason(status));
+ }
+
+ // callback from native
+ private static void onTransmitFollowupEvent(short transactionId, int reason) {
+ if (VDBG) {
+ Log.v(TAG, "onTransmitFollowupEvent: transactionId=" + transactionId + ", reason="
+ + reason);
+ }
+
+ if (reason == NAN_STATUS_SUCCESS) {
+ WifiNanStateManager.getInstance().onMessageSendSuccessNotification(transactionId);
+ } else {
+ WifiNanStateManager.getInstance().onMessageSendFailNotification(transactionId,
+ translateHalStatusToNanSessionCallbackReason(reason));
+ }
+ }
+
+ private static void onDataPathRequest(int pubSubId, byte[] mac, int ndpId, byte[] message, int
+ messageLength) {
+ if (VDBG) {
+ Log.v(TAG, "onDataPathRequest: pubSubId=" + pubSubId + ", mac=" + String.valueOf(
+ HexEncoding.encode(mac)) + ", ndpId=" + ndpId + ", messageLength="
+ + messageLength);
+ }
+
+ WifiNanStateManager.getInstance()
+ .onDataPathRequestNotification(pubSubId, mac, ndpId, message, messageLength);
+ }
+
+ private static void onDataPathConfirm(int ndpId, byte[] mac, boolean accept, int reason, byte[]
+ message, int messageLength) {
+ if (VDBG) {
+ Log.v(TAG, "onDataPathConfirm: ndpId=" + ndpId + ", mac=" + String.valueOf(HexEncoding
+ .encode(mac)) + ", accept=" + accept + ", reason=" + reason + ", "
+ + "messageLength=" + messageLength);
+ }
+
+ WifiNanStateManager.getInstance()
+ .onDataPathConfirmNotification(ndpId, mac, accept, reason, message, messageLength);
+ }
+
+ private static void onDataPathEnd(int ndpId) {
+ if (VDBG) {
+ Log.v(TAG, "onDataPathEndNotification: ndpId=" + ndpId);
+ }
+
+ WifiNanStateManager.getInstance().onDataPathEndNotification(ndpId);
}
}
diff --git a/service/java/com/android/server/wifi/nan/WifiNanRttStateManager.java b/service/java/com/android/server/wifi/nan/WifiNanRttStateManager.java
new file mode 100644
index 0000000..4e15486
--- /dev/null
+++ b/service/java/com/android/server/wifi/nan/WifiNanRttStateManager.java
@@ -0,0 +1,203 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi.nan;
+
+import android.content.Context;
+import android.net.wifi.IRttManager;
+import android.net.wifi.RttManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.AsyncChannel;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+
+/**
+ * Manages interactions between the NAN and the RTT service. Duplicates some of the functionality
+ * of the RttManager.
+ */
+public class WifiNanRttStateManager {
+ private static final String TAG = "WifiNanRttStateManager";
+
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false; // STOPSHIP if true
+
+ private final SparseArray<WifiNanClientState> mPendingOperations = new SparseArray<>();
+ private AsyncChannel mAsyncChannel;
+
+ /**
+ * Initializes the connection to the RTT service.
+ */
+ public void start(Context context, Looper looper) {
+ if (VDBG) Log.v(TAG, "start()");
+
+ IBinder b = ServiceManager.getService(Context.WIFI_RTT_SERVICE);
+ IRttManager service = IRttManager.Stub.asInterface(b);
+ if (service == null) {
+ Log.e(TAG, "start(): not able to get WIFI_RTT_SERVICE");
+ return;
+ }
+
+ startWithRttService(context, looper, service);
+ }
+
+ /**
+ * Initializes the connection to the RTT service.
+ */
+ @VisibleForTesting
+ public void startWithRttService(Context context, Looper looper, IRttManager service) {
+ Messenger messenger;
+ try {
+ messenger = service.getMessenger();
+ } catch (RemoteException e) {
+ Log.e(TAG, "start(): not able to getMessenger() of WIFI_RTT_SERVICE");
+ return;
+ }
+
+ mAsyncChannel = new AsyncChannel();
+ mAsyncChannel.connect(context, new NanRttHandler(looper), messenger);
+ }
+
+ private WifiNanClientState getAndRemovePendingOperationClient(int rangingId) {
+ WifiNanClientState client = mPendingOperations.get(rangingId);
+ mPendingOperations.delete(rangingId);
+ return client;
+ }
+
+ /**
+ * Start a ranging operation for the client + peer MAC.
+ */
+ public void startRanging(int rangingId, WifiNanClientState client,
+ RttManager.RttParams[] params) {
+ if (VDBG) {
+ Log.v(TAG, "startRanging: rangingId=" + rangingId + ", parms="
+ + Arrays.toString(params));
+ }
+
+ if (mAsyncChannel == null) {
+ Log.d(TAG, "startRanging(): AsyncChannel to RTT service not configured - failing");
+ client.onRangingFailure(rangingId, RttManager.REASON_NOT_AVAILABLE,
+ "NAN service not able to configure connection to RTT service");
+ return;
+ }
+
+ mPendingOperations.put(rangingId, client);
+ RttManager.ParcelableRttParams pparams = new RttManager.ParcelableRttParams(params);
+ mAsyncChannel.sendMessage(RttManager.CMD_OP_START_RANGING, 0, rangingId, pparams);
+ }
+
+ private class NanRttHandler extends Handler {
+ NanRttHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (VDBG) Log.v(TAG, "handleMessage(): " + msg.what);
+
+ // channel configuration messages
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+ if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+ } else {
+ Log.e(TAG, "Failed to set up channel connection to RTT service");
+ mAsyncChannel = null;
+ }
+ return;
+ case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
+ /* NOP */
+ return;
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+ Log.e(TAG, "Channel connection to RTT service lost");
+ mAsyncChannel = null;
+ return;
+ }
+
+ // RTT-specific messages
+ WifiNanClientState client = getAndRemovePendingOperationClient(msg.arg2);
+ if (client == null) {
+ Log.e(TAG, "handleMessage(): RTT message (" + msg.what
+ + ") -- cannot find registered pending operation client for ID "
+ + msg.arg2);
+ return;
+ }
+
+ switch (msg.what) {
+ case RttManager.CMD_OP_SUCCEEDED: {
+ int rangingId = msg.arg2;
+ RttManager.ParcelableRttResults results = (RttManager.ParcelableRttResults)
+ msg.obj;
+ if (VDBG) {
+ Log.v(TAG, "CMD_OP_SUCCEEDED: rangingId=" + rangingId + ", results="
+ + results);
+ }
+ for (int i = 0; i < results.mResults.length; ++i) {
+ /*
+ * TODO: store peer ID rather than null in the return result.
+ */
+ results.mResults[i].bssid = null;
+ }
+ client.onRangingSuccess(rangingId, results);
+ break;
+ }
+ case RttManager.CMD_OP_FAILED: {
+ int rangingId = msg.arg2;
+ int reason = msg.arg1;
+ String description = ((Bundle) msg.obj).getString(RttManager.DESCRIPTION_KEY);
+ if (VDBG) {
+ Log.v(TAG, "CMD_OP_FAILED: rangingId=" + rangingId + ", reason=" + reason
+ + ", description=" + description);
+ }
+ client.onRangingFailure(rangingId, reason, description);
+ break;
+ }
+ case RttManager.CMD_OP_ABORTED: {
+ int rangingId = msg.arg2;
+ if (VDBG) {
+ Log.v(TAG, "CMD_OP_ABORTED: rangingId=" + rangingId);
+ }
+ client.onRangingAborted(rangingId);
+ break;
+ }
+ default:
+ Log.e(TAG, "handleMessage(): ignoring message " + msg.what);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Dump the internal state of the class.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("WifiNanRttStateManager:");
+ pw.println(" mPendingOperations: [" + mPendingOperations + "]");
+ }
+}
diff --git a/service/java/com/android/server/wifi/nan/WifiNanService.java b/service/java/com/android/server/wifi/nan/WifiNanService.java
index b5920f2..6589067 100644
--- a/service/java/com/android/server/wifi/nan/WifiNanService.java
+++ b/service/java/com/android/server/wifi/nan/WifiNanService.java
@@ -21,6 +21,10 @@
import com.android.server.SystemService;
+/**
+ * Service implementing Wi-Fi NAN functionality. Delegates actual interface
+ * implementation to WifiNanServiceImpl.
+ */
public final class WifiNanService extends SystemService {
private static final String TAG = "WifiNanService";
final WifiNanServiceImpl mImpl;
@@ -40,6 +44,8 @@
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
mImpl.start();
+ } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
+ mImpl.startLate();
}
}
}
diff --git a/service/java/com/android/server/wifi/nan/WifiNanServiceImpl.java b/service/java/com/android/server/wifi/nan/WifiNanServiceImpl.java
index deefe94..140c6fd 100644
--- a/service/java/com/android/server/wifi/nan/WifiNanServiceImpl.java
+++ b/service/java/com/android/server/wifi/nan/WifiNanServiceImpl.java
@@ -18,24 +18,33 @@
import android.content.Context;
import android.content.pm.PackageManager;
+import android.net.wifi.RttManager;
import android.net.wifi.nan.ConfigRequest;
-import android.net.wifi.nan.IWifiNanEventListener;
+import android.net.wifi.nan.IWifiNanEventCallback;
import android.net.wifi.nan.IWifiNanManager;
-import android.net.wifi.nan.IWifiNanSessionListener;
-import android.net.wifi.nan.PublishData;
-import android.net.wifi.nan.PublishSettings;
-import android.net.wifi.nan.SubscribeData;
-import android.net.wifi.nan.SubscribeSettings;
+import android.net.wifi.nan.IWifiNanSessionCallback;
+import android.net.wifi.nan.PublishConfig;
+import android.net.wifi.nan.SubscribeConfig;
+import android.net.wifi.nan.WifiNanEventCallback;
+import android.net.wifi.nan.WifiNanSession;
import android.os.Binder;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Arrays;
+/**
+ * Implementation of the IWifiNanManager AIDL interface. Performs validity
+ * (permission and clientID-UID mapping) checks and delegates execution to the
+ * WifiNanStateManager singleton handler. Limited state to feedback which has to
+ * be provided instantly: client and session IDs.
+ */
public class WifiNanServiceImpl extends IWifiNanManager.Stub {
private static final String TAG = "WifiNanService";
private static final boolean DBG = false;
@@ -43,180 +52,336 @@
private Context mContext;
private WifiNanStateManager mStateManager;
- private final boolean mNanSupported;
private final Object mLock = new Object();
- private final SparseArray<IBinder.DeathRecipient> mDeathRecipientsByUid = new SparseArray<>();
- private int mNextNetworkRequestToken = 1;
- private int mNextSessionId = 1;
+ private final SparseArray<IBinder.DeathRecipient> mDeathRecipientsByClientId =
+ new SparseArray<>();
+ private int mNextClientId = 1;
+ private int mNextRangingId = 1;
+ private final SparseIntArray mUidByClientId = new SparseIntArray();
public WifiNanServiceImpl(Context context) {
mContext = context.getApplicationContext();
-
- mNanSupported = mContext.getPackageManager()
- .hasSystemFeature(PackageManager.FEATURE_WIFI_NAN);
- if (DBG) Log.w(TAG, "WifiNanServiceImpl: mNanSupported=" + mNanSupported);
-
mStateManager = WifiNanStateManager.getInstance();
}
+ /**
+ * Proxy for the final native call of the parent class. Enables mocking of
+ * the function.
+ */
+ public int getMockableCallingUid() {
+ return getCallingUid();
+ }
+
+ /**
+ * Start the service: allocate a new thread (for now), start the handlers of
+ * the components of the service.
+ */
public void start() {
Log.i(TAG, "Starting Wi-Fi NAN service");
- // TODO: share worker thread with other Wi-Fi handlers
+ // TODO: share worker thread with other Wi-Fi handlers (b/27924886)
HandlerThread wifiNanThread = new HandlerThread("wifiNanService");
wifiNanThread.start();
- mStateManager.start(wifiNanThread.getLooper());
+ mStateManager.start(mContext, wifiNanThread.getLooper());
+ }
+
+ /**
+ * Start/initialize portions of the service which require the boot stage to be complete.
+ */
+ public void startLate() {
+ Log.i(TAG, "Late initialization of Wi-Fi NAN service");
+
+ mStateManager.startLate();
}
@Override
- public void connect(final IBinder binder, IWifiNanEventListener listener, int events) {
+ public void enableUsage() {
enforceAccessPermission();
enforceChangePermission();
+ /*
+ * TODO: enforce additional permissions b/27696149.
+ */
- final int uid = getCallingUid();
+ mStateManager.enableUsage();
+ }
- if (VDBG) Log.v(TAG, "connect: uid=" + uid);
+ @Override
+ public void disableUsage() {
+ enforceAccessPermission();
+ enforceChangePermission();
+ /*
+ * TODO: enforce additional permissions b/27696149.
+ */
+ mStateManager.disableUsage();
+
+ /*
+ * Potential leak (b/27796984) since we keep app information here (uid,
+ * binder-link-to-death), while clearing all state information. However:
+ * (1) can't clear all information since don't have binder, (2)
+ * information will clear once app dies, (3) allows us to do security
+ * checks in the future.
+ */
+ }
+
+ @Override
+ public boolean isUsageEnabled() {
+ enforceAccessPermission();
+
+ return mStateManager.isUsageEnabled();
+ }
+
+ @Override
+ public int connect(final IBinder binder, IWifiNanEventCallback callback,
+ ConfigRequest configRequest) {
+ enforceAccessPermission();
+ enforceChangePermission();
+ if (callback == null) {
+ throw new IllegalArgumentException("Callback must not be null");
+ }
+ if (binder == null) {
+ throw new IllegalArgumentException("Binder must not be null");
+ }
+
+ if (configRequest != null) {
+ /*
+ * TODO: enforce additional permissions if configuration is
+ * non-standard (i.e. the system API). (b/27696149)
+ */
+ } else {
+ configRequest = new ConfigRequest.Builder().build();
+ }
+ configRequest.validate();
+
+ final int uid = getMockableCallingUid();
+
+ final int clientId;
+ synchronized (mLock) {
+ clientId = mNextClientId++;
+ }
+
+ if (VDBG) {
+ Log.v(TAG, "connect: uid=" + uid + ", clientId=" + clientId + ", configRequest"
+ + configRequest);
+ }
IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
- if (DBG) Log.d(TAG, "binderDied: uid=" + uid);
+ if (DBG) Log.d(TAG, "binderDied: clientId=" + clientId);
binder.unlinkToDeath(this, 0);
synchronized (mLock) {
- mDeathRecipientsByUid.delete(uid);
+ mDeathRecipientsByClientId.delete(clientId);
+ mUidByClientId.delete(clientId);
}
- mStateManager.disconnect(uid);
+ mStateManager.disconnect(clientId);
}
};
- synchronized (mLock) {
- mDeathRecipientsByUid.put(uid, dr);
- }
+
try {
binder.linkToDeath(dr, 0);
} catch (RemoteException e) {
- Log.w(TAG, "Error on linkToDeath - " + e);
+ Log.e(TAG, "Error on linkToDeath - " + e);
+ try {
+ callback.onConnectFail(WifiNanEventCallback.REASON_OTHER);
+ } catch (RemoteException e1) {
+ Log.e(TAG, "Error on onConnectFail()");
+ }
+ return 0;
}
- mStateManager.connect(uid, listener, events);
+ synchronized (mLock) {
+ mDeathRecipientsByClientId.put(clientId, dr);
+ mUidByClientId.put(clientId, uid);
+ }
+
+ mStateManager.connect(clientId, uid, callback, configRequest);
+
+ return clientId;
}
@Override
- public void disconnect(IBinder binder) {
+ public void disconnect(int clientId, IBinder binder) {
enforceAccessPermission();
enforceChangePermission();
- int uid = getCallingUid();
+ int uid = getMockableCallingUid();
+ enforceClientValidity(uid, clientId);
+ if (VDBG) Log.v(TAG, "disconnect: uid=" + uid + ", clientId=" + clientId);
- if (VDBG) Log.v(TAG, "disconnect: uid=" + uid);
+ if (binder == null) {
+ throw new IllegalArgumentException("Binder must not be null");
+ }
synchronized (mLock) {
- IBinder.DeathRecipient dr = mDeathRecipientsByUid.get(uid);
+ IBinder.DeathRecipient dr = mDeathRecipientsByClientId.get(clientId);
if (dr != null) {
binder.unlinkToDeath(dr, 0);
- mDeathRecipientsByUid.delete(uid);
+ mDeathRecipientsByClientId.delete(clientId);
}
+ mUidByClientId.delete(clientId);
}
- mStateManager.disconnect(uid);
+ mStateManager.disconnect(clientId);
}
@Override
- public void requestConfig(ConfigRequest configRequest) {
+ public void terminateSession(int clientId, int sessionId) {
enforceAccessPermission();
enforceChangePermission();
+ int uid = getMockableCallingUid();
+ enforceClientValidity(uid, clientId);
+ if (VDBG) {
+ Log.v(TAG, "terminateSession: sessionId=" + sessionId + ", uid=" + uid + ", clientId="
+ + clientId);
+ }
+
+ mStateManager.terminateSession(clientId, sessionId);
+ }
+
+ @Override
+ public void publish(int clientId, PublishConfig publishConfig,
+ IWifiNanSessionCallback callback) {
+ enforceAccessPermission();
+ enforceChangePermission();
+
+ if (callback == null) {
+ throw new IllegalArgumentException("Callback must not be null");
+ }
+ if (publishConfig == null) {
+ throw new IllegalArgumentException("PublishConfig must not be null");
+ }
+ publishConfig.validate();
+
+ int uid = getMockableCallingUid();
+ enforceClientValidity(uid, clientId);
+ if (VDBG) {
+ Log.v(TAG, "publish: uid=" + uid + ", clientId=" + clientId + ", publishConfig="
+ + publishConfig + ", callback=" + callback);
+ }
+
+ mStateManager.publish(clientId, publishConfig, callback);
+ }
+
+ @Override
+ public void updatePublish(int clientId, int sessionId, PublishConfig publishConfig) {
+ enforceAccessPermission();
+ enforceChangePermission();
+
+ if (publishConfig == null) {
+ throw new IllegalArgumentException("PublishConfig must not be null");
+ }
+ publishConfig.validate();
+
+ int uid = getMockableCallingUid();
+ enforceClientValidity(uid, clientId);
+ if (VDBG) {
+ Log.v(TAG, "updatePublish: uid=" + uid + ", clientId=" + clientId + ", sessionId="
+ + sessionId + ", config=" + publishConfig);
+ }
+
+ mStateManager.updatePublish(clientId, sessionId, publishConfig);
+ }
+
+ @Override
+ public void subscribe(int clientId, SubscribeConfig subscribeConfig,
+ IWifiNanSessionCallback callback) {
+ enforceAccessPermission();
+ enforceChangePermission();
+
+ if (callback == null) {
+ throw new IllegalArgumentException("Callback must not be null");
+ }
+ if (subscribeConfig == null) {
+ throw new IllegalArgumentException("SubscribeConfig must not be null");
+ }
+ subscribeConfig.validate();
+
+ int uid = getMockableCallingUid();
+ enforceClientValidity(uid, clientId);
+ if (VDBG) {
+ Log.v(TAG, "subscribe: uid=" + uid + ", clientId=" + clientId + ", config="
+ + subscribeConfig + ", callback=" + callback);
+ }
+
+ mStateManager.subscribe(clientId, subscribeConfig, callback);
+ }
+
+ @Override
+ public void updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig) {
+ enforceAccessPermission();
+ enforceChangePermission();
+
+ if (subscribeConfig == null) {
+ throw new IllegalArgumentException("SubscribeConfig must not be null");
+ }
+ subscribeConfig.validate();
+
+ int uid = getMockableCallingUid();
+ enforceClientValidity(uid, clientId);
+ if (VDBG) {
+ Log.v(TAG, "updateSubscribe: uid=" + uid + ", clientId=" + clientId + ", sessionId="
+ + sessionId + ", config=" + subscribeConfig);
+ }
+
+ mStateManager.updateSubscribe(clientId, sessionId, subscribeConfig);
+ }
+
+ @Override
+ public void sendMessage(int clientId, int sessionId, int peerId, byte[] message,
+ int messageLength, int messageId, int retryCount) {
+ enforceAccessPermission();
+ enforceChangePermission();
+
+ if (messageLength != 0 && (message == null || message.length < messageLength)) {
+ throw new IllegalArgumentException(
+ "Non-matching combination of message and messageLength");
+ }
+ if (retryCount < 0 || retryCount > WifiNanSession.MAX_SEND_RETRY_COUNT) {
+ throw new IllegalArgumentException("Invalid 'retryCount' must be non-negative "
+ + "and <= WifiNanSession.MAX_SEND_RETRY_COUNT");
+ }
+
+ int uid = getMockableCallingUid();
+ enforceClientValidity(uid, clientId);
if (VDBG) {
Log.v(TAG,
- "requestConfig: uid=" + getCallingUid() + ", configRequest=" + configRequest);
+ "sendMessage: sessionId=" + sessionId + ", uid=" + uid + ", clientId="
+ + clientId + ", peerId=" + peerId + ", messageLength=" + messageLength
+ + ", messageId=" + messageId + ", retryCount=" + retryCount);
}
- mStateManager.requestConfig(getCallingUid(), configRequest);
+ mStateManager.sendMessage(clientId, sessionId, peerId, message, messageLength, messageId,
+ retryCount);
}
@Override
- public void stopSession(int sessionId) {
+ public int startRanging(int clientId, int sessionId, RttManager.ParcelableRttParams params) {
enforceAccessPermission();
- enforceChangePermission();
+ enforceLocationPermission();
- if (VDBG) Log.v(TAG, "stopSession: sessionId=" + sessionId + ", uid=" + getCallingUid());
+ int uid = getMockableCallingUid();
+ enforceClientValidity(uid, clientId);
+ if (VDBG) {
+ Log.v(TAG, "startRanging: clientId=" + clientId + ", sessionId=" + sessionId + ", "
+ + ", parms=" + Arrays.toString(params.mParams));
+ }
- mStateManager.stopSession(getCallingUid(), sessionId);
- }
+ if (params.mParams.length == 0) {
+ throw new IllegalArgumentException("Empty ranging parameters");
+ }
- @Override
- public void destroySession(int sessionId) {
- enforceAccessPermission();
- enforceChangePermission();
-
- if (VDBG) Log.v(TAG, "destroySession: sessionId=" + sessionId + ", uid=" + getCallingUid());
-
- mStateManager.destroySession(getCallingUid(), sessionId);
- }
-
- @Override
- public int createSession(IWifiNanSessionListener listener, int events) {
- enforceAccessPermission();
- enforceChangePermission();
-
- if (VDBG) Log.v(TAG, "createSession: uid=" + getCallingUid());
-
- int sessionId;
+ int rangingId;
synchronized (mLock) {
- sessionId = mNextSessionId++;
+ rangingId = mNextRangingId++;
}
-
- mStateManager.createSession(getCallingUid(), sessionId, listener, events);
-
- return sessionId;
- }
-
- @Override
- public void publish(int sessionId, PublishData publishData, PublishSettings publishSettings) {
- enforceAccessPermission();
- enforceChangePermission();
-
- if (VDBG) {
- Log.v(TAG, "publish: uid=" + getCallingUid() + ", sessionId=" + sessionId + ", data='"
- + publishData + "', settings=" + publishSettings);
- }
-
- mStateManager.publish(getCallingUid(), sessionId, publishData, publishSettings);
- }
-
- @Override
- public void subscribe(int sessionId, SubscribeData subscribeData,
- SubscribeSettings subscribeSettings) {
- enforceAccessPermission();
- enforceChangePermission();
-
- if (VDBG) {
- Log.v(TAG, "subscribe: uid=" + getCallingUid() + ", sessionId=" + sessionId + ", data='"
- + subscribeData + "', settings=" + subscribeSettings);
- }
-
- mStateManager.subscribe(getCallingUid(), sessionId, subscribeData, subscribeSettings);
- }
-
- @Override
- public void sendMessage(int sessionId, int peerId, byte[] message, int messageLength,
- int messageId) {
- enforceAccessPermission();
- enforceChangePermission();
-
- if (VDBG) {
- Log.v(TAG,
- "sendMessage: sessionId=" + sessionId + ", uid=" + getCallingUid() + ", peerId="
- + peerId + ", messageLength=" + messageLength + ", messageId="
- + messageId);
- }
-
- mStateManager.sendMessage(getCallingUid(), sessionId, peerId, message, messageLength,
- messageId);
+ mStateManager.startRanging(clientId, sessionId, params.mParams, rangingId);
+ return rangingId;
}
@Override
@@ -228,13 +393,24 @@
return;
}
pw.println("Wi-Fi NAN Service");
- pw.println(" mNanSupported: " + mNanSupported);
- pw.println(" mNextNetworkRequestToken: " + mNextNetworkRequestToken);
- pw.println(" mNextSessionId: " + mNextSessionId);
- pw.println(" mDeathRecipientsByUid: " + mDeathRecipientsByUid);
+ synchronized (mLock) {
+ pw.println(" mNextClientId: " + mNextClientId);
+ pw.println(" mDeathRecipientsByClientId: " + mDeathRecipientsByClientId);
+ pw.println(" mUidByClientId: " + mUidByClientId);
+ }
mStateManager.dump(fd, pw, args);
}
+ private void enforceClientValidity(int uid, int clientId) {
+ synchronized (mLock) {
+ int uidIndex = mUidByClientId.indexOfKey(clientId);
+ if (uidIndex < 0 || mUidByClientId.valueAt(uidIndex) != uid) {
+ throw new SecurityException("Attempting to use invalid uid+clientId mapping: uid="
+ + uid + ", clientId=" + clientId);
+ }
+ }
+ }
+
private void enforceAccessPermission() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, TAG);
}
@@ -242,4 +418,9 @@
private void enforceChangePermission() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, TAG);
}
+
+ private void enforceLocationPermission() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
+ TAG);
+ }
}
diff --git a/service/java/com/android/server/wifi/nan/WifiNanSessionState.java b/service/java/com/android/server/wifi/nan/WifiNanSessionState.java
index ea64403..56c7d3a 100644
--- a/service/java/com/android/server/wifi/nan/WifiNanSessionState.java
+++ b/service/java/com/android/server/wifi/nan/WifiNanSessionState.java
@@ -16,12 +16,10 @@
package com.android.server.wifi.nan;
-import android.net.wifi.nan.IWifiNanSessionListener;
-import android.net.wifi.nan.PublishData;
-import android.net.wifi.nan.PublishSettings;
-import android.net.wifi.nan.SubscribeData;
-import android.net.wifi.nan.SubscribeSettings;
-import android.net.wifi.nan.WifiNanSessionListener;
+import android.net.wifi.nan.IWifiNanSessionCallback;
+import android.net.wifi.nan.PublishConfig;
+import android.net.wifi.nan.SubscribeConfig;
+import android.net.wifi.nan.WifiNanSessionCallback;
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;
@@ -31,183 +29,172 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+/**
+ * Manages the state of a single NAN discovery session (publish or subscribe).
+ * Primary state consists of a callback through which session callbacks are
+ * executed as well as state related to currently active discovery sessions:
+ * publish/subscribe ID, and MAC address caching (hiding) from clients.
+ */
public class WifiNanSessionState {
private static final String TAG = "WifiNanSessionState";
private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
+ private int mSessionId;
+ private int mPubSubId;
+ private IWifiNanSessionCallback mCallback;
+ private boolean mIsPublishSession;
+
private final SparseArray<String> mMacByRequestorInstanceId = new SparseArray<>();
- private int mSessionId;
- private IWifiNanSessionListener mListener;
- private int mEvents;
-
- private boolean mPubSubIdValid = false;
- private int mPubSubId;
-
- private static final int SESSION_TYPE_NOT_INIT = 0;
- private static final int SESSION_TYPE_PUBLISH = 1;
- private static final int SESSION_TYPE_SUBSCRIBE = 2;
- private int mSessionType = SESSION_TYPE_NOT_INIT;
-
- public WifiNanSessionState(int sessionId, IWifiNanSessionListener listener, int events) {
+ public WifiNanSessionState(int sessionId, int pubSubId, IWifiNanSessionCallback callback,
+ boolean isPublishSession) {
mSessionId = sessionId;
- mListener = listener;
- mEvents = events;
- }
-
- public void destroy() {
- stop(WifiNanStateManager.getInstance().createNextTransactionId());
- if (mPubSubIdValid) {
- mMacByRequestorInstanceId.clear();
- mListener = null;
- mPubSubIdValid = false;
- }
+ mPubSubId = pubSubId;
+ mCallback = callback;
+ mIsPublishSession = isPublishSession;
}
public int getSessionId() {
return mSessionId;
}
+ public int getPubSubId() {
+ return mPubSubId;
+ }
+
+ public IWifiNanSessionCallback getCallback() {
+ return mCallback;
+ }
+
+ /**
+ * Return the MAC address (String) of the specified peer ID - or a null if no such address 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;
+ }
+
+ /**
+ * Destroy the current discovery session - stops publishing or subscribing
+ * if currently active.
+ */
+ public void terminate() {
+ mCallback = null;
+
+ if (mIsPublishSession) {
+ WifiNanNative.getInstance().stopPublish((short) 0, mPubSubId);
+ } else {
+ WifiNanNative.getInstance().stopSubscribe((short) 0, mPubSubId);
+ }
+ }
+
+ /**
+ * Indicates whether the publish/subscribe ID (a HAL ID) corresponds to this
+ * session.
+ *
+ * @param pubSubId The publish/subscribe HAL ID to be tested.
+ * @return true if corresponds to this session, false otherwise.
+ */
public boolean isPubSubIdSession(int pubSubId) {
- return mPubSubIdValid && mPubSubId == pubSubId;
+ return mPubSubId == pubSubId;
}
- public void publish(short transactionId, PublishData data, PublishSettings settings) {
- if (mSessionType == SESSION_TYPE_SUBSCRIBE) {
- throw new IllegalStateException("A SUBSCRIBE session is being used for publish");
+ /**
+ * Modify a publish discovery session.
+ *
+ * @param transactionId Transaction ID for the transaction - used in the
+ * async callback to match with the original request.
+ * @param config Configuration of the publish session.
+ */
+ public boolean updatePublish(short transactionId, PublishConfig config) {
+ if (!mIsPublishSession) {
+ Log.e(TAG, "A SUBSCRIBE session is being used to publish");
+ try {
+ mCallback.onSessionConfigFail(WifiNanSessionCallback.REASON_OTHER);
+ } catch (RemoteException e) {
+ Log.e(TAG, "updatePublish: RemoteException=" + e);
+ }
+ return false;
}
- mSessionType = SESSION_TYPE_PUBLISH;
- WifiNanNative.getInstance().publish(transactionId, mPubSubIdValid ? mPubSubId : 0, data,
- settings);
+ return WifiNanNative.getInstance().publish(transactionId, mPubSubId, config);
}
- public void subscribe(short transactionId, SubscribeData data, SubscribeSettings settings) {
- if (mSessionType == SESSION_TYPE_PUBLISH) {
- throw new IllegalStateException("A PUBLISH session is being used for publish");
+ /**
+ * Modify a subscribe discovery session.
+ *
+ * @param transactionId Transaction ID for the transaction - used in the
+ * async callback to match with the original request.
+ * @param config Configuration of the subscribe session.
+ */
+ public boolean updateSubscribe(short transactionId, SubscribeConfig config) {
+ if (mIsPublishSession) {
+ Log.e(TAG, "A PUBLISH session is being used to subscribe");
+ try {
+ mCallback.onSessionConfigFail(WifiNanSessionCallback.REASON_OTHER);
+ } catch (RemoteException e) {
+ Log.e(TAG, "updateSubscribe: RemoteException=" + e);
+ }
+ return false;
}
- mSessionType = SESSION_TYPE_SUBSCRIBE;
- WifiNanNative.getInstance().subscribe(transactionId, mPubSubIdValid ? mPubSubId : 0, data,
- settings);
+ return WifiNanNative.getInstance().subscribe(transactionId, mPubSubId, config);
}
- public void sendMessage(short transactionId, int peerId, byte[] message, int messageLength,
+ /**
+ * Send a message to a peer which is part of a discovery session.
+ *
+ * @param transactionId Transaction ID for the transaction - used in the
+ * async callback to match with the original request.
+ * @param peerId ID of the peer. Obtained through previous communication (a
+ * match indication).
+ * @param message Message byte array to send to the peer.
+ * @param messageLength Length of the message byte array.
+ * @param messageId A message ID provided by caller to be used in any
+ * callbacks related to the message (success/failure).
+ */
+ public boolean sendMessage(short transactionId, int peerId, byte[] message, int messageLength,
int messageId) {
- if (!mPubSubIdValid) {
- Log.e(TAG, "sendMessage: attempting to send a message on a non-live session "
- + "(no successful publish or subscribe");
- onMessageSendFail(messageId, WifiNanSessionListener.FAIL_REASON_NO_MATCH_SESSION);
- return;
- }
-
String peerMacStr = mMacByRequestorInstanceId.get(peerId);
if (peerMacStr == null) {
Log.e(TAG, "sendMessage: attempting to send a message to an address which didn't "
+ "match/contact us");
- onMessageSendFail(messageId, WifiNanSessionListener.FAIL_REASON_NO_MATCH_SESSION);
- return;
+ try {
+ mCallback.onMessageSendFail(messageId,
+ WifiNanSessionCallback.REASON_NO_MATCH_SESSION);
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendMessage: RemoteException=" + e);
+ }
+ return false;
}
byte[] peerMac = HexEncoding.decode(peerMacStr.toCharArray(), false);
- WifiNanNative.getInstance().sendMessage(transactionId, mPubSubId, peerId, peerMac, message,
- messageLength);
+ return WifiNanNative.getInstance().sendMessage(transactionId, mPubSubId, peerId, peerMac,
+ message, messageLength, messageId);
}
- public void stop(short transactionId) {
- if (!mPubSubIdValid || mSessionType == SESSION_TYPE_NOT_INIT) {
- Log.e(TAG, "sendMessage: attempting to stop pub/sub on a non-live session (no "
- + "successful publish or subscribe");
- return;
- }
-
- if (mSessionType == SESSION_TYPE_PUBLISH) {
- WifiNanNative.getInstance().stopPublish(transactionId, mPubSubId);
- } else if (mSessionType == SESSION_TYPE_SUBSCRIBE) {
- WifiNanNative.getInstance().stopSubscribe(transactionId, mPubSubId);
- }
- }
-
- public void onPublishSuccess(int publishId) {
- mPubSubId = publishId;
- mPubSubIdValid = true;
- }
-
- public void onPublishFail(int status) {
- mPubSubIdValid = false;
- try {
- if (mListener != null && (mEvents & WifiNanSessionListener.LISTEN_PUBLISH_FAIL) != 0) {
- mListener.onPublishFail(status);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "onPublishFail: RemoteException (FYI): " + e);
- }
- }
-
- public void onPublishTerminated(int status) {
- mPubSubIdValid = false;
- try {
- if (mListener != null
- && (mEvents & WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED) != 0) {
- mListener.onPublishTerminated(status);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "onPublishTerminated: RemoteException (FYI): " + e);
- }
- }
-
- public void onSubscribeSuccess(int subscribeId) {
- mPubSubId = subscribeId;
- mPubSubIdValid = true;
- }
-
- public void onSubscribeFail(int status) {
- mPubSubIdValid = false;
- try {
- if (mListener != null
- && (mEvents & WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL) != 0) {
- mListener.onSubscribeFail(status);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "onSubscribeFail: RemoteException (FYI): " + e);
- }
- }
-
- public void onSubscribeTerminated(int status) {
- mPubSubIdValid = false;
- try {
- if (mListener != null
- && (mEvents & WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED) != 0) {
- mListener.onSubscribeTerminated(status);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "onSubscribeTerminated: RemoteException (FYI): " + e);
- }
- }
-
- public void onMessageSendSuccess(int messageId) {
- try {
- if (mListener != null
- && (mEvents & WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS) != 0) {
- mListener.onMessageSendSuccess(messageId);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "onMessageSendSuccess: RemoteException (FYI): " + e);
- }
- }
-
- public void onMessageSendFail(int messageId, int status) {
- try {
- if (mListener != null
- && (mEvents & WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL) != 0) {
- mListener.onMessageSendFail(messageId, status);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "onMessageSendFail: RemoteException (FYI): " + e);
- }
- }
-
+ /**
+ * Callback from HAL when a discovery occurs - i.e. when a match to an
+ * active subscription request or to a solicited publish request occurs.
+ * Propagates to client if registered.
+ *
+ * @param requestorInstanceId The ID used to identify the peer in this
+ * matched session.
+ * @param peerMac The MAC address of the peer. Never propagated to client
+ * due to privacy concerns.
+ * @param serviceSpecificInfo Information from the discovery advertisement
+ * (usually not used in the match decisions).
+ * @param serviceSpecificInfoLength Length of the above information field.
+ * @param matchFilter The filter from the discovery advertisement (which was
+ * used in the match decision).
+ * @param matchFilterLength Length of the above filter field.
+ */
public void onMatch(int requestorInstanceId, byte[] peerMac, byte[] serviceSpecificInfo,
int serviceSpecificInfoLength, byte[] matchFilter, int matchFilterLength) {
String prevMac = mMacByRequestorInstanceId.get(requestorInstanceId);
@@ -216,15 +203,24 @@
if (DBG) Log.d(TAG, "onMatch: previous peer MAC replaced - " + prevMac);
try {
- if (mListener != null && (mEvents & WifiNanSessionListener.LISTEN_MATCH) != 0) {
- mListener.onMatch(requestorInstanceId, serviceSpecificInfo,
- serviceSpecificInfoLength, matchFilter, matchFilterLength);
- }
+ mCallback.onMatch(requestorInstanceId, serviceSpecificInfo, serviceSpecificInfoLength,
+ matchFilter, matchFilterLength);
} catch (RemoteException e) {
Log.w(TAG, "onMatch: RemoteException (FYI): " + e);
}
}
+ /**
+ * Callback from HAL when a message is received from a peer in a discovery
+ * session. Propagated to client if registered.
+ *
+ * @param requestorInstanceId An ID used to identify the peer.
+ * @param peerMac The MAC address of the peer sending the message. This
+ * information is never propagated to the client due to privacy
+ * concerns.
+ * @param message The received message.
+ * @param messageLength The length of the received message.
+ */
public void onMessageReceived(int requestorInstanceId, byte[] peerMac, byte[] message,
int messageLength) {
String prevMac = mMacByRequestorInstanceId.get(requestorInstanceId);
@@ -235,21 +231,20 @@
}
try {
- if (mListener != null
- && (mEvents & WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED) != 0) {
- mListener.onMessageReceived(requestorInstanceId, message, messageLength);
- }
+ mCallback.onMessageReceived(requestorInstanceId, message, messageLength);
} catch (RemoteException e) {
Log.w(TAG, "onMessageReceived: RemoteException (FYI): " + e);
}
}
+ /**
+ * Dump the internal state of the class.
+ */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("NanSessionState:");
pw.println(" mSessionId: " + mSessionId);
- pw.println(" mSessionType: " + mSessionType);
- pw.println(" mEvents: " + mEvents);
- pw.println(" mPubSubId: " + (mPubSubIdValid ? Integer.toString(mPubSubId) : "not valid"));
+ pw.println(" mIsPublishSession: " + mIsPublishSession);
+ pw.println(" mPubSubId: " + mPubSubId);
pw.println(" mMacByRequestorInstanceId: [" + mMacByRequestorInstanceId + "]");
}
}
diff --git a/service/java/com/android/server/wifi/nan/WifiNanStateManager.java b/service/java/com/android/server/wifi/nan/WifiNanStateManager.java
index f7bfa55..86ff2dc 100644
--- a/service/java/com/android/server/wifi/nan/WifiNanStateManager.java
+++ b/service/java/com/android/server/wifi/nan/WifiNanStateManager.java
@@ -16,71 +16,140 @@
package com.android.server.wifi.nan;
+import android.content.Context;
+import android.content.Intent;
+import android.net.wifi.RttManager;
import android.net.wifi.nan.ConfigRequest;
-import android.net.wifi.nan.IWifiNanEventListener;
-import android.net.wifi.nan.IWifiNanSessionListener;
-import android.net.wifi.nan.PublishData;
-import android.net.wifi.nan.PublishSettings;
-import android.net.wifi.nan.SubscribeData;
-import android.net.wifi.nan.SubscribeSettings;
+import android.net.wifi.nan.IWifiNanEventCallback;
+import android.net.wifi.nan.IWifiNanSessionCallback;
+import android.net.wifi.nan.PublishConfig;
+import android.net.wifi.nan.SubscribeConfig;
+import android.net.wifi.nan.WifiNanEventCallback;
+import android.net.wifi.nan.WifiNanManager;
+import android.net.wifi.nan.WifiNanSessionCallback;
import android.os.Bundle;
-import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.MessageUtils;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+import com.android.internal.util.WakeupMessage;
+
import libcore.util.HexEncoding;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+/**
+ * Manages the state of the Wi-Fi NAN system service.
+ */
public class WifiNanStateManager {
private static final String TAG = "WifiNanStateManager";
private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
+ @VisibleForTesting
+ public static final String HAL_COMMAND_TIMEOUT_TAG = TAG + " HAL Command Timeout";
+
+ @VisibleForTesting
+ public static final String HAL_SEND_MESSAGE_TIMEOUT_TAG = TAG + " HAL Send Message Timeout";
+
+ @VisibleForTesting
+ public static final String HAL_DATA_PATH_CONFIRM_TIMEOUT_TAG =
+ TAG + " HAL Data Path Confirm Timeout";
+
private static WifiNanStateManager sNanStateManagerSingleton;
- private static final int MESSAGE_CONNECT = 0;
- private static final int MESSAGE_DISCONNECT = 1;
- private static final int MESSAGE_REQUEST_CONFIG = 4;
- private static final int MESSAGE_CREATE_SESSION = 5;
- private static final int MESSAGE_DESTROY_SESSION = 6;
- private static final int MESSAGE_PUBLISH = 7;
- private static final int MESSAGE_SUBSCRIBE = 8;
- private static final int MESSAGE_SEND_MESSAGE = 9;
- private static final int MESSAGE_STOP_SESSION = 10;
- private static final int MESSAGE_ON_CONFIG_COMPLETED = 11;
- private static final int MESSAGE_ON_CONFIG_FAILED = 12;
- private static final int MESSAGE_ON_NAN_DOWN = 13;
- private static final int MESSAGE_ON_INTERFACE_CHANGE = 14;
- private static final int MESSAGE_ON_CLUSTER_CHANGE = 15;
- private static final int MESSAGE_ON_PUBLISH_SUCCESS = 16;
- private static final int MESSAGE_ON_PUBLISH_FAIL = 17;
- private static final int MESSAGE_ON_PUBLISH_TERMINATED = 18;
- private static final int MESSAGE_ON_SUBSCRIBE_SUCCESS = 19;
- private static final int MESSAGE_ON_SUBSCRIBE_FAIL = 20;
- private static final int MESSAGE_ON_SUBSCRIBE_TERMINATED = 21;
- private static final int MESSAGE_ON_MESSAGE_SEND_SUCCESS = 22;
- private static final int MESSAGE_ON_MESSAGE_SEND_FAIL = 23;
- private static final int MESSAGE_ON_UNKNOWN_TRANSACTION = 24;
- private static final int MESSAGE_ON_MATCH = 25;
- private static final int MESSAGE_ON_MESSAGE_RECEIVED = 26;
- private static final int MESSAGE_ON_CAPABILITIES_UPDATED = 27;
+ /*
+ * State machine message types. There are sub-types for the messages (except for TIMEOUTs).
+ * Format:
+ * - Message.arg1: contains message sub-type
+ * - Message.arg2: contains transaction ID for RESPONSE & RESPONSE_TIMEOUT
+ */
+ private static final int MESSAGE_TYPE_COMMAND = 1;
+ private static final int MESSAGE_TYPE_RESPONSE = 2;
+ private static final int MESSAGE_TYPE_NOTIFICATION = 3;
+ private static final int MESSAGE_TYPE_RESPONSE_TIMEOUT = 4;
+ private static final int MESSAGE_TYPE_SEND_MESSAGE_TIMEOUT = 5;
+ private static final int MESSAGE_TYPE_DATA_PATH_TIMEOUT = 6;
+ /*
+ * Message sub-types:
+ */
+ private static final int COMMAND_TYPE_CONNECT = 100;
+ private static final int COMMAND_TYPE_DISCONNECT = 101;
+ private static final int COMMAND_TYPE_TERMINATE_SESSION = 102;
+ private static final int COMMAND_TYPE_PUBLISH = 103;
+ private static final int COMMAND_TYPE_UPDATE_PUBLISH = 104;
+ private static final int COMMAND_TYPE_SUBSCRIBE = 105;
+ private static final int COMMAND_TYPE_UPDATE_SUBSCRIBE = 106;
+ private static final int COMMAND_TYPE_ENQUEUE_SEND_MESSAGE = 107;
+ private static final int COMMAND_TYPE_ENABLE_USAGE = 108;
+ private static final int COMMAND_TYPE_DISABLE_USAGE = 109;
+ private static final int COMMAND_TYPE_START_RANGING = 110;
+ private static final int COMMAND_TYPE_GET_CAPABILITIES = 111;
+ private static final int COMMAND_TYPE_CREATE_ALL_DATA_PATH_INTERFACES = 112;
+ private static final int COMMAND_TYPE_DELETE_ALL_DATA_PATH_INTERFACES = 113;
+ private static final int COMMAND_TYPE_CREATE_DATA_PATH_INTERFACE = 114;
+ private static final int COMMAND_TYPE_DELETE_DATA_PATH_INTERFACE = 115;
+ private static final int COMMAND_TYPE_INITIATE_DATA_PATH_SETUP = 116;
+ 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 RESPONSE_TYPE_ON_CONFIG_SUCCESS = 200;
+ private static final int RESPONSE_TYPE_ON_CONFIG_FAIL = 201;
+ private static final int RESPONSE_TYPE_ON_SESSION_CONFIG_SUCCESS = 202;
+ private static final int RESPONSE_TYPE_ON_SESSION_CONFIG_FAIL = 203;
+ private static final int RESPONSE_TYPE_ON_MESSAGE_SEND_QUEUED_SUCCESS = 204;
+ private static final int RESPONSE_TYPE_ON_MESSAGE_SEND_QUEUED_FAIL = 205;
+ private static final int RESPONSE_TYPE_ON_CAPABILITIES_UPDATED = 206;
+ private static final int RESPONSE_TYPE_ON_CREATE_INTERFACE = 207;
+ private static final int RESPONSE_TYPE_ON_DELETE_INTERFACE = 208;
+ private static final int RESPONSE_TYPE_ON_INITIATE_DATA_PATH_SUCCESS = 209;
+ 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 NOTIFICATION_TYPE_INTERFACE_CHANGE = 301;
+ private static final int NOTIFICATION_TYPE_CLUSTER_CHANGE = 302;
+ private static final int NOTIFICATION_TYPE_MATCH = 303;
+ private static final int NOTIFICATION_TYPE_SESSION_TERMINATED = 304;
+ private static final int NOTIFICATION_TYPE_MESSAGE_RECEIVED = 305;
+ private static final int NOTIFICATION_TYPE_NAN_DOWN = 306;
+ private static final int NOTIFICATION_TYPE_ON_MESSAGE_SEND_SUCCESS = 307;
+ private static final int NOTIFICATION_TYPE_ON_MESSAGE_SEND_FAIL = 308;
+ private static final int NOTIFICATION_TYPE_ON_DATA_PATH_REQUEST = 309;
+ private static final int NOTIFICATION_TYPE_ON_DATA_PATH_CONFIRM = 310;
+ private static final int NOTIFICATION_TYPE_ON_DATA_PATH_END = 311;
+
+ private static final SparseArray<String> sSmToString = MessageUtils.findMessageNames(
+ new Class[]{WifiNanStateManager.class},
+ new String[]{"MESSAGE_TYPE", "COMMAND_TYPE", "RESPONSE_TYPE", "NOTIFICATION_TYPE"});
+
+ /*
+ * Keys used when passing (some) arguments to the Handler thread (too many
+ * arguments to pass in the short-cut Message members).
+ */
+ private static final String MESSAGE_BUNDLE_KEY_SESSION_TYPE = "session_type";
private static final String MESSAGE_BUNDLE_KEY_SESSION_ID = "session_id";
- private static final String MESSAGE_BUNDLE_KEY_EVENTS = "events";
- private static final String MESSAGE_BUNDLE_KEY_PUBLISH_DATA = "publish_data";
- private static final String MESSAGE_BUNDLE_KEY_PUBLISH_SETTINGS = "publish_settings";
- private static final String MESSAGE_BUNDLE_KEY_SUBSCRIBE_DATA = "subscribe_data";
- private static final String MESSAGE_BUNDLE_KEY_SUBSCRIBE_SETTINGS = "subscribe_settings";
+ private static final String MESSAGE_BUNDLE_KEY_CONFIG = "config";
private static final String MESSAGE_BUNDLE_KEY_MESSAGE = "message";
private static final String MESSAGE_BUNDLE_KEY_MESSAGE_PEER_ID = "message_peer_id";
private static final String MESSAGE_BUNDLE_KEY_MESSAGE_ID = "message_id";
- private static final String MESSAGE_BUNDLE_KEY_RESPONSE_TYPE = "response_type";
private static final String MESSAGE_BUNDLE_KEY_SSI_LENGTH = "ssi_length";
private static final String MESSAGE_BUNDLE_KEY_SSI_DATA = "ssi_data";
private static final String MESSAGE_BUNDLE_KEY_FILTER_LENGTH = "filter_length";
@@ -88,20 +157,52 @@
private static final String MESSAGE_BUNDLE_KEY_MAC_ADDRESS = "mac_address";
private static final String MESSAGE_BUNDLE_KEY_MESSAGE_DATA = "message_data";
private static final String MESSAGE_BUNDLE_KEY_MESSAGE_LENGTH = "message_length";
+ private static final String MESSAGE_BUNDLE_KEY_REQ_INSTANCE_ID = "req_instance_id";
+ private static final String MESSAGE_BUNDLE_KEY_RANGING_ID = "ranging_id";
+ private static final String MESSAGE_BUNDLE_KEY_SEND_MESSAGE_ENQUEUE_TIME = "message_queue_time";
+ private static final String MESSAGE_BUNDLE_KEY_RETRY_COUNT = "retry_count";
+ private static final String MESSAGE_BUNDLE_KEY_SUCCESS_FLAG = "success_flag";
+ private static final String MESSAGE_BUNDLE_KEY_STATUS_CODE = "status_code";
+ private static final String MESSAGE_BUNDLE_KEY_INTERFACE_NAME = "interface_name";
+ private static final String MESSAGE_BUNDLE_KEY_PUB_SUB_ID = "pub_sub_id";
+ private static final String MESSAGE_BUNDLE_KEY_CHANNEL_REQ_TYPE = "channel_request_type";
+ private static final String MESSAGE_BUNDLE_KEY_CHANNEL = "channel";
+ private static final String MESSAGE_BUNDLE_KEY_PEER_ID = "peer_id";
+ private static final String MESSAGE_BUNDLE_KEY_UID = "uid";
+ private static final String MESSAGE_BUNDLE_KEY_SENT_MESSAGE = "send_message";
+ private static final String MESSAGE_BUNDLE_KEY_MESSAGE_ARRIVAL_SEQ = "message_arrival_seq";
- private WifiNanNative.Capabilities mCapabilities;
+ /*
+ * Asynchronous access with no lock
+ */
+ private volatile boolean mUsageEnabled = false;
- private WifiNanStateHandler mHandler;
+ /*
+ * Synchronous access: state is only accessed through the state machine
+ * handler thread: no need to use a lock.
+ */
+ private Context mContext;
+ /* package */ WifiNanNative.Capabilities mCapabilities;
+ private WifiNanStateMachine mSm;
+ private WifiNanRttStateManager mRtt;
+ private WifiNanDataPathStateManager mDataPathMgr;
- // no synchronization necessary: only access through Handler
private final SparseArray<WifiNanClientState> mClients = new SparseArray<>();
- private final SparseArray<TransactionInfoBase> mPendingResponses = new SparseArray<>();
- private short mNextTransactionId = 1;
+ private ConfigRequest mCurrentNanConfiguration = null;
+
+ private static final byte[] ALL_ZERO_MAC = new byte[] {0, 0, 0, 0, 0, 0};
+ private byte[] mCurrentDiscoveryInterfaceMac = ALL_ZERO_MAC;
private WifiNanStateManager() {
// EMPTY: singleton pattern
}
+ /**
+ * Access the singleton NAN state manager. Use a singleton since need to be
+ * accessed (for now) from several other child classes.
+ *
+ * @return The state manager singleton.
+ */
public static WifiNanStateManager getInstance() {
if (sNanStateManagerSingleton == null) {
sNanStateManagerSingleton = new WifiNanStateManager();
@@ -110,384 +211,810 @@
return sNanStateManagerSingleton;
}
- public void start(Looper looper) {
+ /**
+ * 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) {
Log.i(TAG, "start()");
- mHandler = new WifiNanStateHandler(looper);
+ mContext = context;
+ mSm = new WifiNanStateMachine(TAG, looper);
+ mSm.setDbg(DBG);
+ mSm.start();
+
+ mRtt = new WifiNanRttStateManager();
+ mDataPathMgr = new WifiNanDataPathStateManager(this);
+ mDataPathMgr.start(mContext, mSm.getHandler().getLooper());
}
- public void connect(int uid, IWifiNanEventListener listener, int events) {
- Message msg = mHandler.obtainMessage(MESSAGE_CONNECT);
- msg.arg1 = uid;
- msg.arg2 = events;
- msg.obj = listener;
- mHandler.sendMessage(msg);
+ /**
+ * Initialize the late-initialization sub-services: depend on other services already existing.
+ */
+ public void startLate() {
+ mRtt.start(mContext, mSm.getHandler().getLooper());
}
- public void disconnect(int uid) {
- Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT);
- msg.arg1 = uid;
- mHandler.sendMessage(msg);
+ /**
+ * Get the client state for the specified ID (or null if none exists).
+ */
+ /* package */ WifiNanClientState getClient(int clientId) {
+ return mClients.get(clientId);
}
- public void requestConfig(int uid, ConfigRequest configRequest) {
- Message msg = mHandler.obtainMessage(MESSAGE_REQUEST_CONFIG);
- msg.arg1 = uid;
- msg.obj = configRequest;
- mHandler.sendMessage(msg);
+ /*
+ * COMMANDS
+ */
+
+ /**
+ * Place a request for a new client connection on the state machine queue.
+ */
+ public void connect(int clientId, int uid, IWifiNanEventCallback callback,
+ ConfigRequest configRequest) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_CONNECT;
+ msg.arg2 = clientId;
+ msg.obj = callback;
+ msg.getData().putParcelable(MESSAGE_BUNDLE_KEY_CONFIG, configRequest);
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_UID, uid);
+ mSm.sendMessage(msg);
}
- public void stopSession(int uid, int sessionId) {
- Message msg = mHandler.obtainMessage(MESSAGE_STOP_SESSION);
- msg.arg1 = uid;
- msg.arg2 = sessionId;
- mHandler.sendMessage(msg);
+ /**
+ * Place a request to disconnect (destroy) an existing client on the state
+ * machine queue.
+ */
+ public void disconnect(int clientId) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_DISCONNECT;
+ msg.arg2 = clientId;
+ mSm.sendMessage(msg);
}
- public void destroySession(int uid, int sessionId) {
- Message msg = mHandler.obtainMessage(MESSAGE_DESTROY_SESSION);
- msg.arg1 = uid;
- msg.arg2 = sessionId;
- mHandler.sendMessage(msg);
+ /**
+ * Place a request to stop a discovery session on the state machine queue.
+ */
+ public void terminateSession(int clientId, int sessionId) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_TERMINATE_SESSION;
+ msg.arg2 = clientId;
+ msg.obj = sessionId;
+ mSm.sendMessage(msg);
}
- public void createSession(int uid, int sessionId, IWifiNanSessionListener listener,
- int events) {
- Bundle data = new Bundle();
- data.putInt(MESSAGE_BUNDLE_KEY_EVENTS, events);
-
- Message msg = mHandler.obtainMessage(MESSAGE_CREATE_SESSION);
- msg.setData(data);
- msg.arg1 = uid;
- msg.arg2 = sessionId;
- msg.obj = listener;
- mHandler.sendMessage(msg);
+ /**
+ * Place a request to start a new publish discovery session on the state
+ * machine queue.
+ */
+ public void publish(int clientId, PublishConfig publishConfig,
+ IWifiNanSessionCallback callback) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_PUBLISH;
+ msg.arg2 = clientId;
+ msg.obj = callback;
+ msg.getData().putParcelable(MESSAGE_BUNDLE_KEY_CONFIG, publishConfig);
+ mSm.sendMessage(msg);
}
- public void publish(int uid, int sessionId, PublishData publishData,
- PublishSettings publishSettings) {
- Bundle data = new Bundle();
- data.putParcelable(MESSAGE_BUNDLE_KEY_PUBLISH_DATA, publishData);
- data.putParcelable(MESSAGE_BUNDLE_KEY_PUBLISH_SETTINGS, publishSettings);
-
- Message msg = mHandler.obtainMessage(MESSAGE_PUBLISH);
- msg.setData(data);
- msg.arg1 = uid;
- msg.arg2 = sessionId;
- mHandler.sendMessage(msg);
+ /**
+ * Place a request to modify an existing publish discovery session on the
+ * state machine queue.
+ */
+ public void updatePublish(int clientId, int sessionId, PublishConfig publishConfig) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_UPDATE_PUBLISH;
+ msg.arg2 = clientId;
+ msg.obj = publishConfig;
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_SESSION_ID, sessionId);
+ mSm.sendMessage(msg);
}
- public void subscribe(int uid, int sessionId, SubscribeData subscribeData,
- SubscribeSettings subscribeSettings) {
- Bundle data = new Bundle();
- data.putParcelable(MESSAGE_BUNDLE_KEY_SUBSCRIBE_DATA, subscribeData);
- data.putParcelable(MESSAGE_BUNDLE_KEY_SUBSCRIBE_SETTINGS, subscribeSettings);
-
- Message msg = mHandler.obtainMessage(MESSAGE_SUBSCRIBE);
- msg.setData(data);
- msg.arg1 = uid;
- msg.arg2 = sessionId;
- mHandler.sendMessage(msg);
+ /**
+ * Place a request to start a new subscribe discovery session on the state
+ * machine queue.
+ */
+ public void subscribe(int clientId, SubscribeConfig subscribeConfig,
+ IWifiNanSessionCallback callback) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_SUBSCRIBE;
+ msg.arg2 = clientId;
+ msg.obj = callback;
+ msg.getData().putParcelable(MESSAGE_BUNDLE_KEY_CONFIG, subscribeConfig);
+ mSm.sendMessage(msg);
}
- public void sendMessage(int uid, int sessionId, int peerId, byte[] message, int messageLength,
- int messageId) {
- Bundle data = new Bundle();
- data.putInt(MESSAGE_BUNDLE_KEY_SESSION_ID, sessionId);
- data.putInt(MESSAGE_BUNDLE_KEY_MESSAGE_PEER_ID, peerId);
- data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, message);
- data.putInt(MESSAGE_BUNDLE_KEY_MESSAGE_ID, messageId);
-
- Message msg = mHandler.obtainMessage(MESSAGE_SEND_MESSAGE);
- msg.arg1 = uid;
- msg.arg2 = messageLength;
- msg.setData(data);
- mHandler.sendMessage(msg);
+ /**
+ * Place a request to modify an existing subscribe discovery session on the
+ * state machine queue.
+ */
+ public void updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_UPDATE_SUBSCRIBE;
+ msg.arg2 = clientId;
+ msg.obj = subscribeConfig;
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_SESSION_ID, sessionId);
+ mSm.sendMessage(msg);
}
- public void onCapabilitiesUpdate(short transactionId, WifiNanNative.Capabilities capabilities) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_CAPABILITIES_UPDATED);
- msg.arg1 = transactionId;
+ /**
+ * Place a request to send a message on a discovery session on the state
+ * machine queue.
+ */
+ public void sendMessage(int clientId, int sessionId, int peerId, byte[] message,
+ int messageLength, int messageId, int retryCount) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_ENQUEUE_SEND_MESSAGE;
+ msg.arg2 = clientId;
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_SESSION_ID, sessionId);
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_MESSAGE_PEER_ID, peerId);
+ msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, message);
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_MESSAGE_ID, messageId);
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_MESSAGE_LENGTH, messageLength);
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_RETRY_COUNT, retryCount);
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Place a request to range a peer on the discovery session on the state machine queue.
+ */
+ public void startRanging(int clientId, int sessionId, RttManager.RttParams[] params,
+ int rangingId) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_START_RANGING;
+ msg.arg2 = clientId;
+ msg.obj = params;
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_SESSION_ID, sessionId);
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_RANGING_ID, rangingId);
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Enable usage of NAN. Doesn't actually turn on NAN (form clusters) - that
+ * only happens when a connection is created.
+ */
+ public void enableUsage() {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_ENABLE_USAGE;
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Disable usage of NAN. Terminates all existing clients with onNanDown().
+ */
+ public void disableUsage() {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_DISABLE_USAGE;
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Checks whether NAN usage is enabled (not necessarily that NAN is up right
+ * now) or disabled.
+ *
+ * @return A boolean indicating whether NAN usage is enabled (true) or
+ * disabled (false).
+ */
+ public boolean isUsageEnabled() {
+ return mUsageEnabled;
+ }
+
+ /**
+ * Get the capabilities of the current NAN firmware.
+ */
+ public void getCapabilities() {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_GET_CAPABILITIES;
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Create all NAN data path interfaces which are supported by the firmware capabilities.
+ */
+ public void createAllDataPathInterfaces() {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_CREATE_ALL_DATA_PATH_INTERFACES;
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * delete all NAN data path interfaces.
+ */
+ public void deleteAllDataPathInterfaces() {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_DELETE_ALL_DATA_PATH_INTERFACES;
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Create the specified data-path interface. Doesn't actually creates a data-path.
+ */
+ public void createDataPathInterface(String interfaceName) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_CREATE_DATA_PATH_INTERFACE;
+ msg.obj = interfaceName;
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Deletes the specified data-path interface.
+ */
+ public void deleteDataPathInterface(String interfaceName) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_DELETE_DATA_PATH_INTERFACE;
+ msg.obj = interfaceName;
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Command to initiate a data-path (executed by the initiator).
+ */
+ public void initiateDataPathSetup(String networkSpecifier, int peerId, int channelRequestType,
+ int channel, byte[] peer, String interfaceName, byte[] token) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_INITIATE_DATA_PATH_SETUP;
+ msg.obj = networkSpecifier;
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_PEER_ID, peerId);
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_CHANNEL_REQ_TYPE, channelRequestType);
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_CHANNEL, channel);
+ msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS, peer);
+ msg.getData().putString(MESSAGE_BUNDLE_KEY_INTERFACE_NAME, interfaceName);
+ msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, token);
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Command to respond to the data-path request (executed by the responder).
+ */
+ public void respondToDataPathRequest(boolean accept, int ndpId, String interfaceName,
+ String token) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_RESPOND_TO_DATA_PATH_SETUP_REQUEST;
+ msg.arg2 = ndpId;
+ msg.obj = accept;
+ msg.getData().putString(MESSAGE_BUNDLE_KEY_INTERFACE_NAME, interfaceName);
+ msg.getData().putString(MESSAGE_BUNDLE_KEY_MESSAGE, token);
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Command to terminate the specified data-path.
+ */
+ public void endDataPath(int ndpId) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_END_DATA_PATH;
+ msg.arg2 = ndpId;
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * NAN follow-on messages (L2 messages) are queued by the firmware for transmission
+ * on-the-air. The firmware has limited queue depth. The host queues all messages and doles
+ * them out to the firmware when possible. This command removes the next messages for
+ * transmission from the host queue and attempts to send it through the firmware. The queues
+ * are inspected when the command is executed - not when the command is placed on the handler
+ * (i.e. not evaluated here).
+ */
+ private void transmitNextMessage() {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_TRANSMIT_NEXT_MESSAGE;
+ mSm.sendMessage(msg);
+ }
+
+ /*
+ * RESPONSES
+ */
+
+ /**
+ * Place a callback request on the state machine queue: configuration
+ * request completed (successfully).
+ */
+ public void onConfigSuccessResponse(short transactionId) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
+ msg.arg1 = RESPONSE_TYPE_ON_CONFIG_SUCCESS;
+ msg.arg2 = transactionId;
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Place a callback request on the state machine queue: configuration
+ * request failed.
+ */
+ public void onConfigFailedResponse(short transactionId, int reason) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
+ msg.arg1 = RESPONSE_TYPE_ON_CONFIG_FAIL;
+ 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) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
+ msg.arg1 = RESPONSE_TYPE_ON_SESSION_CONFIG_SUCCESS;
+ msg.arg2 = transactionId;
+ msg.obj = pubSubId;
+ msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_SESSION_TYPE, isPublish);
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Place a callback request on the state machine queue: session
+ * configuration (new or update) request failed.
+ */
+ public void onSessionConfigFailResponse(short transactionId, boolean isPublish, int reason) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
+ msg.arg1 = RESPONSE_TYPE_ON_SESSION_CONFIG_FAIL;
+ msg.arg2 = transactionId;
+ msg.obj = reason;
+ msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_SESSION_TYPE, isPublish);
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Place a callback request on the state machine queue: message has been queued successfully.
+ */
+ public void onMessageSendQueuedSuccessResponse(short transactionId) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
+ msg.arg1 = RESPONSE_TYPE_ON_MESSAGE_SEND_QUEUED_SUCCESS;
+ msg.arg2 = transactionId;
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Place a callback request on the state machine queue: attempt to queue the message failed.
+ */
+ public void onMessageSendQueuedFailResponse(short transactionId, int reason) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
+ msg.arg1 = RESPONSE_TYPE_ON_MESSAGE_SEND_QUEUED_FAIL;
+ msg.arg2 = transactionId;
+ msg.obj = reason;
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Place a callback request on the state machine queue: update vendor
+ * capabilities of the NAN stack.
+ */
+ public void onCapabilitiesUpdateResponse(short transactionId,
+ WifiNanNative.Capabilities capabilities) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
+ msg.arg1 = RESPONSE_TYPE_ON_CAPABILITIES_UPDATED;
+ msg.arg2 = transactionId;
msg.obj = capabilities;
- mHandler.sendMessage(msg);
+ mSm.sendMessage(msg);
}
- public void onConfigCompleted(short transactionId) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_CONFIG_COMPLETED);
- msg.arg1 = transactionId;
- mHandler.sendMessage(msg);
+ /**
+ * Places a callback request on the state machine queue: data-path interface creation command
+ * completed.
+ */
+ public void onCreateDataPathInterfaceResponse(short transactionId, boolean success,
+ int reasonOnFailure) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
+ msg.arg1 = RESPONSE_TYPE_ON_CREATE_INTERFACE;
+ msg.arg2 = transactionId;
+ msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG, success);
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_STATUS_CODE, reasonOnFailure);
+ mSm.sendMessage(msg);
}
- public void onConfigFailed(short transactionId, int reason) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_CONFIG_FAILED);
- msg.arg1 = transactionId;
- msg.arg2 = reason;
- mHandler.sendMessage(msg);
+ /**
+ * Places a callback request on the state machine queue: data-path interface deletion command
+ * completed.
+ */
+ public void onDeleteDataPathInterfaceResponse(short transactionId, boolean success,
+ int reasonOnFailure) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
+ msg.arg1 = RESPONSE_TYPE_ON_DELETE_INTERFACE;
+ msg.arg2 = transactionId;
+ msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG, success);
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_STATUS_CODE, reasonOnFailure);
+ mSm.sendMessage(msg);
}
- public void onPublishSuccess(short transactionId, int publishId) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_PUBLISH_SUCCESS);
- msg.arg1 = transactionId;
- msg.arg2 = publishId;
- mHandler.sendMessage(msg);
+ /**
+ * Response from firmware to {@link #initiateDataPathSetup(String, int, int, int, byte[],
+ * String, byte[])}. Indicates that command has started succesfully (not completed!).
+ */
+ public void onInitiateDataPathResponseSuccess(short transactionId, int ndpId) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
+ msg.arg1 = RESPONSE_TYPE_ON_INITIATE_DATA_PATH_SUCCESS;
+ msg.arg2 = transactionId;
+ msg.obj = ndpId;
+ mSm.sendMessage(msg);
}
- public void onPublishFail(short transactionId, int status) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_PUBLISH_FAIL);
- msg.arg1 = transactionId;
- msg.arg2 = status;
- mHandler.sendMessage(msg);
+ /**
+ * Response from firmware to
+ * {@link #initiateDataPathSetup(String, int, int, int, byte[], String, byte[])}. Indicates
+ * that command has failed.
+ */
+ public void onInitiateDataPathResponseFail(short transactionId, int reason) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
+ msg.arg1 = RESPONSE_TYPE_ON_INITIATE_DATA_PATH_FAIL;
+ msg.arg2 = transactionId;
+ msg.obj = reason;
+ mSm.sendMessage(msg);
}
- public void onMessageSendSuccess(short transactionId) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_MESSAGE_SEND_SUCCESS);
- msg.arg1 = transactionId;
- mHandler.sendMessage(msg);
+ /**
+ * Response from firmware to {@link #respondToDataPathRequest(boolean, int, String, String)}.
+ */
+ public void onRespondToDataPathSetupRequestResponse(short transactionId, boolean success,
+ int reasonOnFailure) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
+ msg.arg1 = RESPONSE_TYPE_ON_RESPOND_TO_DATA_PATH_SETUP_REQUEST;
+ msg.arg2 = transactionId;
+ msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG, success);
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_STATUS_CODE, reasonOnFailure);
+ mSm.sendMessage(msg);
}
- public void onMessageSendFail(short transactionId, int status) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_MESSAGE_SEND_FAIL);
- msg.arg1 = transactionId;
- msg.arg2 = status;
- mHandler.sendMessage(msg);
+ /**
+ * Response from firmware to {@link #endDataPath(int)}.
+ */
+ public void onEndDataPathResponse(short transactionId, boolean success, int reasonOnFailure) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
+ msg.arg1 = RESPONSE_TYPE_ON_END_DATA_PATH;
+ msg.arg2 = transactionId;
+ msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG, success);
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_STATUS_CODE, reasonOnFailure);
+ mSm.sendMessage(msg);
}
- public void onSubscribeSuccess(short transactionId, int subscribeId) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_SUBSCRIBE_SUCCESS);
- msg.arg1 = transactionId;
- msg.arg2 = subscribeId;
- mHandler.sendMessage(msg);
- }
+ /*
+ * NOTIFICATIONS
+ */
- public void onSubscribeFail(short transactionId, int status) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_SUBSCRIBE_FAIL);
- msg.arg1 = transactionId;
- msg.arg2 = status;
- mHandler.sendMessage(msg);
- }
-
- public void onUnknownTransaction(int responseType, short transactionId, int status) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_UNKNOWN_TRANSACTION);
- Bundle data = new Bundle();
- data.putInt(MESSAGE_BUNDLE_KEY_RESPONSE_TYPE, responseType);
- msg.setData(data);
- msg.arg1 = transactionId;
- msg.arg2 = status;
- mHandler.sendMessage(msg);
- }
-
- public void onInterfaceAddressChange(byte[] mac) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_INTERFACE_CHANGE);
+ /**
+ * Place a callback request on the state machine queue: the discovery
+ * interface has changed.
+ */
+ public void onInterfaceAddressChangeNotification(byte[] mac) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
+ msg.arg1 = NOTIFICATION_TYPE_INTERFACE_CHANGE;
msg.obj = mac;
- mHandler.sendMessage(msg);
+ mSm.sendMessage(msg);
}
- public void onClusterChange(int flag, byte[] clusterId) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_CLUSTER_CHANGE);
- msg.arg1 = flag;
+ /**
+ * Place a callback request on the state machine queue: the cluster
+ * membership has changed (e.g. due to starting a new cluster or joining
+ * another cluster).
+ */
+ public void onClusterChangeNotification(int flag, byte[] clusterId) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
+ msg.arg1 = NOTIFICATION_TYPE_CLUSTER_CHANGE;
+ msg.arg2 = flag;
msg.obj = clusterId;
- mHandler.sendMessage(msg);
+ mSm.sendMessage(msg);
}
- public void onMatch(int pubSubId, int requestorInstanceId, byte[] peerMac,
+ /**
+ * Place a callback request on the state machine queue: a discovery match
+ * has occurred - e.g. our subscription discovered someone else publishing a
+ * matching service (to the one we were looking for).
+ */
+ public void onMatchNotification(int pubSubId, int requestorInstanceId, byte[] peerMac,
byte[] serviceSpecificInfo, int serviceSpecificInfoLength, byte[] matchFilter,
int matchFilterLength) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_MATCH);
- msg.arg1 = pubSubId;
- msg.arg2 = requestorInstanceId;
- Bundle data = new Bundle();
- data.putByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS, peerMac);
- data.putByteArray(MESSAGE_BUNDLE_KEY_SSI_DATA, serviceSpecificInfo);
- data.putInt(MESSAGE_BUNDLE_KEY_SSI_LENGTH, serviceSpecificInfoLength);
- data.putByteArray(MESSAGE_BUNDLE_KEY_FILTER_DATA, matchFilter);
- data.putInt(MESSAGE_BUNDLE_KEY_FILTER_LENGTH, matchFilterLength);
- msg.setData(data);
- mHandler.sendMessage(msg);
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
+ msg.arg1 = NOTIFICATION_TYPE_MATCH;
+ msg.arg2 = pubSubId;
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_REQ_INSTANCE_ID, requestorInstanceId);
+ msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS, peerMac);
+ msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_SSI_DATA, serviceSpecificInfo);
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_SSI_LENGTH, serviceSpecificInfoLength);
+ msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_FILTER_DATA, matchFilter);
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_FILTER_LENGTH, matchFilterLength);
+ mSm.sendMessage(msg);
}
- public void onPublishTerminated(int publishId, int status) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_PUBLISH_TERMINATED);
- msg.arg1 = publishId;
- msg.arg2 = status;
- mHandler.sendMessage(msg);
+ /**
+ * Place a callback request on the state machine queue: a session (publish
+ * or subscribe) has terminated (per plan or due to an error).
+ */
+ public void onSessionTerminatedNotification(int pubSubId, int reason, boolean isPublish) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
+ msg.arg1 = NOTIFICATION_TYPE_SESSION_TERMINATED;
+ msg.arg2 = pubSubId;
+ msg.obj = reason;
+ msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_SESSION_TYPE, isPublish);
+ mSm.sendMessage(msg);
}
- public void onSubscribeTerminated(int subscribeId, int status) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_SUBSCRIBE_TERMINATED);
- msg.arg1 = subscribeId;
- msg.arg2 = status;
- mHandler.sendMessage(msg);
- }
-
- public void onMessageReceived(int pubSubId, int requestorInstanceId, byte[] peerMac,
+ /**
+ * Place a callback request on the state machine queue: a message has been
+ * received as part of a discovery session.
+ */
+ public void onMessageReceivedNotification(int pubSubId, int requestorInstanceId, byte[] peerMac,
byte[] message, int messageLength) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_MESSAGE_RECEIVED);
- msg.arg1 = pubSubId;
- msg.arg2 = requestorInstanceId;
- Bundle data = new Bundle();
- data.putByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS, peerMac);
- data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE_DATA, message);
- data.putInt(MESSAGE_BUNDLE_KEY_MESSAGE_LENGTH, messageLength);
- msg.setData(data);
- mHandler.sendMessage(msg);
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
+ msg.arg1 = NOTIFICATION_TYPE_MESSAGE_RECEIVED;
+ msg.arg2 = pubSubId;
+ msg.obj = requestorInstanceId;
+ msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS, peerMac);
+ msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE_DATA, message);
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_MESSAGE_LENGTH, messageLength);
+ mSm.sendMessage(msg);
}
- public void onNanDown(int reason) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_NAN_DOWN);
- msg.arg1 = reason;
- mHandler.sendMessage(msg);
+ /**
+ * Place a callback request on the state machine queue: NAN is going down.
+ */
+ public void onNanDownNotification(int reason) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
+ msg.arg1 = NOTIFICATION_TYPE_NAN_DOWN;
+ msg.arg2 = reason;
+ mSm.sendMessage(msg);
}
- private class WifiNanStateHandler extends Handler {
- WifiNanStateHandler(android.os.Looper looper) {
- super(looper);
+ /**
+ * Notification that a message has been sent successfully (i.e. an ACK has been received).
+ */
+ public void onMessageSendSuccessNotification(short transactionId) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
+ msg.arg1 = NOTIFICATION_TYPE_ON_MESSAGE_SEND_SUCCESS;
+ msg.arg2 = transactionId;
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Notification that a message transmission has failed due to the indicated reason - e.g. no ACK
+ * was received.
+ */
+ public void onMessageSendFailNotification(short transactionId, int reason) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
+ msg.arg1 = NOTIFICATION_TYPE_ON_MESSAGE_SEND_FAIL;
+ msg.arg2 = transactionId;
+ msg.obj = reason;
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Place a callback request on the state machine queue: data-path request (from peer) received.
+ */
+ public void onDataPathRequestNotification(int pubSubId, byte[] mac, int ndpId, byte[] message,
+ int messageLength) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
+ msg.arg1 = NOTIFICATION_TYPE_ON_DATA_PATH_REQUEST;
+ msg.arg2 = pubSubId;
+ msg.obj = ndpId;
+ msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS, mac);
+ msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE_DATA, message);
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_MESSAGE_LENGTH, messageLength);
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Place a callback request on the state machine queue: data-path confirmation received - i.e.
+ * data-path is now up.
+ */
+ public void onDataPathConfirmNotification(int ndpId, byte[] mac, boolean accept, int reason,
+ byte[] message, int messageLength) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
+ msg.arg1 = NOTIFICATION_TYPE_ON_DATA_PATH_CONFIRM;
+ msg.arg2 = ndpId;
+ msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS, mac);
+ msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG, accept);
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_STATUS_CODE, reason);
+ msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE_DATA, message);
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_MESSAGE_LENGTH, messageLength);
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Place a callback request on the state machine queue: the specified data-path has been
+ * terminated.
+ */
+ public void onDataPathEndNotification(int ndpId) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
+ msg.arg1 = NOTIFICATION_TYPE_ON_DATA_PATH_END;
+ msg.arg2 = ndpId;
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * State machine.
+ */
+ @VisibleForTesting
+ class WifiNanStateMachine extends StateMachine {
+ private static final int TRANSACTION_ID_IGNORE = 0;
+
+ private DefaultState mDefaultState = new DefaultState();
+ private WaitState mWaitState = new WaitState();
+ private WaitForResponseState mWaitForResponseState = new WaitForResponseState();
+
+ private short mNextTransactionId = 1;
+ public int mNextSessionId = 1;
+
+ private Message mCurrentCommand;
+ private short mCurrentTransactionId = TRANSACTION_ID_IGNORE;
+
+ private static final long NAN_SEND_MESSAGE_TIMEOUT = 10_000;
+ private int mSendArrivalSequenceCounter = 0;
+ private boolean mSendQueueBlocked = false;
+ private final SparseArray<Message> mHostQueuedSendMessages = new SparseArray<>();
+ private final Map<Short, Message> mFwQueuedSendMessages = new LinkedHashMap<>();
+ private WakeupMessage mSendMessageTimeoutMessage = new WakeupMessage(mContext, getHandler(),
+ HAL_SEND_MESSAGE_TIMEOUT_TAG, MESSAGE_TYPE_SEND_MESSAGE_TIMEOUT);
+
+ private static final long NAN_WAIT_FOR_DP_CONFIRM_TIMEOUT = 5_000;
+ private final Map<String, WakeupMessage> mDataPathConfirmTimeoutMessages = new HashMap<>();
+
+ WifiNanStateMachine(String name, Looper looper) {
+ super(name, looper);
+
+ addState(mDefaultState);
+ /* --> */ addState(mWaitState, mDefaultState);
+ /* --> */ addState(mWaitForResponseState, mDefaultState);
+
+ setInitialState(mWaitState);
}
- @Override
- public void handleMessage(Message msg) {
- if (DBG) {
- Log.d(TAG, "Message: " + msg.what);
+ public void onNanDownCleanupSendQueueState() {
+ mSendQueueBlocked = false;
+ mHostQueuedSendMessages.clear();
+ mFwQueuedSendMessages.clear();
+ }
+
+ private class DefaultState extends State {
+ @Override
+ public boolean processMessage(Message msg) {
+ if (VDBG) {
+ Log.v(TAG, getName() + msg.toString());
+ }
+
+ switch (msg.what) {
+ case MESSAGE_TYPE_NOTIFICATION:
+ processNotification(msg);
+ return HANDLED;
+ case MESSAGE_TYPE_SEND_MESSAGE_TIMEOUT:
+ processSendMessageTimeout();
+ return HANDLED;
+ case MESSAGE_TYPE_DATA_PATH_TIMEOUT: {
+ String networkSpecifier = (String) msg.obj;
+
+ if (VDBG) {
+ Log.v(TAG, "MESSAGE_TYPE_DATA_PATH_TIMEOUT: networkSpecifier="
+ + networkSpecifier);
+ }
+
+ mDataPathMgr.handleDataPathTimeout(networkSpecifier);
+ mDataPathConfirmTimeoutMessages.remove(networkSpecifier);
+ return HANDLED;
+ }
+ default:
+ /* fall-through */
+ }
+
+ Log.wtf(TAG,
+ "DefaultState: should not get non-NOTIFICATION in this state: msg=" + msg);
+ return NOT_HANDLED;
}
- switch (msg.what) {
- case MESSAGE_CONNECT: {
- if (VDBG) {
- Log.d(TAG, "NAN connection request received");
- }
- connectLocal(msg.arg1, (IWifiNanEventListener) msg.obj, msg.arg2);
- break;
- }
- case MESSAGE_DISCONNECT: {
- if (VDBG) {
- Log.d(TAG, "NAN disconnection request received");
- }
- disconnectLocal(msg.arg1);
- break;
- }
- case MESSAGE_REQUEST_CONFIG: {
- if (VDBG) {
- Log.d(TAG, "NAN configuration request received");
- }
- requestConfigLocal(msg.arg1, (ConfigRequest) msg.obj);
- break;
- }
- case MESSAGE_CREATE_SESSION: {
- if (VDBG) {
- Log.d(TAG, "Create session");
- }
- int events = msg.getData().getInt(MESSAGE_BUNDLE_KEY_EVENTS);
- createSessionLocal(msg.arg1, msg.arg2, (IWifiNanSessionListener) msg.obj,
- events);
- break;
- }
- case MESSAGE_DESTROY_SESSION: {
- if (VDBG) {
- Log.d(TAG, "Destroy session");
- }
- destroySessionLocal(msg.arg1, msg.arg2);
- break;
- }
- case MESSAGE_PUBLISH: {
- Bundle data = msg.getData();
- PublishData publishData = (PublishData) data
- .getParcelable(MESSAGE_BUNDLE_KEY_PUBLISH_DATA);
- PublishSettings publishSettings = (PublishSettings) data
- .getParcelable(MESSAGE_BUNDLE_KEY_PUBLISH_SETTINGS);
- if (VDBG) {
- Log.d(TAG,
- "Publish: data='" + publishData + "', settings=" + publishSettings);
- }
+ }
- publishLocal(msg.arg1, msg.arg2, publishData, publishSettings);
- break;
+ private class WaitState extends State {
+ @Override
+ public boolean processMessage(Message msg) {
+ if (VDBG) {
+ Log.v(TAG, getName() + msg.toString());
}
- case MESSAGE_SUBSCRIBE: {
- Bundle data = msg.getData();
- SubscribeData subscribeData = (SubscribeData) data
- .getParcelable(MESSAGE_BUNDLE_KEY_SUBSCRIBE_DATA);
- SubscribeSettings subscribeSettings = (SubscribeSettings) data
- .getParcelable(MESSAGE_BUNDLE_KEY_SUBSCRIBE_SETTINGS);
- if (VDBG) {
- Log.d(TAG, "Subscribe: data='" + subscribeData + "', settings="
- + subscribeSettings);
- }
- subscribeLocal(msg.arg1, msg.arg2, subscribeData, subscribeSettings);
- break;
+ switch (msg.what) {
+ case MESSAGE_TYPE_COMMAND:
+ if (processCommand(msg)) {
+ transitionTo(mWaitForResponseState);
+ }
+ return HANDLED;
+ case MESSAGE_TYPE_RESPONSE:
+ /* fall-through */
+ case MESSAGE_TYPE_RESPONSE_TIMEOUT:
+ /*
+ * remnants/delayed/out-of-sync messages - but let
+ * WaitForResponseState deal with them (identified as
+ * out-of-date by transaction ID).
+ */
+ deferMessage(msg);
+ return HANDLED;
+ default:
+ /* fall-through */
}
- case MESSAGE_SEND_MESSAGE: {
- Bundle data = msg.getData();
- int sessionId = msg.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
- int peerId = data.getInt(MESSAGE_BUNDLE_KEY_MESSAGE_PEER_ID);
- byte[] message = data.getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE);
- int messageId = data.getInt(MESSAGE_BUNDLE_KEY_MESSAGE_ID);
- if (VDBG) {
- Log.d(TAG, "Send Message: message='" + message + "' (ID=" + messageId
- + ") to peerId=" + peerId);
- }
+ return NOT_HANDLED;
+ }
+ }
- sendFollowonMessageLocal(msg.arg1, sessionId, peerId, message, msg.arg2,
- messageId);
+ private class WaitForResponseState extends State {
+ private static final long NAN_COMMAND_TIMEOUT = 5_000;
+ private WakeupMessage mTimeoutMessage;
+
+ @Override
+ public void enter() {
+ mTimeoutMessage = new WakeupMessage(mContext, getHandler(), HAL_COMMAND_TIMEOUT_TAG,
+ MESSAGE_TYPE_RESPONSE_TIMEOUT, mCurrentCommand.arg1, mCurrentTransactionId);
+ mTimeoutMessage.schedule(SystemClock.elapsedRealtime() + NAN_COMMAND_TIMEOUT);
+ }
+
+ @Override
+ public void exit() {
+ mTimeoutMessage.cancel();
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ if (VDBG) {
+ Log.v(TAG, getName() + msg.toString());
+ }
+
+ switch (msg.what) {
+ case MESSAGE_TYPE_COMMAND:
+ /*
+ * don't want COMMANDs in this state - defer until back
+ * in WaitState
+ */
+ deferMessage(msg);
+ return HANDLED;
+ case MESSAGE_TYPE_RESPONSE:
+ if (msg.arg2 == mCurrentTransactionId) {
+ processResponse(msg);
+ transitionTo(mWaitState);
+ } else {
+ Log.w(TAG,
+ "WaitForResponseState: processMessage: non-matching "
+ + "transaction ID on RESPONSE (a very late "
+ + "response) -- msg=" + msg);
+ /* no transition */
+ }
+ return HANDLED;
+ case MESSAGE_TYPE_RESPONSE_TIMEOUT:
+ if (msg.arg2 == mCurrentTransactionId) {
+ processTimeout(msg);
+ transitionTo(mWaitState);
+ } else {
+ Log.w(TAG, "WaitForResponseState: processMessage: non-matching "
+ + "transaction ID on RESPONSE_TIMEOUT (either a non-cancelled "
+ + "timeout or a race condition with cancel) -- msg=" + msg);
+ /* no transition */
+ }
+ return HANDLED;
+ default:
+ /* fall-through */
+ }
+
+ return NOT_HANDLED;
+ }
+ }
+
+ private void processNotification(Message msg) {
+ if (VDBG) {
+ Log.v(TAG, "processNotification: msg=" + msg);
+ }
+
+ switch (msg.arg1) {
+ case NOTIFICATION_TYPE_INTERFACE_CHANGE: {
+ byte[] mac = (byte[]) msg.obj;
+
+ onInterfaceAddressChangeLocal(mac);
break;
}
- case MESSAGE_STOP_SESSION: {
- if (VDBG) {
- Log.d(TAG, "Stop session");
- }
- stopSessionLocal(msg.arg1, msg.arg2);
+ case NOTIFICATION_TYPE_CLUSTER_CHANGE: {
+ int flag = msg.arg2;
+ byte[] clusterId = (byte[]) msg.obj;
+
+ onClusterChangeLocal(flag, clusterId);
break;
}
- case MESSAGE_ON_CAPABILITIES_UPDATED:
- onCapabilitiesUpdatedLocal((short) msg.arg1,
- (WifiNanNative.Capabilities) msg.obj);
- break;
- case MESSAGE_ON_CONFIG_COMPLETED:
- onConfigCompletedLocal((short) msg.arg1);
- break;
- case MESSAGE_ON_CONFIG_FAILED:
- onConfigFailedLocal((short) msg.arg1, msg.arg2);
- break;
- case MESSAGE_ON_NAN_DOWN:
- onNanDownLocal(msg.arg1);
- break;
- case MESSAGE_ON_INTERFACE_CHANGE:
- onInterfaceAddressChangeLocal((byte[]) msg.obj);
- break;
- case MESSAGE_ON_CLUSTER_CHANGE:
- onClusterChangeLocal(msg.arg1, (byte[]) msg.obj);
- break;
- case MESSAGE_ON_PUBLISH_SUCCESS:
- onPublishSuccessLocal((short) msg.arg1, msg.arg2);
- break;
- case MESSAGE_ON_PUBLISH_FAIL:
- onPublishFailLocal((short) msg.arg1, msg.arg2);
- break;
- case MESSAGE_ON_PUBLISH_TERMINATED:
- onPublishTerminatedLocal(msg.arg1, msg.arg2);
- break;
- case MESSAGE_ON_SUBSCRIBE_SUCCESS:
- onSubscribeSuccessLocal((short) msg.arg1, msg.arg2);
- break;
- case MESSAGE_ON_SUBSCRIBE_FAIL:
- onSubscribeFailLocal((short) msg.arg1, msg.arg2);
- break;
- case MESSAGE_ON_SUBSCRIBE_TERMINATED:
- onSubscribeTerminatedLocal(msg.arg1, msg.arg2);
- break;
- case MESSAGE_ON_MESSAGE_SEND_SUCCESS:
- onMessageSendSuccessLocal((short) msg.arg1);
- break;
- case MESSAGE_ON_MESSAGE_SEND_FAIL:
- onMessageSendFailLocal((short) msg.arg1, msg.arg2);
- break;
- case MESSAGE_ON_UNKNOWN_TRANSACTION:
- onUnknownTransactionLocal(
- msg.getData().getInt(MESSAGE_BUNDLE_KEY_RESPONSE_TYPE),
- (short) msg.arg1, msg.arg2);
- break;
- case MESSAGE_ON_MATCH: {
- int pubSubId = msg.arg1;
- int requestorInstanceId = msg.arg2;
+ case NOTIFICATION_TYPE_MATCH: {
+ int pubSubId = msg.arg2;
+ int requestorInstanceId = msg.getData()
+ .getInt(MESSAGE_BUNDLE_KEY_REQ_INSTANCE_ID);
byte[] peerMac = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS);
byte[] serviceSpecificInfo = msg.getData()
.getByteArray(MESSAGE_BUNDLE_KEY_SSI_DATA);
@@ -495,394 +1022,1361 @@
.getInt(MESSAGE_BUNDLE_KEY_SSI_LENGTH);
byte[] matchFilter = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_FILTER_DATA);
int matchFilterLength = msg.getData().getInt(MESSAGE_BUNDLE_KEY_FILTER_LENGTH);
+
onMatchLocal(pubSubId, requestorInstanceId, peerMac, serviceSpecificInfo,
serviceSpecificInfoLength, matchFilter, matchFilterLength);
break;
}
- case MESSAGE_ON_MESSAGE_RECEIVED: {
- int pubSubId = msg.arg1;
- int requestorInstanceId = msg.arg2;
+ case NOTIFICATION_TYPE_SESSION_TERMINATED: {
+ int pubSubId = msg.arg2;
+ int reason = (Integer) msg.obj;
+ boolean isPublish = msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SESSION_TYPE);
+
+ onSessionTerminatedLocal(pubSubId, isPublish, reason);
+ break;
+ }
+ case NOTIFICATION_TYPE_MESSAGE_RECEIVED: {
+ int pubSubId = msg.arg2;
+ int requestorInstanceId = (Integer) msg.obj;
byte[] peerMac = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS);
byte[] message = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE_DATA);
int messageLength = msg.getData().getInt(MESSAGE_BUNDLE_KEY_MESSAGE_LENGTH);
+
onMessageReceivedLocal(pubSubId, requestorInstanceId, peerMac, message,
messageLength);
break;
}
+ case NOTIFICATION_TYPE_NAN_DOWN: {
+ int reason = msg.arg2;
+
+ /*
+ * TODO: b/28615938. Use reason code to determine whether or not need clean-up
+ * local state (only needed if NAN_DOWN is due to internal firmware reason, e.g.
+ * concurrency, rather than due to a requested shutdown).
+ */
+
+ onNanDownLocal();
+
+ break;
+ }
+ case NOTIFICATION_TYPE_ON_MESSAGE_SEND_SUCCESS: {
+ short transactionId = (short) msg.arg2;
+ Message queuedSendCommand = mFwQueuedSendMessages.get(transactionId);
+ if (VDBG) {
+ Log.v(TAG, "NOTIFICATION_TYPE_ON_MESSAGE_SEND_SUCCESS: queuedSendCommand="
+ + queuedSendCommand);
+ }
+ if (queuedSendCommand == null) {
+ Log.w(TAG,
+ "processNotification: NOTIFICATION_TYPE_ON_MESSAGE_SEND_SUCCESS:"
+ + " transactionId=" + transactionId
+ + " - no such queued send command (timed-out?)");
+ } else {
+ mFwQueuedSendMessages.remove(transactionId);
+ updateSendMessageTimeout();
+ onMessageSendSuccessLocal(queuedSendCommand);
+ }
+ mSendQueueBlocked = false;
+ transmitNextMessage();
+
+ break;
+ }
+ case NOTIFICATION_TYPE_ON_MESSAGE_SEND_FAIL: {
+ short transactionId = (short) msg.arg2;
+ int reason = (Integer) msg.obj;
+ Message sentMessage = mFwQueuedSendMessages.get(transactionId);
+ if (VDBG) {
+ Log.v(TAG, "NOTIFICATION_TYPE_ON_MESSAGE_SEND_FAIL: sentMessage="
+ + sentMessage);
+ }
+ if (sentMessage == null) {
+ Log.w(TAG,
+ "processNotification: NOTIFICATION_TYPE_ON_MESSAGE_SEND_FAIL:"
+ + " transactionId=" + transactionId
+ + " - no such queued send command (timed-out?)");
+ } else {
+ mFwQueuedSendMessages.remove(transactionId);
+ updateSendMessageTimeout();
+
+ int retryCount = sentMessage.getData()
+ .getInt(MESSAGE_BUNDLE_KEY_RETRY_COUNT);
+ if (retryCount > 0 && reason == WifiNanSessionCallback.REASON_TX_FAIL) {
+ if (DBG) {
+ Log.d(TAG,
+ "NOTIFICATION_TYPE_ON_MESSAGE_SEND_FAIL: transactionId="
+ + transactionId + ", reason=" + reason
+ + ": retransmitting - retryCount=" + retryCount);
+ }
+ sentMessage.getData().putInt(MESSAGE_BUNDLE_KEY_RETRY_COUNT,
+ retryCount - 1);
+
+ int arrivalSeq = sentMessage.getData().getInt(
+ MESSAGE_BUNDLE_KEY_MESSAGE_ARRIVAL_SEQ);
+ mHostQueuedSendMessages.put(arrivalSeq, sentMessage);
+ } else {
+ onMessageSendFailLocal(sentMessage, reason);
+ }
+ mSendQueueBlocked = false;
+ transmitNextMessage();
+ }
+ break;
+ }
+ case NOTIFICATION_TYPE_ON_DATA_PATH_REQUEST: {
+ String networkSpecifier = mDataPathMgr.onDataPathRequest(msg.arg2,
+ msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS),
+ (int) msg.obj,
+ msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE_DATA),
+ msg.getData().getInt(MESSAGE_BUNDLE_KEY_MESSAGE_LENGTH));
+
+ if (networkSpecifier != null) {
+ WakeupMessage timeout = new WakeupMessage(mContext, getHandler(),
+ HAL_DATA_PATH_CONFIRM_TIMEOUT_TAG, MESSAGE_TYPE_DATA_PATH_TIMEOUT,
+ 0, 0, networkSpecifier);
+ mDataPathConfirmTimeoutMessages.put(networkSpecifier, timeout);
+ timeout.schedule(
+ SystemClock.elapsedRealtime() + NAN_WAIT_FOR_DP_CONFIRM_TIMEOUT);
+ }
+
+ break;
+ }
+ case NOTIFICATION_TYPE_ON_DATA_PATH_CONFIRM: {
+ String networkSpecifier = mDataPathMgr.onDataPathConfirm(msg.arg2,
+ msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS),
+ msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG),
+ msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE),
+ msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE_DATA),
+ msg.getData().getInt(MESSAGE_BUNDLE_KEY_MESSAGE_LENGTH));
+
+ if (networkSpecifier != null) {
+ WakeupMessage timeout = mDataPathConfirmTimeoutMessages.remove(
+ networkSpecifier);
+ if (timeout != null) {
+ timeout.cancel();
+ }
+ }
+
+ break;
+ }
+ case NOTIFICATION_TYPE_ON_DATA_PATH_END:
+ mDataPathMgr.onDataPathEnd(msg.arg2);
+ break;
default:
- Log.e(TAG, "Unknown message code: " + msg.what);
+ Log.wtf(TAG, "processNotification: this isn't a NOTIFICATION -- msg=" + msg);
+ return;
}
}
+
+ /**
+ * Execute the command specified by the input Message. Returns a true if
+ * need to wait for a RESPONSE, otherwise a false. We may not have to
+ * wait for a RESPONSE if there was an error in the state (so no command
+ * is sent to HAL) OR if we choose not to wait for response - e.g. for
+ * disconnected/terminate commands failure is not possible.
+ */
+ private boolean processCommand(Message msg) {
+ if (VDBG) {
+ Log.v(TAG, "processCommand: msg=" + msg);
+ }
+
+ if (mCurrentCommand != null) {
+ Log.wtf(TAG,
+ "processCommand: receiving a command (msg=" + msg
+ + ") but current (previous) command isn't null (prev_msg="
+ + mCurrentCommand + ")");
+ mCurrentCommand = null;
+ }
+
+ mCurrentTransactionId = mNextTransactionId++;
+
+ boolean waitForResponse = true;
+
+ switch (msg.arg1) {
+ case COMMAND_TYPE_CONNECT: {
+ int clientId = msg.arg2;
+ IWifiNanEventCallback callback = (IWifiNanEventCallback) msg.obj;
+ ConfigRequest configRequest = (ConfigRequest) msg.getData()
+ .getParcelable(MESSAGE_BUNDLE_KEY_CONFIG);
+ int uid = msg.getData().getInt(MESSAGE_BUNDLE_KEY_UID);
+
+ waitForResponse = connectLocal(mCurrentTransactionId, clientId, uid, callback,
+ configRequest);
+ break;
+ }
+ case COMMAND_TYPE_DISCONNECT: {
+ int clientId = msg.arg2;
+
+ waitForResponse = disconnectLocal(mCurrentTransactionId, clientId);
+ break;
+ }
+ case COMMAND_TYPE_TERMINATE_SESSION: {
+ int clientId = msg.arg2;
+ int sessionId = (Integer) msg.obj;
+
+ terminateSessionLocal(clientId, sessionId);
+ waitForResponse = false;
+ break;
+ }
+ case COMMAND_TYPE_PUBLISH: {
+ int clientId = msg.arg2;
+ IWifiNanSessionCallback callback = (IWifiNanSessionCallback) msg.obj;
+ PublishConfig publishConfig = (PublishConfig) msg.getData()
+ .getParcelable(MESSAGE_BUNDLE_KEY_CONFIG);
+
+ waitForResponse = publishLocal(mCurrentTransactionId, clientId, publishConfig,
+ callback);
+ break;
+ }
+ case COMMAND_TYPE_UPDATE_PUBLISH: {
+ int clientId = msg.arg2;
+ int sessionId = msg.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
+ PublishConfig publishConfig = (PublishConfig) msg.obj;
+
+ waitForResponse = updatePublishLocal(mCurrentTransactionId, clientId, sessionId,
+ publishConfig);
+ break;
+ }
+ case COMMAND_TYPE_SUBSCRIBE: {
+ int clientId = msg.arg2;
+ IWifiNanSessionCallback callback = (IWifiNanSessionCallback) msg.obj;
+ SubscribeConfig subscribeConfig = (SubscribeConfig) msg.getData()
+ .getParcelable(MESSAGE_BUNDLE_KEY_CONFIG);
+
+ waitForResponse = subscribeLocal(mCurrentTransactionId, clientId,
+ subscribeConfig, callback);
+ break;
+ }
+ case COMMAND_TYPE_UPDATE_SUBSCRIBE: {
+ int clientId = msg.arg2;
+ int sessionId = msg.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
+ SubscribeConfig subscribeConfig = (SubscribeConfig) msg.obj;
+
+ waitForResponse = updateSubscribeLocal(mCurrentTransactionId, clientId,
+ sessionId, subscribeConfig);
+ break;
+ }
+ case COMMAND_TYPE_ENQUEUE_SEND_MESSAGE: {
+ if (VDBG) {
+ Log.v(TAG, "processCommand: ENQUEUE_SEND_MESSAGE - messageId="
+ + msg.getData().getInt(MESSAGE_BUNDLE_KEY_MESSAGE_ID)
+ + ", mSendArrivalSequenceCounter=" + mSendArrivalSequenceCounter);
+ }
+ Message sendMsg = obtainMessage(msg.what);
+ sendMsg.copyFrom(msg);
+ sendMsg.getData().putInt(MESSAGE_BUNDLE_KEY_MESSAGE_ARRIVAL_SEQ,
+ mSendArrivalSequenceCounter);
+ mHostQueuedSendMessages.put(mSendArrivalSequenceCounter, sendMsg);
+ mSendArrivalSequenceCounter++;
+ waitForResponse = false;
+
+ if (!mSendQueueBlocked) {
+ transmitNextMessage();
+ }
+
+ break;
+ }
+ case COMMAND_TYPE_TRANSMIT_NEXT_MESSAGE: {
+ if (mSendQueueBlocked || mHostQueuedSendMessages.size() == 0) {
+ if (VDBG) {
+ Log.v(TAG, "processCommand: SEND_TOP_OF_QUEUE_MESSAGE - blocked or "
+ + "empty host queue");
+ }
+ waitForResponse = false;
+ } else {
+ if (VDBG) {
+ Log.v(TAG, "processCommand: SEND_TOP_OF_QUEUE_MESSAGE - "
+ + "sendArrivalSequenceCounter="
+ + mHostQueuedSendMessages.keyAt(0));
+ }
+ Message sendMessage = mHostQueuedSendMessages.valueAt(0);
+ mHostQueuedSendMessages.removeAt(0);
+
+ Bundle data = sendMessage.getData();
+ int clientId = sendMessage.arg2;
+ int sessionId = sendMessage.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
+ int peerId = data.getInt(MESSAGE_BUNDLE_KEY_MESSAGE_PEER_ID);
+ byte[] message = data.getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE);
+ int messageId = data.getInt(MESSAGE_BUNDLE_KEY_MESSAGE_ID);
+ int messageLength = data.getInt(MESSAGE_BUNDLE_KEY_MESSAGE_LENGTH);
+
+ msg.getData().putParcelable(MESSAGE_BUNDLE_KEY_SENT_MESSAGE, sendMessage);
+
+ waitForResponse = sendFollowonMessageLocal(mCurrentTransactionId, clientId,
+ sessionId, peerId, message, messageLength, messageId);
+ }
+ break;
+ }
+ case COMMAND_TYPE_ENABLE_USAGE:
+ enableUsageLocal();
+ waitForResponse = false;
+ break;
+ case COMMAND_TYPE_DISABLE_USAGE:
+ disableUsageLocal();
+ waitForResponse = false;
+ break;
+ case COMMAND_TYPE_START_RANGING: {
+ Bundle data = msg.getData();
+
+ int clientId = msg.arg2;
+ RttManager.RttParams[] params = (RttManager.RttParams[]) msg.obj;
+ int sessionId = data.getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
+ int rangingId = data.getInt(MESSAGE_BUNDLE_KEY_RANGING_ID);
+
+ startRangingLocal(clientId, sessionId, params, rangingId);
+ waitForResponse = false;
+ break;
+ }
+ case COMMAND_TYPE_GET_CAPABILITIES:
+ if (mCapabilities == null) {
+ waitForResponse = WifiNanNative.getInstance().getCapabilities(
+ mCurrentTransactionId);
+ } else {
+ if (VDBG) {
+ Log.v(TAG, "COMMAND_TYPE_GET_CAPABILITIES: already have capabilities - "
+ + "skipping");
+ }
+ waitForResponse = false;
+ }
+ break;
+ case COMMAND_TYPE_CREATE_ALL_DATA_PATH_INTERFACES:
+ mDataPathMgr.createAllInterfaces();
+ waitForResponse = false;
+ break;
+ case COMMAND_TYPE_DELETE_ALL_DATA_PATH_INTERFACES:
+ mDataPathMgr.deleteAllInterfaces();
+ waitForResponse = false;
+ break;
+ case COMMAND_TYPE_CREATE_DATA_PATH_INTERFACE:
+ waitForResponse = WifiNanNative.getInstance().createNanNetworkInterface(
+ mCurrentTransactionId, (String) msg.obj);
+ break;
+ case COMMAND_TYPE_DELETE_DATA_PATH_INTERFACE:
+ waitForResponse = WifiNanNative.getInstance().deleteNanNetworkInterface(
+ mCurrentTransactionId, (String) msg.obj);
+ break;
+ case COMMAND_TYPE_INITIATE_DATA_PATH_SETUP: {
+ Bundle data = msg.getData();
+
+ String networkSpecifier = (String) msg.obj;
+
+ int peerId = data.getInt(MESSAGE_BUNDLE_KEY_PEER_ID);
+ int channelRequestType = data.getInt(MESSAGE_BUNDLE_KEY_CHANNEL_REQ_TYPE);
+ int channel = data.getInt(MESSAGE_BUNDLE_KEY_CHANNEL);
+ byte[] peer = data.getByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS);
+ String interfaceName = data.getString(MESSAGE_BUNDLE_KEY_INTERFACE_NAME);
+ byte[] token = data.getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE);
+
+ waitForResponse = initiateDataPathSetupLocal(mCurrentTransactionId, peerId,
+ channelRequestType, channel, peer, interfaceName, token);
+
+ if (waitForResponse) {
+ WakeupMessage timeout = new WakeupMessage(mContext, getHandler(),
+ HAL_DATA_PATH_CONFIRM_TIMEOUT_TAG, MESSAGE_TYPE_DATA_PATH_TIMEOUT,
+ 0, 0, networkSpecifier);
+ mDataPathConfirmTimeoutMessages.put(networkSpecifier, timeout);
+ timeout.schedule(
+ SystemClock.elapsedRealtime() + NAN_WAIT_FOR_DP_CONFIRM_TIMEOUT);
+ }
+ break;
+ }
+ case COMMAND_TYPE_RESPOND_TO_DATA_PATH_SETUP_REQUEST: {
+ Bundle data = msg.getData();
+
+ int ndpId = msg.arg2;
+ boolean accept = (boolean) msg.obj;
+ String interfaceName = data.getString(MESSAGE_BUNDLE_KEY_INTERFACE_NAME);
+ String token = data.getString(MESSAGE_BUNDLE_KEY_MESSAGE);
+
+ waitForResponse = respondToDataPathRequestLocal(mCurrentTransactionId, accept,
+ ndpId, interfaceName, token);
+
+ break;
+ }
+ case COMMAND_TYPE_END_DATA_PATH:
+ waitForResponse = endDataPathLocal(mCurrentTransactionId, msg.arg2);
+ break;
+ default:
+ waitForResponse = false;
+ Log.wtf(TAG, "processCommand: this isn't a COMMAND -- msg=" + msg);
+ /* fall-through */
+ }
+
+ if (!waitForResponse) {
+ mCurrentTransactionId = TRANSACTION_ID_IGNORE;
+ } else {
+ mCurrentCommand = obtainMessage(msg.what);
+ mCurrentCommand.copyFrom(msg);
+ }
+
+ return waitForResponse;
+ }
+
+ private void processResponse(Message msg) {
+ if (VDBG) {
+ Log.v(TAG, "processResponse: msg=" + msg);
+ }
+
+ if (mCurrentCommand == null) {
+ Log.wtf(TAG, "processResponse: no existing command stored!? msg=" + msg);
+ mCurrentTransactionId = TRANSACTION_ID_IGNORE;
+ return;
+ }
+
+ switch (msg.arg1) {
+ case RESPONSE_TYPE_ON_CONFIG_SUCCESS:
+ onConfigCompletedLocal(mCurrentCommand);
+ break;
+ case RESPONSE_TYPE_ON_CONFIG_FAIL: {
+ int reason = (Integer) msg.obj;
+
+ onConfigFailedLocal(mCurrentCommand, reason);
+ break;
+ }
+ case RESPONSE_TYPE_ON_SESSION_CONFIG_SUCCESS: {
+ int pubSubId = (Integer) msg.obj;
+ boolean isPublish = msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SESSION_TYPE);
+
+ onSessionConfigSuccessLocal(mCurrentCommand, pubSubId, isPublish);
+ break;
+ }
+ case RESPONSE_TYPE_ON_SESSION_CONFIG_FAIL: {
+ int reason = (Integer) msg.obj;
+ boolean isPublish = msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SESSION_TYPE);
+
+ onSessionConfigFailLocal(mCurrentCommand, isPublish, reason);
+ break;
+ }
+ case RESPONSE_TYPE_ON_MESSAGE_SEND_QUEUED_SUCCESS: {
+ Message sentMessage = mCurrentCommand.getData().getParcelable(
+ MESSAGE_BUNDLE_KEY_SENT_MESSAGE);
+ sentMessage.getData().putLong(MESSAGE_BUNDLE_KEY_SEND_MESSAGE_ENQUEUE_TIME,
+ SystemClock.elapsedRealtime());
+ mFwQueuedSendMessages.put(mCurrentTransactionId, sentMessage);
+ updateSendMessageTimeout();
+ if (!mSendQueueBlocked) {
+ transmitNextMessage();
+ }
+
+ if (VDBG) {
+ Log.v(TAG, "processResponse: ON_MESSAGE_SEND_QUEUED_SUCCESS - arrivalSeq="
+ + sentMessage.getData().getInt(
+ MESSAGE_BUNDLE_KEY_MESSAGE_ARRIVAL_SEQ));
+ }
+ break;
+ }
+ case RESPONSE_TYPE_ON_MESSAGE_SEND_QUEUED_FAIL: {
+ if (VDBG) {
+ Log.v(TAG, "processResponse: ON_MESSAGE_SEND_QUEUED_FAIL - blocking!");
+ }
+ // TODO: b/29459286 - once there's a unique code for "queue is full" use it!
+ int reason = (Integer) msg.obj;
+
+ Message sentMessage = mCurrentCommand.getData().getParcelable(
+ MESSAGE_BUNDLE_KEY_SENT_MESSAGE);
+ int arrivalSeq = sentMessage.getData().getInt(
+ MESSAGE_BUNDLE_KEY_MESSAGE_ARRIVAL_SEQ);
+ mHostQueuedSendMessages.put(arrivalSeq, sentMessage);
+ mSendQueueBlocked = true;
+
+ if (VDBG) {
+ Log.v(TAG,
+ "processResponse: ON_MESSAGE_SEND_QUEUED_FAIL - arrivalSeq="
+ + arrivalSeq + " -- blocking");
+ }
+ break;
+ }
+ case RESPONSE_TYPE_ON_CAPABILITIES_UPDATED: {
+ onCapabilitiesUpdatedResponseLocal((WifiNanNative.Capabilities) msg.obj);
+ break;
+ }
+ case RESPONSE_TYPE_ON_CREATE_INTERFACE:
+ onCreateDataPathInterfaceResponseLocal(mCurrentCommand,
+ msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG),
+ msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE));
+ break;
+ case RESPONSE_TYPE_ON_DELETE_INTERFACE:
+ onDeleteDataPathInterfaceResponseLocal(mCurrentCommand,
+ msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG),
+ msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE));
+ break;
+ case RESPONSE_TYPE_ON_INITIATE_DATA_PATH_SUCCESS:
+ onInitiateDataPathResponseSuccessLocal(mCurrentCommand, (int) msg.obj);
+ break;
+ case RESPONSE_TYPE_ON_INITIATE_DATA_PATH_FAIL:
+ onInitiateDataPathResponseFailLocal(mCurrentCommand, (int) msg.obj);
+ break;
+ case RESPONSE_TYPE_ON_RESPOND_TO_DATA_PATH_SETUP_REQUEST:
+ onRespondToDataPathSetupRequestResponseLocal(mCurrentCommand,
+ msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG),
+ msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE));
+ break;
+ case RESPONSE_TYPE_ON_END_DATA_PATH:
+ onEndPathEndResponseLocal(mCurrentCommand,
+ msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG),
+ msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE));
+ break;
+ default:
+ Log.wtf(TAG, "processResponse: this isn't a RESPONSE -- msg=" + msg);
+ mCurrentCommand = null;
+ mCurrentTransactionId = TRANSACTION_ID_IGNORE;
+ return;
+ }
+
+ mCurrentCommand = null;
+ mCurrentTransactionId = TRANSACTION_ID_IGNORE;
+ }
+
+ private void processTimeout(Message msg) {
+ if (VDBG) {
+ Log.v(TAG, "processTimeout: msg=" + msg);
+ }
+
+ if (mCurrentCommand == null) {
+ Log.wtf(TAG, "processTimeout: no existing command stored!? msg=" + msg);
+ mCurrentTransactionId = TRANSACTION_ID_IGNORE;
+ return;
+ }
+
+ /*
+ * Only have to handle those COMMANDs which wait for a response.
+ */
+ switch (msg.arg1) {
+ case COMMAND_TYPE_CONNECT: {
+ onConfigFailedLocal(mCurrentCommand, WifiNanEventCallback.REASON_OTHER);
+ 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, WifiNanEventCallback.REASON_OTHER);
+ break;
+ }
+ case COMMAND_TYPE_TERMINATE_SESSION: {
+ Log.wtf(TAG, "processTimeout: TERMINATE_SESSION - shouldn't be waiting!");
+ break;
+ }
+ case COMMAND_TYPE_PUBLISH: {
+ onSessionConfigFailLocal(mCurrentCommand, true,
+ WifiNanSessionCallback.REASON_OTHER);
+ break;
+ }
+ case COMMAND_TYPE_UPDATE_PUBLISH: {
+ onSessionConfigFailLocal(mCurrentCommand, true,
+ WifiNanSessionCallback.REASON_OTHER);
+ break;
+ }
+ case COMMAND_TYPE_SUBSCRIBE: {
+ onSessionConfigFailLocal(mCurrentCommand, false,
+ WifiNanSessionCallback.REASON_OTHER);
+ break;
+ }
+ case COMMAND_TYPE_UPDATE_SUBSCRIBE: {
+ onSessionConfigFailLocal(mCurrentCommand, false,
+ WifiNanSessionCallback.REASON_OTHER);
+ break;
+ }
+ case COMMAND_TYPE_ENQUEUE_SEND_MESSAGE: {
+ Log.wtf(TAG, "processTimeout: ENQUEUE_SEND_MESSAGE - shouldn't be waiting!");
+ break;
+ }
+ case COMMAND_TYPE_TRANSMIT_NEXT_MESSAGE: {
+ Message sentMessage = mCurrentCommand.getData().getParcelable(
+ MESSAGE_BUNDLE_KEY_SENT_MESSAGE);
+ onMessageSendFailLocal(sentMessage, WifiNanSessionCallback.REASON_TX_FAIL);
+ mSendQueueBlocked = false;
+ transmitNextMessage();
+ break;
+ }
+ case COMMAND_TYPE_ENABLE_USAGE:
+ Log.wtf(TAG, "processTimeout: ENABLE_USAGE - shouldn't be waiting!");
+ break;
+ case COMMAND_TYPE_DISABLE_USAGE:
+ Log.wtf(TAG, "processTimeout: DISABLE_USAGE - shouldn't be waiting!");
+ break;
+ case COMMAND_TYPE_START_RANGING:
+ Log.wtf(TAG, "processTimeout: START_RANGING - shouldn't be waiting!");
+ break;
+ case COMMAND_TYPE_GET_CAPABILITIES:
+ Log.e(TAG,
+ "processTimeout: GET_CAPABILITIES timed-out - strange, will try again"
+ + " when next enabled!?");
+ break;
+ case COMMAND_TYPE_CREATE_ALL_DATA_PATH_INTERFACES:
+ Log.wtf(TAG,
+ "processTimeout: CREATE_ALL_DATA_PATH_INTERFACES - shouldn't be "
+ + "waiting!");
+ break;
+ case COMMAND_TYPE_DELETE_ALL_DATA_PATH_INTERFACES:
+ Log.wtf(TAG,
+ "processTimeout: DELETE_ALL_DATA_PATH_INTERFACES - shouldn't be "
+ + "waiting!");
+ break;
+ case COMMAND_TYPE_CREATE_DATA_PATH_INTERFACE:
+ // TODO: fix status: timeout
+ onCreateDataPathInterfaceResponseLocal(mCurrentCommand, false, 0);
+ break;
+ case COMMAND_TYPE_DELETE_DATA_PATH_INTERFACE:
+ // TODO: fix status: timeout
+ onDeleteDataPathInterfaceResponseLocal(mCurrentCommand, false, 0);
+ break;
+ case COMMAND_TYPE_INITIATE_DATA_PATH_SETUP:
+ // TODO: fix status: timeout
+ onInitiateDataPathResponseFailLocal(mCurrentCommand, 0);
+ break;
+ case COMMAND_TYPE_RESPOND_TO_DATA_PATH_SETUP_REQUEST:
+ // TODO: fix status: timeout
+ onRespondToDataPathSetupRequestResponseLocal(mCurrentCommand, false, 0);
+ break;
+ case COMMAND_TYPE_END_DATA_PATH:
+ // TODO: fix status: timeout
+ onEndPathEndResponseLocal(mCurrentCommand, false, 0);
+ break;
+ default:
+ Log.wtf(TAG, "processTimeout: this isn't a COMMAND -- msg=" + msg);
+ /* fall-through */
+ }
+
+ mCurrentCommand = null;
+ mCurrentTransactionId = TRANSACTION_ID_IGNORE;
+ }
+
+ private void updateSendMessageTimeout() {
+ if (VDBG) {
+ Log.v(TAG, "updateSendMessageTimeout: mHostQueuedSendMessages.size()="
+ + mHostQueuedSendMessages.size() + ", mFwQueuedSendMessages.size()="
+ + mFwQueuedSendMessages.size() + ", mSendQueueBlocked="
+ + mSendQueueBlocked);
+ }
+ Iterator<Message> it = mFwQueuedSendMessages.values().iterator();
+ if (it.hasNext()) {
+ /*
+ * Schedule timeout based on the first message in the queue (which is the earliest
+ * submitted message). Timeout = queuing time + timeout constant.
+ */
+ Message msg = it.next();
+ mSendMessageTimeoutMessage.schedule(
+ msg.getData().getLong(MESSAGE_BUNDLE_KEY_SEND_MESSAGE_ENQUEUE_TIME)
+ + NAN_SEND_MESSAGE_TIMEOUT);
+ } else {
+ mSendMessageTimeoutMessage.cancel();
+ }
+ }
+
+ private void processSendMessageTimeout() {
+ if (VDBG) {
+ Log.v(TAG, "processSendMessageTimeout: mHostQueuedSendMessages.size()="
+ + mHostQueuedSendMessages.size() + ", mFwQueuedSendMessages.size()="
+ + mFwQueuedSendMessages.size() + ", mSendQueueBlocked="
+ + mSendQueueBlocked);
+
+ }
+ /*
+ * Note: using 'first' to always time-out (remove) at least 1 notification (partially)
+ * due to test code needs: there's no way to mock elapsedRealtime(). TODO: replace with
+ * injected getClock() once moved off of mmwd.
+ */
+ boolean first = true;
+ long currentTime = SystemClock.elapsedRealtime();
+ Iterator<Map.Entry<Short, Message>> it = mFwQueuedSendMessages.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<Short, Message> entry = it.next();
+ short transactionId = entry.getKey();
+ Message message = entry.getValue();
+ long messageEnqueueTime = message.getData().getLong(
+ MESSAGE_BUNDLE_KEY_SEND_MESSAGE_ENQUEUE_TIME);
+ if (first || messageEnqueueTime + NAN_SEND_MESSAGE_TIMEOUT <= currentTime) {
+ if (VDBG) {
+ Log.v(TAG, "processSendMessageTimeout: expiring - transactionId="
+ + transactionId + ", message=" + message
+ + ", due to messageEnqueueTime=" + messageEnqueueTime
+ + ", currentTime=" + currentTime);
+ }
+ onMessageSendFailLocal(message, WifiNanSessionCallback.REASON_TX_FAIL);
+ it.remove();
+ first = false;
+ } else {
+ break;
+ }
+ }
+ updateSendMessageTimeout();
+ mSendQueueBlocked = false;
+ transmitNextMessage();
+ }
+
+ @Override
+ protected String getLogRecString(Message msg) {
+ StringBuilder sb = new StringBuilder(WifiNanStateManager.messageToString(msg));
+
+ if (msg.what == MESSAGE_TYPE_COMMAND
+ && mCurrentTransactionId != TRANSACTION_ID_IGNORE) {
+ sb.append(" (Transaction ID=").append(mCurrentTransactionId).append(")");
+ }
+
+ return sb.toString();
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("WifiNanStateMachine:");
+ pw.println(" mNextTransactionId: " + mNextTransactionId);
+ pw.println(" mNextSessionId: " + mNextSessionId);
+ pw.println(" mCurrentCommand: " + mCurrentCommand);
+ pw.println(" mCurrentTransaction: " + mCurrentTransactionId);
+ pw.println(" mSendQueueBlocked: " + mSendQueueBlocked);
+ pw.println(" mSendArrivalSequenceCounter: " + mSendArrivalSequenceCounter);
+ pw.println(" mHostQueuedSendMessages: [" + mHostQueuedSendMessages + "]");
+ pw.println(" mFwQueuedSendMessages: [" + mFwQueuedSendMessages + "]");
+ super.dump(fd, pw, args);
+ }
+ }
+
+ private void sendNanStateChangedBroadcast(boolean enabled) {
+ if (VDBG) {
+ Log.v(TAG, "sendNanStateChangedBroadcast: enabled=" + enabled);
+ }
+ final Intent intent = new Intent(WifiNanManager.WIFI_NAN_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ if (enabled) {
+ intent.putExtra(WifiNanManager.EXTRA_WIFI_STATE, WifiNanManager.WIFI_NAN_STATE_ENABLED);
+ } else {
+ intent.putExtra(WifiNanManager.EXTRA_WIFI_STATE,
+ WifiNanManager.WIFI_NAN_STATE_DISABLED);
+ }
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
/*
- * Transaction management classes & operations
+ * COMMANDS
*/
- // non-synchronized (should be ok as long as only used from NanStateManager,
- // NanClientState, and NanSessionState)
- /* package */ short createNextTransactionId() {
- return mNextTransactionId++;
- }
-
- private static class TransactionInfoBase {
- short mTransactionId;
- }
-
- private static class TransactionInfoSession extends TransactionInfoBase {
- public WifiNanClientState mClient;
- public WifiNanSessionState mSession;
- }
-
- private static class TransactionInfoMessage extends TransactionInfoSession {
- public int mMessageId;
- }
-
- private static class TransactionInfoConfig extends TransactionInfoBase {
- public ConfigRequest mConfig;
- }
-
- private void allocateAndRegisterTransactionId(TransactionInfoBase info) {
- info.mTransactionId = createNextTransactionId();
-
- mPendingResponses.put(info.mTransactionId, info);
- }
-
- private void fillInTransactionInfoSession(TransactionInfoSession info, int uid,
- int sessionId) {
- WifiNanClientState client = mClients.get(uid);
- if (client == null) {
- throw new IllegalStateException(
- "getAndRegisterTransactionId: no client exists for uid=" + uid);
- }
- info.mClient = client;
-
- WifiNanSessionState session = info.mClient.getSession(sessionId);
- if (session == null) {
- throw new IllegalStateException(
- "getAndRegisterSessionTransactionId: no session exists for uid=" + uid
- + ", sessionId=" + sessionId);
- }
- info.mSession = session;
- }
-
- private TransactionInfoBase createTransactionInfo() {
- TransactionInfoBase info = new TransactionInfoBase();
- allocateAndRegisterTransactionId(info);
- return info;
- }
-
- private TransactionInfoSession createTransactionInfoSession(int uid, int sessionId) {
- TransactionInfoSession info = new TransactionInfoSession();
- fillInTransactionInfoSession(info, uid, sessionId);
- allocateAndRegisterTransactionId(info);
- return info;
- }
-
- private TransactionInfoMessage createTransactionInfoMessage(int uid, int sessionId,
- int messageId) {
- TransactionInfoMessage info = new TransactionInfoMessage();
- fillInTransactionInfoSession(info, uid, sessionId);
- info.mMessageId = messageId;
- allocateAndRegisterTransactionId(info);
- return info;
- }
-
- private TransactionInfoConfig createTransactionInfoConfig(ConfigRequest configRequest) {
- TransactionInfoConfig info = new TransactionInfoConfig();
- info.mConfig = configRequest;
- allocateAndRegisterTransactionId(info);
- return info;
- }
-
- private TransactionInfoBase getAndRemovePendingResponseTransactionInfo(short transactionId) {
- TransactionInfoBase transInfo = mPendingResponses.get(transactionId);
- if (transInfo != null) {
- mPendingResponses.remove(transactionId);
- }
-
- return transInfo;
- }
-
- private WifiNanSessionState getNanSessionStateForPubSubId(int pubSubId) {
- for (int i = 0; i < mClients.size(); ++i) {
- WifiNanSessionState session = mClients.valueAt(i)
- .getNanSessionStateForPubSubId(pubSubId);
- if (session != null) {
- return session;
- }
- }
-
- return null;
- }
-
- /*
- * Actions (calls from API to service)
- */
- private void connectLocal(int uid, IWifiNanEventListener listener, int events) {
+ private boolean connectLocal(short transactionId, int clientId, int uid,
+ IWifiNanEventCallback callback, ConfigRequest configRequest) {
if (VDBG) {
- Log.v(TAG, "connect(): uid=" + uid + ", listener=" + listener + ", events=" + events);
+ Log.v(TAG, "connectLocal(): transactionId=" + transactionId + ", clientId=" + clientId
+ + ", uid=" + uid + ", callback=" + callback + ", configRequest="
+ + configRequest);
}
- if (mClients.get(uid) != null) {
- Log.e(TAG, "connect: entry already exists for uid=" + uid);
- return;
+ if (!mUsageEnabled) {
+ Log.w(TAG, "connect(): called with mUsageEnabled=false");
+ return false;
}
- WifiNanClientState client = new WifiNanClientState(uid, listener, events);
- mClients.put(uid, client);
+ if (mClients.get(clientId) != null) {
+ Log.e(TAG, "connectLocal: entry already exists for clientId=" + clientId);
+ }
+
+ if (mCurrentNanConfiguration != null
+ && !mCurrentNanConfiguration.equalsOnTheAir(configRequest)) {
+ try {
+ callback.onConnectFail(
+ WifiNanEventCallback.REASON_ALREADY_CONNECTED_INCOMPAT_CONFIG);
+ } catch (RemoteException e) {
+ Log.w(TAG, "connectLocal onConnectFail(): RemoteException (FYI): " + e);
+ }
+ return false;
+ }
+
+ ConfigRequest merged = mergeConfigRequests(configRequest);
+ if (mCurrentNanConfiguration != null && mCurrentNanConfiguration.equals(merged)) {
+ try {
+ callback.onConnectSuccess();
+ } catch (RemoteException e) {
+ Log.w(TAG, "connectLocal onConnectSuccess(): RemoteException (FYI): " + e);
+ }
+ WifiNanClientState client = new WifiNanClientState(clientId, uid, callback,
+ configRequest);
+ client.onInterfaceAddressChange(mCurrentDiscoveryInterfaceMac);
+ mClients.append(clientId, client);
+ return false;
+ }
+
+ return WifiNanNative.getInstance().enableAndConfigure(transactionId, merged,
+ mCurrentNanConfiguration == null);
}
- private void disconnectLocal(int uid) {
+ private boolean disconnectLocal(short transactionId, int clientId) {
if (VDBG) {
- Log.v(TAG, "disconnect(): uid=" + uid);
+ Log.v(TAG,
+ "disconnectLocal(): transactionId=" + transactionId + ", clientId=" + clientId);
}
- WifiNanClientState client = mClients.get(uid);
- mClients.delete(uid);
-
+ WifiNanClientState client = mClients.get(clientId);
if (client == null) {
- Log.e(TAG, "disconnect: no entry for uid=" + uid);
- return;
+ Log.e(TAG, "disconnectLocal: no entry for clientId=" + clientId);
+ return false;
}
-
- List<Integer> toRemove = new ArrayList<>();
- for (int i = 0; i < mPendingResponses.size(); ++i) {
- TransactionInfoBase info = mPendingResponses.valueAt(i);
- if (!(info instanceof TransactionInfoSession)) {
- continue;
- }
- if (((TransactionInfoSession) info).mClient.getUid() == uid) {
- toRemove.add(i);
- }
- }
- for (Integer id : toRemove) {
- mPendingResponses.removeAt(id);
- }
-
+ mClients.delete(clientId);
client.destroy();
if (mClients.size() == 0) {
- WifiNanNative.getInstance().disable(createTransactionInfo().mTransactionId);
- return;
+ mCurrentNanConfiguration = null;
+ WifiNanNative.getInstance().disable((short) 0);
+ return false;
}
- ConfigRequest merged = mergeConfigRequests();
+ ConfigRequest merged = mergeConfigRequests(null);
+ if (merged.equals(mCurrentNanConfiguration)) {
+ return false;
+ }
- WifiNanNative.getInstance()
- .enableAndConfigure(createTransactionInfoConfig(merged).mTransactionId, merged);
+ return WifiNanNative.getInstance().enableAndConfigure(transactionId, merged, false);
}
- private void requestConfigLocal(int uid, ConfigRequest configRequest) {
+ private void terminateSessionLocal(int clientId, int sessionId) {
if (VDBG) {
- Log.v(TAG, "requestConfig(): uid=" + uid + ", configRequest=" + configRequest);
+ Log.v(TAG,
+ "terminateSessionLocal(): clientId=" + clientId + ", sessionId=" + sessionId);
}
- WifiNanClientState client = mClients.get(uid);
+ WifiNanClientState client = mClients.get(clientId);
if (client == null) {
- Log.e(TAG, "requestConfig: no client exists for uid=" + uid);
+ Log.e(TAG, "terminateSession: no client exists for clientId=" + clientId);
return;
}
- client.setConfigRequest(configRequest);
-
- ConfigRequest merged = mergeConfigRequests();
-
- WifiNanNative.getInstance()
- .enableAndConfigure(createTransactionInfoConfig(merged).mTransactionId, merged);
+ client.terminateSession(sessionId);
}
- private void createSessionLocal(int uid, int sessionId, IWifiNanSessionListener listener,
- int events) {
+ private boolean publishLocal(short transactionId, int clientId, PublishConfig publishConfig,
+ IWifiNanSessionCallback callback) {
if (VDBG) {
- Log.v(TAG, "createSession(): uid=" + uid + ", sessionId=" + sessionId + ", listener="
- + listener + ", events=" + events);
+ Log.v(TAG, "publishLocal(): transactionId=" + transactionId + ", clientId=" + clientId
+ + ", publishConfig=" + publishConfig + ", callback=" + callback);
}
- WifiNanClientState client = mClients.get(uid);
+ WifiNanClientState client = mClients.get(clientId);
if (client == null) {
- Log.e(TAG, "createSession: no client exists for uid=" + uid);
- return;
+ Log.e(TAG, "publishLocal: no client exists for clientId=" + clientId);
+ return false;
}
- client.createSession(sessionId, listener, events);
+ return WifiNanNative.getInstance().publish(transactionId, 0, publishConfig);
}
- private void destroySessionLocal(int uid, int sessionId) {
+ private boolean updatePublishLocal(short transactionId, int clientId, int sessionId,
+ PublishConfig publishConfig) {
if (VDBG) {
- Log.v(TAG, "destroySession(): uid=" + uid + ", sessionId=" + sessionId);
+ Log.v(TAG, "updatePublishLocal(): transactionId=" + transactionId + ", clientId="
+ + clientId + ", sessionId=" + sessionId + ", publishConfig=" + publishConfig);
}
- WifiNanClientState client = mClients.get(uid);
+ WifiNanClientState client = mClients.get(clientId);
if (client == null) {
- Log.e(TAG, "destroySession: no client exists for uid=" + uid);
+ Log.e(TAG, "updatePublishLocal: no client exists for clientId=" + clientId);
+ return false;
+ }
+
+ WifiNanSessionState session = client.getSession(sessionId);
+ if (session == null) {
+ Log.e(TAG, "updatePublishLocal: no session exists for clientId=" + clientId
+ + ", sessionId=" + sessionId);
+ return false;
+ }
+
+ return session.updatePublish(transactionId, publishConfig);
+ }
+
+ private boolean subscribeLocal(short transactionId, int clientId,
+ SubscribeConfig subscribeConfig, IWifiNanSessionCallback callback) {
+ if (VDBG) {
+ Log.v(TAG, "subscribeLocal(): transactionId=" + transactionId + ", clientId=" + clientId
+ + ", subscribeConfig=" + subscribeConfig + ", callback=" + callback);
+ }
+
+ WifiNanClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG, "subscribeLocal: no client exists for clientId=" + clientId);
+ return false;
+ }
+
+ return WifiNanNative.getInstance().subscribe(transactionId, 0, subscribeConfig);
+ }
+
+ private boolean updateSubscribeLocal(short transactionId, int clientId, int sessionId,
+ SubscribeConfig subscribeConfig) {
+ if (VDBG) {
+ Log.v(TAG,
+ "updateSubscribeLocal(): transactionId=" + transactionId + ", clientId="
+ + clientId + ", sessionId=" + sessionId + ", subscribeConfig="
+ + subscribeConfig);
+ }
+
+ WifiNanClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG, "updateSubscribeLocal: no client exists for clientId=" + clientId);
+ return false;
+ }
+
+ WifiNanSessionState session = client.getSession(sessionId);
+ if (session == null) {
+ Log.e(TAG, "updateSubscribeLocal: no session exists for clientId=" + clientId
+ + ", sessionId=" + sessionId);
+ return false;
+ }
+
+ return session.updateSubscribe(transactionId, subscribeConfig);
+ }
+
+ private boolean sendFollowonMessageLocal(short transactionId, int clientId, int sessionId,
+ int peerId, byte[] message, int messageLength, int messageId) {
+ if (VDBG) {
+ Log.v(TAG,
+ "sendFollowonMessageLocal(): transactionId=" + transactionId + ", clientId="
+ + clientId + ", sessionId=" + sessionId + ", peerId=" + peerId
+ + ", messageLength=" + messageLength + ", messageId=" + messageId);
+ }
+
+ WifiNanClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG, "sendFollowonMessageLocal: no client exists for clientId=" + clientId);
+ return false;
+ }
+
+ WifiNanSessionState session = client.getSession(sessionId);
+ if (session == null) {
+ Log.e(TAG, "sendFollowonMessageLocal: no session exists for clientId=" + clientId
+ + ", sessionId=" + sessionId);
+ return false;
+ }
+
+ return session.sendMessage(transactionId, peerId, message, messageLength, messageId);
+ }
+
+ private void enableUsageLocal() {
+ if (mUsageEnabled) {
return;
}
- List<Integer> toRemove = new ArrayList<>();
- for (int i = 0; i < mPendingResponses.size(); ++i) {
- TransactionInfoBase info = mPendingResponses.valueAt(i);
- if (!(info instanceof TransactionInfoSession)) {
- continue;
- }
- TransactionInfoSession infoSession = (TransactionInfoSession) info;
- if (infoSession.mClient.getUid() == uid
- && infoSession.mSession.getSessionId() == sessionId) {
- toRemove.add(i);
+ mUsageEnabled = true;
+ getCapabilities();
+ createAllDataPathInterfaces();
+ sendNanStateChangedBroadcast(true);
+ }
+
+ private void disableUsageLocal() {
+ if (!mUsageEnabled) {
+ return;
+ }
+
+ mUsageEnabled = false;
+ WifiNanNative.getInstance().disable((short) 0);
+ WifiNanNative.getInstance().deInitNan();
+ onNanDownLocal();
+
+ deleteAllDataPathInterfaces();
+ sendNanStateChangedBroadcast(false);
+ }
+
+ private void startRangingLocal(int clientId, int sessionId, RttManager.RttParams[] params,
+ int rangingId) {
+ if (VDBG) {
+ Log.v(TAG, "startRangingLocal: clientId=" + clientId + ", sessionId=" + sessionId
+ + ", parms=" + Arrays.toString(params) + ", rangingId=" + rangingId);
+ }
+
+ WifiNanClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG, "startRangingLocal: no client exists for clientId=" + clientId);
+ return;
+ }
+
+ WifiNanSessionState session = client.getSession(sessionId);
+ if (session == null) {
+ Log.e(TAG, "startRangingLocal: no session exists for clientId=" + clientId
+ + ", sessionId=" + sessionId);
+ client.onRangingFailure(rangingId, RttManager.REASON_INVALID_REQUEST,
+ "Invalid session ID");
+ return;
+ }
+
+ for (RttManager.RttParams param : params) {
+ String peerIdStr = param.bssid;
+ try {
+ param.bssid = session.getMac(Integer.parseInt(peerIdStr), ":");
+ if (param.bssid == null) {
+ Log.d(TAG, "startRangingLocal: no MAC address for peer ID=" + peerIdStr);
+ param.bssid = "";
+ }
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "startRangingLocal: invalid peer ID specification (in bssid field): '"
+ + peerIdStr + "'");
+ param.bssid = "";
}
}
- for (Integer id : toRemove) {
- mPendingResponses.removeAt(id);
- }
- client.destroySession(sessionId);
+ mRtt.startRanging(rangingId, client, params);
}
- private void publishLocal(int uid, int sessionId, PublishData publishData,
- PublishSettings publishSettings) {
+ private boolean initiateDataPathSetupLocal(short transactionId, int peerId,
+ int channelRequestType, int channel, byte[] peer, String interfaceName, byte[] token) {
if (VDBG) {
- Log.v(TAG, "publish(): uid=" + uid + ", sessionId=" + sessionId + ", data="
- + publishData + ", settings=" + publishSettings);
+ Log.v(TAG,
+ "initiateDataPathSetupLocal(): transactionId=" + transactionId + ", peerId="
+ + peerId + ", channelRequestType=" + channelRequestType + ", channel="
+ + channel + ", peer=" + String.valueOf(HexEncoding.encode(peer))
+ + ", interfaceName=" + interfaceName + ", token=" + token);
}
- TransactionInfoSession info = createTransactionInfoSession(uid, sessionId);
-
- info.mSession.publish(info.mTransactionId, publishData, publishSettings);
+ return WifiNanNative.getInstance().initiateDataPath(transactionId, peerId,
+ channelRequestType, channel, peer, interfaceName, token, token.length);
}
- private void subscribeLocal(int uid, int sessionId, SubscribeData subscribeData,
- SubscribeSettings subscribeSettings) {
+ private boolean respondToDataPathRequestLocal(short transactionId, boolean accept,
+ int ndpId, String interfaceName, String token) {
if (VDBG) {
- Log.v(TAG, "subscribe(): uid=" + uid + ", sessionId=" + sessionId + ", data="
- + subscribeData + ", settings=" + subscribeSettings);
+ Log.v(TAG,
+ "respondToDataPathRequestLocal(): transactionId=" + transactionId + ", accept="
+ + accept + ", ndpId=" + ndpId + ", interfaceName=" + interfaceName
+ + ", token=" + token);
}
- TransactionInfoSession info = createTransactionInfoSession(uid, sessionId);
+ byte[] tokenBytes = token.getBytes();
- info.mSession.subscribe(info.mTransactionId, subscribeData, subscribeSettings);
+ return WifiNanNative.getInstance().respondToDataPathRequest(transactionId, accept, ndpId,
+ interfaceName, tokenBytes, tokenBytes.length);
}
- private void sendFollowonMessageLocal(int uid, int sessionId, int peerId, byte[] message,
- int messageLength, int messageId) {
+ private boolean endDataPathLocal(short transactionId, int ndpId) {
if (VDBG) {
- Log.v(TAG, "sendMessage(): uid=" + uid + ", sessionId=" + sessionId + ", peerId="
- + peerId + ", messageLength=" + messageLength + ", messageId=" + messageId);
+ Log.v(TAG,
+ "endDataPathLocal: transactionId=" + transactionId + ", ndpId=" + ndpId);
}
- TransactionInfoMessage info = createTransactionInfoMessage(uid, sessionId, messageId);
-
- info.mSession.sendMessage(info.mTransactionId, peerId, message, messageLength, messageId);
- }
-
- private void stopSessionLocal(int uid, int sessionId) {
- if (VDBG) {
- Log.v(TAG, "stopSession(): uid=" + uid + ", sessionId=" + sessionId);
- }
-
- TransactionInfoSession info = createTransactionInfoSession(uid, sessionId);
-
- info.mSession.stop(info.mTransactionId);
+ return WifiNanNative.getInstance().endDataPath(transactionId, ndpId);
}
/*
- * Callbacks (calls from HAL/Native to service)
+ * RESPONSES
*/
- private void onCapabilitiesUpdatedLocal(short transactionId,
- WifiNanNative.Capabilities capabilities) {
+ private void onConfigCompletedLocal(Message completedCommand) {
if (VDBG) {
- Log.v(TAG, "onCapabilitiesUpdatedLocal: transactionId=" + transactionId
- + ", capabilites=" + capabilities);
+ Log.v(TAG, "onConfigCompleted: completedCommand=" + completedCommand);
+ }
+
+ if (completedCommand.arg1 == COMMAND_TYPE_CONNECT) {
+ Bundle data = completedCommand.getData();
+
+ int clientId = completedCommand.arg2;
+ IWifiNanEventCallback callback = (IWifiNanEventCallback) completedCommand.obj;
+ ConfigRequest configRequest = (ConfigRequest) data
+ .getParcelable(MESSAGE_BUNDLE_KEY_CONFIG);
+ int uid = data.getInt(MESSAGE_BUNDLE_KEY_UID);
+
+ WifiNanClientState client = new WifiNanClientState(clientId, uid, callback,
+ configRequest);
+ mClients.put(clientId, client);
+ try {
+ callback.onConnectSuccess();
+ } catch (RemoteException e) {
+ Log.w(TAG,
+ "onConfigCompletedLocal onConnectSuccess(): RemoteException (FYI): " + e);
+ }
+ client.onInterfaceAddressChange(mCurrentDiscoveryInterfaceMac);
+ } else if (completedCommand.arg1 == COMMAND_TYPE_DISCONNECT) {
+ /*
+ * NOP (i.e. updated configuration after disconnecting a client)
+ */
+ } else {
+ Log.wtf(TAG, "onConfigCompletedLocal: unexpected completedCommand=" + completedCommand);
+ return;
+ }
+
+ mCurrentNanConfiguration = mergeConfigRequests(null);
+ }
+
+ private void onConfigFailedLocal(Message failedCommand, int reason) {
+ if (VDBG) {
+ Log.v(TAG,
+ "onConfigFailedLocal: failedCommand=" + failedCommand + ", reason=" + reason);
+ }
+
+ if (failedCommand.arg1 == COMMAND_TYPE_CONNECT) {
+ IWifiNanEventCallback callback = (IWifiNanEventCallback) failedCommand.obj;
+
+ try {
+ callback.onConnectFail(reason);
+ } catch (RemoteException e) {
+ Log.w(TAG, "onConfigFailedLocal onConnectFail(): RemoteException (FYI): " + e);
+ }
+ } else if (failedCommand.arg1 == COMMAND_TYPE_DISCONNECT) {
+ /*
+ * 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).
+ */
+ } else {
+ Log.wtf(TAG, "onConfigFailedLocal: unexpected failedCommand=" + failedCommand);
+ return;
+ }
+
+ }
+
+ private void onSessionConfigSuccessLocal(Message completedCommand, int pubSubId,
+ boolean isPublish) {
+ if (VDBG) {
+ Log.v(TAG, "onSessionConfigSuccessLocal: completedCommand=" + completedCommand
+ + ", pubSubId=" + pubSubId + ", isPublish=" + isPublish);
+ }
+
+ if (completedCommand.arg1 == COMMAND_TYPE_PUBLISH
+ || completedCommand.arg1 == COMMAND_TYPE_SUBSCRIBE) {
+ int clientId = completedCommand.arg2;
+ IWifiNanSessionCallback callback = (IWifiNanSessionCallback) completedCommand.obj;
+
+ WifiNanClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG,
+ "onSessionConfigSuccessLocal: no client exists for clientId=" + clientId);
+ return;
+ }
+
+ int sessionId = mSm.mNextSessionId++;
+ try {
+ callback.onSessionStarted(sessionId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onSessionConfigSuccessLocal: onSessionStarted() RemoteException=" + e);
+ return;
+ }
+
+ WifiNanSessionState session = new WifiNanSessionState(sessionId, pubSubId, callback,
+ isPublish);
+ client.addSession(session);
+ } else if (completedCommand.arg1 == COMMAND_TYPE_UPDATE_PUBLISH
+ || completedCommand.arg1 == COMMAND_TYPE_UPDATE_SUBSCRIBE) {
+ int clientId = completedCommand.arg2;
+ int sessionId = completedCommand.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
+
+ WifiNanClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG,
+ "onSessionConfigSuccessLocal: no client exists for clientId=" + clientId);
+ return;
+ }
+
+ WifiNanSessionState session = client.getSession(sessionId);
+ if (session == null) {
+ Log.e(TAG, "onSessionConfigSuccessLocal: no session exists for clientId=" + clientId
+ + ", sessionId=" + sessionId);
+ return;
+ }
+
+ try {
+ session.getCallback().onSessionConfigSuccess();
+ } catch (RemoteException e) {
+ Log.e(TAG, "onSessionConfigSuccessLocal: onSessionConfigSuccess() RemoteException="
+ + e);
+ }
+ } else {
+ Log.wtf(TAG,
+ "onSessionConfigSuccessLocal: unexpected completedCommand=" + completedCommand);
+ }
+ }
+
+ private void onSessionConfigFailLocal(Message failedCommand, boolean isPublish, int reason) {
+ if (VDBG) {
+ Log.v(TAG, "onSessionConfigFailLocal: failedCommand=" + failedCommand + ", isPublish="
+ + isPublish + ", reason=" + reason);
+ }
+
+ if (failedCommand.arg1 == COMMAND_TYPE_PUBLISH
+ || failedCommand.arg1 == COMMAND_TYPE_SUBSCRIBE) {
+ IWifiNanSessionCallback callback = (IWifiNanSessionCallback) failedCommand.obj;
+ try {
+ callback.onSessionConfigFail(reason);
+ } catch (RemoteException e) {
+ Log.w(TAG, "onSessionConfigFailLocal onSessionConfigFail(): RemoteException (FYI): "
+ + e);
+ }
+ } else if (failedCommand.arg1 == COMMAND_TYPE_UPDATE_PUBLISH
+ || failedCommand.arg1 == COMMAND_TYPE_UPDATE_SUBSCRIBE) {
+ int clientId = failedCommand.arg2;
+ int sessionId = failedCommand.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
+
+ WifiNanClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG, "onSessionConfigFailLocal: no client exists for clientId=" + clientId);
+ return;
+ }
+
+ WifiNanSessionState session = client.getSession(sessionId);
+ if (session == null) {
+ Log.e(TAG, "onSessionConfigFailLocal: no session exists for clientId=" + clientId
+ + ", sessionId=" + sessionId);
+ return;
+ }
+
+ try {
+ session.getCallback().onSessionConfigFail(reason);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onSessionConfigFailLocal: onSessionConfigFail() RemoteException=" + e);
+ }
+ } else {
+ Log.wtf(TAG, "onSessionConfigFailLocal: unexpected failedCommand=" + failedCommand);
+ }
+ }
+
+ private void onMessageSendSuccessLocal(Message completedCommand) {
+ if (VDBG) {
+ Log.v(TAG, "onMessageSendSuccess: completedCommand=" + completedCommand);
+ }
+
+ int clientId = completedCommand.arg2;
+ int sessionId = completedCommand.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
+ int messageId = completedCommand.getData().getInt(MESSAGE_BUNDLE_KEY_MESSAGE_ID);
+
+ WifiNanClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG, "onMessageSendSuccessLocal: no client exists for clientId=" + clientId);
+ return;
+ }
+
+ WifiNanSessionState session = client.getSession(sessionId);
+ if (session == null) {
+ Log.e(TAG, "onMessageSendSuccessLocal: no session exists for clientId=" + clientId
+ + ", sessionId=" + sessionId);
+ return;
+ }
+
+ try {
+ session.getCallback().onMessageSendSuccess(messageId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "onMessageSendSuccessLocal: RemoteException (FYI): " + e);
+ }
+ }
+
+ private void onMessageSendFailLocal(Message failedCommand, int reason) {
+ if (VDBG) {
+ Log.v(TAG, "onMessageSendFail: failedCommand=" + failedCommand + ", reason=" + reason);
+ }
+
+ int clientId = failedCommand.arg2;
+ int sessionId = failedCommand.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
+ int messageId = failedCommand.getData().getInt(MESSAGE_BUNDLE_KEY_MESSAGE_ID);
+
+ WifiNanClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG, "onMessageSendFailLocal: no client exists for clientId=" + clientId);
+ return;
+ }
+
+ WifiNanSessionState session = client.getSession(sessionId);
+ if (session == null) {
+ Log.e(TAG, "onMessageSendFailLocal: no session exists for clientId=" + clientId
+ + ", sessionId=" + sessionId);
+ return;
+ }
+
+ try {
+ session.getCallback().onMessageSendFail(messageId, reason);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onMessageSendFailLocal: onMessageSendFail RemoteException=" + e);
+ }
+ }
+
+ private void onCapabilitiesUpdatedResponseLocal(WifiNanNative.Capabilities capabilities) {
+ if (VDBG) {
+ Log.v(TAG, "onCapabilitiesUpdatedResponseLocal: capabilites=" + capabilities);
}
mCapabilities = capabilities;
}
- private void onConfigCompletedLocal(short transactionId) {
+ private void onCreateDataPathInterfaceResponseLocal(Message command, boolean success,
+ int reasonOnFailure) {
if (VDBG) {
- Log.v(TAG, "onConfigCompleted: transactionId=" + transactionId);
+ Log.v(TAG, "onCreateDataPathInterfaceResponseLocal: command=" + command + ", success="
+ + success + ", reasonOnFailure=" + reasonOnFailure);
}
- TransactionInfoBase info = getAndRemovePendingResponseTransactionInfo(transactionId);
- if (info == null) {
- Log.e(TAG, "onConfigCompleted: no transaction info for transactionId=" + transactionId);
- return;
- }
- if (!(info instanceof TransactionInfoConfig)) {
- Log.e(TAG, "onConfigCompleted: invalid info structure stored for transactionId="
- + transactionId);
- return;
- }
- TransactionInfoConfig infoConfig = (TransactionInfoConfig) info;
-
- if (DBG) {
- Log.d(TAG, "onConfigCompleted: request=" + infoConfig.mConfig);
- }
-
- for (int i = 0; i < mClients.size(); ++i) {
- WifiNanClientState client = mClients.valueAt(i);
- client.onConfigCompleted(infoConfig.mConfig);
+ if (success) {
+ if (DBG) {
+ Log.d(TAG, "onCreateDataPathInterfaceResponseLocal: successfully created interface "
+ + command.obj);
+ }
+ mDataPathMgr.onInterfaceCreated((String) command.obj);
+ } else {
+ Log.e(TAG,
+ "onCreateDataPathInterfaceResponseLocal: failed when trying to create "
+ + "interface "
+ + command.obj + ". Reason code=" + reasonOnFailure);
}
}
- private void onConfigFailedLocal(short transactionId, int reason) {
+ private void onDeleteDataPathInterfaceResponseLocal(Message command, boolean success,
+ int reasonOnFailure) {
if (VDBG) {
- Log.v(TAG, "onEnableFailed: transactionId=" + transactionId + ", reason=" + reason);
+ Log.v(TAG, "onDeleteDataPathInterfaceResponseLocal: command=" + command + ", success="
+ + success + ", reasonOnFailure=" + reasonOnFailure);
}
- TransactionInfoBase info = getAndRemovePendingResponseTransactionInfo(transactionId);
- if (info == null) {
- Log.e(TAG, "onConfigFailed: no transaction info for transactionId=" + transactionId);
- return;
- }
- if (!(info instanceof TransactionInfoConfig)) {
- Log.e(TAG, "onConfigCompleted: invalid info structure stored for transactionId="
- + transactionId);
- return;
- }
- TransactionInfoConfig infoConfig = (TransactionInfoConfig) info;
-
- if (DBG) {
- Log.d(TAG, "onConfigFailed: request=" + infoConfig.mConfig);
- }
-
- for (int i = 0; i < mClients.size(); ++i) {
- WifiNanClientState client = mClients.valueAt(i);
- client.onConfigFailed(infoConfig.mConfig, reason);
+ if (success) {
+ if (DBG) {
+ Log.d(TAG, "onDeleteDataPathInterfaceResponseLocal: successfully deleted interface "
+ + command.obj);
+ }
+ mDataPathMgr.onInterfaceDeleted((String) command.obj);
+ } else {
+ Log.e(TAG,
+ "onDeleteDataPathInterfaceResponseLocal: failed when trying to delete "
+ + "interface "
+ + command.obj + ". Reason code=" + reasonOnFailure);
}
}
- private void onNanDownLocal(int reason) {
+ private void onInitiateDataPathResponseSuccessLocal(Message command, int ndpId) {
if (VDBG) {
- Log.v(TAG, "onNanDown: reason=" + reason);
+ Log.v(TAG, "onInitiateDataPathResponseSuccessLocal: command=" + command + ", ndpId="
+ + ndpId);
}
- int interested = 0;
- for (int i = 0; i < mClients.size(); ++i) {
- WifiNanClientState client = mClients.valueAt(i);
- interested += client.onNanDown(reason);
- }
-
- if (interested == 0) {
- Log.e(TAG, "onNanDown: event received but no listeners registered for this event "
- + "- should be disabled from fw!");
- }
+ mDataPathMgr.onDataPathInitiateSuccess((String) command.obj, ndpId);
}
+ private void onInitiateDataPathResponseFailLocal(Message command, int reason) {
+ if (VDBG) {
+ Log.v(TAG, "onInitiateDataPathResponseFailLocal: command=" + command + ", reason="
+ + reason);
+ }
+
+ mDataPathMgr.onDataPathInitiateFail((String) command.obj, reason);
+ }
+
+ private void onRespondToDataPathSetupRequestResponseLocal(Message command, boolean success,
+ int reasonOnFailure) {
+ if (VDBG) {
+ Log.v(TAG, "onRespondToDataPathSetupRequestResponseLocal: command=" + command
+ + ", success=" + success + ", reasonOnFailure=" + reasonOnFailure);
+ }
+
+ // TODO: do something with this
+ }
+
+ private void onEndPathEndResponseLocal(Message command, boolean success, int reasonOnFailure) {
+ if (VDBG) {
+ Log.v(TAG, "onEndPathEndResponseLocal: command=" + command
+ + ", success=" + success + ", reasonOnFailure=" + reasonOnFailure);
+ }
+
+ // TODO: do something with this
+ }
+
+ /*
+ * NOTIFICATIONS
+ */
+
private void onInterfaceAddressChangeLocal(byte[] mac) {
if (VDBG) {
Log.v(TAG, "onInterfaceAddressChange: mac=" + String.valueOf(HexEncoding.encode(mac)));
}
- int interested = 0;
+ mCurrentDiscoveryInterfaceMac = mac;
+
for (int i = 0; i < mClients.size(); ++i) {
WifiNanClientState client = mClients.valueAt(i);
- interested += client.onInterfaceAddressChange(mac);
- }
-
- if (interested == 0) {
- Log.e(TAG, "onInterfaceAddressChange: event received but no listeners registered "
- + "for this event - should be disabled from fw!");
+ client.onInterfaceAddressChange(mac);
}
}
@@ -892,179 +2386,9 @@
+ String.valueOf(HexEncoding.encode(clusterId)));
}
- int interested = 0;
for (int i = 0; i < mClients.size(); ++i) {
WifiNanClientState client = mClients.valueAt(i);
- interested += client.onClusterChange(flag, clusterId);
- }
-
- if (interested == 0) {
- Log.e(TAG, "onClusterChange: event received but no listeners registered for this "
- + "event - should be disabled from fw!");
- }
- }
-
- private void onPublishSuccessLocal(short transactionId, int publishId) {
- if (VDBG) {
- Log.v(TAG, "onPublishSuccess: transactionId=" + transactionId + ", publishId="
- + publishId);
- }
-
- TransactionInfoBase info = getAndRemovePendingResponseTransactionInfo(transactionId);
- if (info == null) {
- Log.e(TAG, "onPublishSuccess(): no info registered for transactionId=" + transactionId);
- return;
- }
- if (!(info instanceof TransactionInfoSession)) {
- Log.e(TAG, "onPublishSuccess: invalid info structure stored for transactionId="
- + transactionId);
- return;
- }
- TransactionInfoSession infoSession = (TransactionInfoSession) info;
-
- infoSession.mSession.onPublishSuccess(publishId);
- }
-
- private void onPublishFailLocal(short transactionId, int status) {
- if (VDBG) {
- Log.v(TAG, "onPublishFail: transactionId=" + transactionId + ", status=" + status);
- }
-
- TransactionInfoBase info = getAndRemovePendingResponseTransactionInfo(transactionId);
- if (info == null) {
- Log.e(TAG, "onPublishFail(): no info registered for transactionId=" + transactionId);
- return;
- }
- if (!(info instanceof TransactionInfoSession)) {
- Log.e(TAG, "onPublishFail: invalid info structure stored for transactionId="
- + transactionId);
- return;
- }
- TransactionInfoSession infoSession = (TransactionInfoSession) info;
-
- infoSession.mSession.onPublishFail(status);
- }
-
- private void onPublishTerminatedLocal(int publishId, int status) {
- if (VDBG) {
- Log.v(TAG, "onPublishTerminated: publishId=" + publishId + ", status=" + status);
- }
-
- WifiNanSessionState session = getNanSessionStateForPubSubId(publishId);
- if (session == null) {
- Log.e(TAG, "onPublishTerminated: no session found for publishId=" + publishId);
- return;
- }
-
- session.onPublishTerminated(status);
- }
-
- private void onSubscribeSuccessLocal(short transactionId, int subscribeId) {
- if (VDBG) {
- Log.v(TAG, "onSubscribeSuccess: transactionId=" + transactionId + ", subscribeId="
- + subscribeId);
- }
-
- TransactionInfoBase info = getAndRemovePendingResponseTransactionInfo(transactionId);
- if (info == null) {
- Log.e(TAG,
- "onSubscribeSuccess(): no info registered for transactionId=" + transactionId);
- return;
- }
- if (!(info instanceof TransactionInfoSession)) {
- Log.e(TAG, "onSubscribeSuccess: invalid info structure stored for transactionId="
- + transactionId);
- return;
- }
- TransactionInfoSession infoSession = (TransactionInfoSession) info;
-
- infoSession.mSession.onSubscribeSuccess(subscribeId);
- }
-
- private void onSubscribeFailLocal(short transactionId, int status) {
- if (VDBG) {
- Log.v(TAG, "onSubscribeFail: transactionId=" + transactionId + ", status=" + status);
- }
-
- TransactionInfoBase info = getAndRemovePendingResponseTransactionInfo(transactionId);
- if (info == null) {
- Log.e(TAG, "onSubscribeFail(): no info registered for transactionId=" + transactionId);
- return;
- }
- if (!(info instanceof TransactionInfoSession)) {
- Log.e(TAG, "onSubscribeFail: invalid info structure stored for transactionId="
- + transactionId);
- return;
- }
- TransactionInfoSession infoSession = (TransactionInfoSession) info;
-
- infoSession.mSession.onSubscribeFail(status);
- }
-
- private void onSubscribeTerminatedLocal(int subscribeId, int status) {
- if (VDBG) {
- Log.v(TAG, "onPublishTerminated: subscribeId=" + subscribeId + ", status=" + status);
- }
-
- WifiNanSessionState session = getNanSessionStateForPubSubId(subscribeId);
- if (session == null) {
- Log.e(TAG, "onSubscribeTerminated: no session found for subscribeId=" + subscribeId);
- return;
- }
-
- session.onSubscribeTerminated(status);
- }
-
- private void onMessageSendSuccessLocal(short transactionId) {
- if (VDBG) {
- Log.v(TAG, "onMessageSendSuccess: transactionId=" + transactionId);
- }
-
- TransactionInfoBase info = getAndRemovePendingResponseTransactionInfo(transactionId);
- if (info == null) {
- Log.e(TAG, "onMessageSendSuccess(): no info registered for transactionId="
- + transactionId);
- return;
- }
- if (!(info instanceof TransactionInfoMessage)) {
- Log.e(TAG, "onMessageSendSuccess: invalid info structure stored for transactionId="
- + transactionId);
- return;
- }
- TransactionInfoMessage infoMessage = (TransactionInfoMessage) info;
-
- infoMessage.mSession.onMessageSendSuccess(infoMessage.mMessageId);
- }
-
- private void onMessageSendFailLocal(short transactionId, int status) {
- if (VDBG) {
- Log.v(TAG, "onMessageSendFail: transactionId=" + transactionId + ", status=" + status);
- }
-
- TransactionInfoBase info = getAndRemovePendingResponseTransactionInfo(transactionId);
- if (info == null) {
- Log.e(TAG,
- "onMessageSendFail(): no info registered for transactionId=" + transactionId);
- return;
- }
- if (!(info instanceof TransactionInfoMessage)) {
- Log.e(TAG, "onMessageSendFail: invalid info structure stored for transactionId="
- + transactionId);
- return;
- }
- TransactionInfoMessage infoMessage = (TransactionInfoMessage) info;
-
- infoMessage.mSession.onMessageSendFail(infoMessage.mMessageId, status);
- }
-
- private void onUnknownTransactionLocal(int responseType, short transactionId, int status) {
- Log.e(TAG, "onUnknownTransaction: responseType=" + responseType + ", transactionId="
- + transactionId + ", status=" + status);
-
- TransactionInfoBase info = getAndRemovePendingResponseTransactionInfo(transactionId);
- if (info == null) {
- Log.e(TAG, "onUnknownTransaction(): no info registered for transactionId="
- + transactionId);
+ client.onClusterChange(flag, clusterId, mCurrentDiscoveryInterfaceMac);
}
}
@@ -1073,65 +2397,123 @@
int matchFilterLength) {
if (VDBG) {
Log.v(TAG, "onMatch: pubSubId=" + pubSubId + ", requestorInstanceId="
- + requestorInstanceId + ", peerMac="
+ + requestorInstanceId + ", peerDiscoveryMac="
+ String.valueOf(HexEncoding.encode(peerMac)) + ", serviceSpecificInfoLength="
- + serviceSpecificInfoLength + ", serviceSpecificInfo=" + serviceSpecificInfo
- + ", matchFilterLength=" + matchFilterLength + ", matchFilter=" + matchFilter);
+ + serviceSpecificInfoLength + ", serviceSpecificInfo="
+ + Arrays.toString(serviceSpecificInfo) + ", matchFilterLength="
+ + matchFilterLength + ", matchFilter=" + Arrays.toString(matchFilter));
}
- WifiNanSessionState session = getNanSessionStateForPubSubId(pubSubId);
- if (session == null) {
+ Pair<WifiNanClientState, WifiNanSessionState> data = getClientSessionForPubSubId(pubSubId);
+ if (data == null) {
Log.e(TAG, "onMatch: no session found for pubSubId=" + pubSubId);
return;
}
- session.onMatch(requestorInstanceId, peerMac, serviceSpecificInfo,
+ data.second.onMatch(requestorInstanceId, peerMac, serviceSpecificInfo,
serviceSpecificInfoLength, matchFilter, matchFilterLength);
}
+ private void onSessionTerminatedLocal(int pubSubId, boolean isPublish, int reason) {
+ if (VDBG) {
+ Log.v(TAG, "onSessionTerminatedLocal: pubSubId=" + pubSubId + ", isPublish=" + isPublish
+ + ", reason=" + reason);
+ }
+
+ Pair<WifiNanClientState, WifiNanSessionState> data = getClientSessionForPubSubId(pubSubId);
+ if (data == null) {
+ Log.e(TAG, "onSessionTerminatedLocal: no session found for pubSubId=" + pubSubId);
+ return;
+ }
+
+ try {
+ data.second.getCallback().onSessionTerminated(reason);
+ } catch (RemoteException e) {
+ Log.w(TAG,
+ "onSessionTerminatedLocal onSessionTerminated(): RemoteException (FYI): " + e);
+ }
+ data.first.removeSession(data.second.getSessionId());
+ }
+
private void onMessageReceivedLocal(int pubSubId, int requestorInstanceId, byte[] peerMac,
byte[] message, int messageLength) {
if (VDBG) {
Log.v(TAG,
- "onMessageReceived: pubSubId=" + pubSubId + ", requestorInstanceId="
- + requestorInstanceId + ", peerMac="
+ "onMessageReceivedLocal: pubSubId=" + pubSubId + ", requestorInstanceId="
+ + requestorInstanceId + ", peerDiscoveryMac="
+ String.valueOf(HexEncoding.encode(peerMac)) + ", messageLength="
+ messageLength);
}
- WifiNanSessionState session = getNanSessionStateForPubSubId(pubSubId);
- if (session == null) {
- Log.e(TAG, "onMessageReceived: no session found for pubSubId=" + pubSubId);
+ Pair<WifiNanClientState, WifiNanSessionState> data = getClientSessionForPubSubId(pubSubId);
+ if (data == null) {
+ Log.e(TAG, "onMessageReceivedLocal: no session found for pubSubId=" + pubSubId);
return;
}
- session.onMessageReceived(requestorInstanceId, peerMac, message, messageLength);
+ data.second.onMessageReceived(requestorInstanceId, peerMac, message, messageLength);
}
- private ConfigRequest mergeConfigRequests() {
+ private void onNanDownLocal() {
if (VDBG) {
- Log.v(TAG, "mergeConfigRequests(): mClients=[" + mClients + "]");
+ Log.v(TAG, "onNanDown");
}
- if (mClients.size() == 0) {
+ mClients.clear();
+ mCurrentNanConfiguration = null;
+ mSm.onNanDownCleanupSendQueueState();
+ mDataPathMgr.onNanDownCleanupDataPaths();
+ mCurrentDiscoveryInterfaceMac = ALL_ZERO_MAC;
+ }
+
+ /*
+ * Utilities
+ */
+
+ private Pair<WifiNanClientState, WifiNanSessionState> getClientSessionForPubSubId(
+ int pubSubId) {
+ for (int i = 0; i < mClients.size(); ++i) {
+ WifiNanClientState client = mClients.valueAt(i);
+ WifiNanSessionState session = client.getNanSessionStateForPubSubId(pubSubId);
+ if (session != null) {
+ return new Pair<>(client, session);
+ }
+ }
+
+ return null;
+ }
+
+ private ConfigRequest mergeConfigRequests(ConfigRequest configRequest) {
+ if (VDBG) {
+ Log.v(TAG, "mergeConfigRequests(): mClients=[" + mClients + "], configRequest="
+ + configRequest);
+ }
+
+ if (mClients.size() == 0 && configRequest == null) {
Log.e(TAG, "mergeConfigRequests: invalid state - called with 0 clients registered!");
return null;
}
- if (mClients.size() == 1) {
- return mClients.valueAt(0).getConfigRequest();
- }
-
// TODO: continue working on merge algorithm:
// - if any request 5g: enable
// - maximal master preference
// - cluster range covering all requests: assume that [0,max] is a
// non-request
+ // - if any request identity change: enable
boolean support5gBand = false;
int masterPreference = 0;
boolean clusterIdValid = false;
int clusterLow = 0;
int clusterHigh = ConfigRequest.CLUSTER_ID_MAX;
+ boolean identityChange = false;
+ if (configRequest != null) {
+ support5gBand = configRequest.mSupport5gBand;
+ masterPreference = configRequest.mMasterPreference;
+ clusterIdValid = true;
+ clusterLow = configRequest.mClusterLow;
+ clusterHigh = configRequest.mClusterHigh;
+ identityChange = configRequest.mEnableIdentityChangeCallback;
+ }
for (int i = 0; i < mClients.size(); ++i) {
ConfigRequest cr = mClients.valueAt(i).getConfigRequest();
@@ -1151,22 +2533,56 @@
}
clusterIdValid = true;
}
- }
- ConfigRequest.Builder builder = new ConfigRequest.Builder();
- builder.setSupport5gBand(support5gBand).setMasterPreference(masterPreference)
- .setClusterLow(clusterLow).setClusterHigh(clusterHigh);
- return builder.build();
+ if (cr.mEnableIdentityChangeCallback) {
+ identityChange = true;
+ }
+ }
+ return new ConfigRequest.Builder().setSupport5gBand(support5gBand)
+ .setMasterPreference(masterPreference).setClusterLow(clusterLow)
+ .setClusterHigh(clusterHigh).setEnableIdentityChangeCallback(identityChange)
+ .build();
}
+ private static String messageToString(Message msg) {
+ StringBuilder sb = new StringBuilder();
+
+ String s = sSmToString.get(msg.what);
+ if (s == null) {
+ s = "<unknown>";
+ }
+ sb.append(s).append("/");
+
+ if (msg.what == MESSAGE_TYPE_NOTIFICATION || msg.what == MESSAGE_TYPE_COMMAND
+ || msg.what == MESSAGE_TYPE_RESPONSE) {
+ s = sSmToString.get(msg.arg1);
+ if (s == null) {
+ s = "<unknown>";
+ }
+ sb.append(s);
+ }
+
+ if (msg.what == MESSAGE_TYPE_RESPONSE || msg.what == MESSAGE_TYPE_RESPONSE_TIMEOUT) {
+ sb.append(" (Transaction ID=").append(msg.arg2).append(")");
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Dump the internal state of the class.
+ */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("NanStateManager:");
pw.println(" mClients: [" + mClients + "]");
- pw.println(" mPendingResponses: [" + mPendingResponses + "]");
+ pw.println(" mUsageEnabled: " + mUsageEnabled);
pw.println(" mCapabilities: [" + mCapabilities + "]");
- pw.println(" mNextTransactionId: " + mNextTransactionId);
+ pw.println(" mCurrentNanConfiguration: " + mCurrentNanConfiguration);
for (int i = 0; i < mClients.size(); ++i) {
mClients.valueAt(i).dump(fd, pw, args);
}
+ mSm.dump(fd, pw, args);
+ mRtt.dump(fd, pw, args);
+ mDataPathMgr.dump(fd, pw, args);
}
}
diff --git a/service/java/com/android/server/wifi/scanner/SupplicantWifiScannerImpl.java b/service/java/com/android/server/wifi/scanner/SupplicantWifiScannerImpl.java
index 3f93c90..4d93e8f 100644
--- a/service/java/com/android/server/wifi/scanner/SupplicantWifiScannerImpl.java
+++ b/service/java/com/android/server/wifi/scanner/SupplicantWifiScannerImpl.java
@@ -68,7 +68,7 @@
private final ChannelHelper mChannelHelper;
private final Clock mClock;
- private Object mSettingsLock = new Object();
+ private final Object mSettingsLock = new Object();
// Next scan settings to apply when the previous scan completes
private WifiNative.ScanSettings mPendingBackgroundScanSettings = null;
@@ -341,7 +341,7 @@
ChannelCollection allFreqs = mChannelHelper.createChannelCollection();
Set<Integer> hiddenNetworkIdSet = new HashSet<Integer>();
final LastScanSettings newScanSettings =
- new LastScanSettings(mClock.elapsedRealtime());
+ new LastScanSettings(mClock.getElapsedSinceBootMillis());
// Update scan settings if there is a pending scan
if (!mBackgroundScanPaused) {
@@ -397,7 +397,7 @@
mNextBackgroundScanPeriod++;
mBackgroundScanPeriodPending = false;
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- mClock.elapsedRealtime() + mBackgroundScanSettings.base_period_ms,
+ mClock.getElapsedSinceBootMillis() + mBackgroundScanSettings.base_period_ms,
BACKGROUND_PERIOD_ALARM_TAG, mScanPeriodListener, mEventHandler);
}
}
@@ -443,7 +443,7 @@
}
mLastScanSettings = newScanSettings;
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- mClock.elapsedRealtime() + SCAN_TIMEOUT_MS,
+ mClock.getElapsedSinceBootMillis() + SCAN_TIMEOUT_MS,
TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler);
} else {
Log.e(TAG, "Failed to start scan, freqs=" + freqs);
@@ -1042,7 +1042,7 @@
return true;
}
- mLastPnoChangeTimeStamp = mClock.elapsedRealtime();
+ mLastPnoChangeTimeStamp = mClock.getElapsedSinceBootMillis();
if (mWifiNative.setPnoScan(enable)) {
Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to " + enable);
mCurrentPnoState = enable;
@@ -1074,14 +1074,14 @@
boolean isSuccess = true;
mExpectedPnoState = enable;
if (!mWaitForTimer) {
- long timeDifference = mClock.elapsedRealtime() - mLastPnoChangeTimeStamp;
+ long timeDifference = mClock.getElapsedSinceBootMillis() - mLastPnoChangeTimeStamp;
if (timeDifference >= MINIMUM_PNO_GAP_MS) {
isSuccess = updatePnoState(enable);
} else {
long alarmTimeout = MINIMUM_PNO_GAP_MS - timeDifference;
Log.d(TAG, "Start PNO timer with delay " + alarmTimeout);
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- mClock.elapsedRealtime() + alarmTimeout, PNO_DEBOUNCER_ALARM_TAG,
+ mClock.getElapsedSinceBootMillis() + alarmTimeout, PNO_DEBOUNCER_ALARM_TAG,
mAlarmListener, mEventHandler);
mWaitForTimer = true;
}
diff --git a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
index 6ae2237..fdf3dfa 100644
--- a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
+++ b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
@@ -2284,7 +2284,7 @@
long unchangedDelay = settings.unchangedSampleSize * settings.periodInMs;
mAlarmManager.cancel(mTimeoutIntent);
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- mClock.elapsedRealtime() + unchangedDelay,
+ mClock.getElapsedSinceBootMillis() + unchangedDelay,
mTimeoutIntent);
break;
case WifiScanner.CMD_SCAN_RESULT:
@@ -2295,7 +2295,7 @@
STATIONARY_SCAN_PERIOD_MS);
mWifiChangeDetected = false;
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- mClock.elapsedRealtime() + MOVING_STATE_TIMEOUT_MS,
+ mClock.getElapsedSinceBootMillis() + MOVING_STATE_TIMEOUT_MS,
mTimeoutIntent);
mScanResultsPending = false;
}
diff --git a/service/java/com/android/server/wifi/util/ScanDetailUtil.java b/service/java/com/android/server/wifi/util/ScanDetailUtil.java
deleted file mode 100644
index a83900e..0000000
--- a/service/java/com/android/server/wifi/util/ScanDetailUtil.java
+++ /dev/null
@@ -1,40 +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.
- */
-
-package com.android.server.wifi.util;
-
-import android.net.wifi.ScanResult;
-
-import com.android.server.wifi.ScanDetail;
-import com.android.server.wifi.hotspot2.NetworkDetail;
-
-/**
- * Utility for converting a ScanResult to a ScanDetail.
- * Only fields that are supported in ScanResult are copied.
- */
-public class ScanDetailUtil {
- private ScanDetailUtil() { /* not constructable */ }
-
- /**
- * This method should only be used when the informationElements field in the provided scan
- * result is filled in with the IEs from the beacon.
- */
- public static ScanDetail toScanDetail(ScanResult scanResult) {
- NetworkDetail networkDetail = new NetworkDetail(scanResult.BSSID,
- scanResult.informationElements, scanResult.anqpLines, scanResult.frequency);
- return new ScanDetail(scanResult, networkDetail, null);
- }
-}
diff --git a/service/java/com/android/server/wifi/util/ScanResultUtil.java b/service/java/com/android/server/wifi/util/ScanResultUtil.java
new file mode 100644
index 0000000..3f1e98f
--- /dev/null
+++ b/service/java/com/android/server/wifi/util/ScanResultUtil.java
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi.util;
+
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+
+import com.android.server.wifi.ScanDetail;
+import com.android.server.wifi.WifiConfigurationUtil;
+import com.android.server.wifi.hotspot2.NetworkDetail;
+
+/**
+ * Scan result utility for any {@link ScanResult} related operations.
+ * Currently contains:
+ * > Helper method for converting a ScanResult to a ScanDetail.
+ * Only fields that are supported in ScanResult are copied.
+ * > Helper methods to identify the encryption of a ScanResult.
+ */
+public class ScanResultUtil {
+ private ScanResultUtil() { /* not constructable */ }
+
+ /**
+ * This method should only be used when the informationElements field in the provided scan
+ * result is filled in with the IEs from the beacon.
+ */
+ public static ScanDetail toScanDetail(ScanResult scanResult) {
+ NetworkDetail networkDetail = new NetworkDetail(scanResult.BSSID,
+ scanResult.informationElements, scanResult.anqpLines, scanResult.frequency);
+ return new ScanDetail(scanResult, networkDetail, null);
+ }
+
+ /**
+ * Helper method to check if the provided |scanResult| corresponds to a PSK network or not.
+ * This checks if the provided capabilities string contains PSK encryption type or not.
+ */
+ public static boolean isScanResultForPskNetwork(ScanResult scanResult) {
+ return scanResult.capabilities.contains("PSK");
+ }
+
+ /**
+ * Helper method to check if the provided |scanResult| corresponds to a EAP network or not.
+ * This checks if the provided capabilities string contains EAP encryption type or not.
+ */
+ public static boolean isScanResultForEapNetwork(ScanResult scanResult) {
+ return scanResult.capabilities.contains("EAP");
+ }
+
+ /**
+ * Helper method to check if the provided |scanResult| corresponds to a WEP network or not.
+ * This checks if the provided capabilities string contains WEP encryption type or not.
+ */
+ public static boolean isScanResultForWepNetwork(ScanResult scanResult) {
+ return scanResult.capabilities.contains("WEP");
+ }
+
+ /**
+ * Helper method to check if the provided |scanResult| corresponds to an open network or not.
+ * This checks if the provided capabilities string does not contain either of WEP, PSK or EAP
+ * encryption types or not.
+ */
+ public static boolean isScanResultForOpenNetwork(ScanResult scanResult) {
+ return !(isScanResultForWepNetwork(scanResult) || isScanResultForPskNetwork(scanResult)
+ || isScanResultForEapNetwork(scanResult));
+ }
+
+ /**
+ * Helper method to check if the provided |scanResult| and |config| have the same
+ * encryption type.
+ */
+ public static boolean doesScanResultEncryptionMatchWithNetwork(
+ ScanResult scanResult, WifiConfiguration config) {
+ 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;
+ }
+
+}
diff --git a/service/java/com/android/server/wifi/util/XmlUtil.java b/service/java/com/android/server/wifi/util/XmlUtil.java
new file mode 100644
index 0000000..dffe602
--- /dev/null
+++ b/service/java/com/android/server/wifi/util/XmlUtil.java
@@ -0,0 +1,1027 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi.util;
+
+import android.net.IpConfiguration;
+import android.net.IpConfiguration.IpAssignment;
+import android.net.IpConfiguration.ProxySettings;
+import android.net.LinkAddress;
+import android.net.NetworkUtils;
+import android.net.ProxyInfo;
+import android.net.RouteInfo;
+import android.net.StaticIpConfiguration;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.util.BitSet;
+import java.util.HashMap;
+
+/**
+ * Utils for manipulating XML data. This is essentially a wrapper over XmlUtils provided by core.
+ * The utility provides methods to write/parse section headers and write/parse values.
+ * This utility is designed for formatting the XML into the following format:
+ * <Document Header>
+ * <Section 1 Header>
+ * <Value 1>
+ * <Value 2>
+ * ...
+ * <Sub Section 1 Header>
+ * <Value 1>
+ * <Value 2>
+ * ...
+ * </Sub Section 1 Header>
+ * </Section 1 Header>
+ * </Document Header>
+ */
+public class XmlUtil {
+ private static final String TAG = "WifiXmlUtil";
+
+ /**
+ * Ensure that the XML stream is at a start tag or the end of document.
+ *
+ * @throws XmlPullParserException if parsing errors occur.
+ */
+ private static void gotoStartTag(XmlPullParser in)
+ throws XmlPullParserException, IOException {
+ int type = in.getEventType();
+ while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) {
+ type = in.next();
+ }
+ }
+
+ /**
+ * Ensure that the XML stream is at an end tag or the end of document.
+ *
+ * @throws XmlPullParserException if parsing errors occur.
+ */
+ private static void gotoEndTag(XmlPullParser in)
+ throws XmlPullParserException, IOException {
+ int type = in.getEventType();
+ while (type != XmlPullParser.END_TAG && type != XmlPullParser.END_DOCUMENT) {
+ type = in.next();
+ }
+ }
+
+ /**
+ * Start processing the XML stream at the document header.
+ *
+ * @param in XmlPullParser instance pointing to the XML stream.
+ * @param headerName expected name for the start tag.
+ * @throws XmlPullParserException if parsing errors occur.
+ */
+ public static void gotoDocumentStart(XmlPullParser in, String headerName)
+ throws XmlPullParserException, IOException {
+ XmlUtils.beginDocument(in, headerName);
+ }
+
+ /**
+ * Move the XML stream to the next section header or indicate if there are no more sections.
+ * The provided outerDepth is used to find sub sections within that depth.
+ *
+ * Use this to move across sections if the ordering of sections are variable. The returned name
+ * can be used to decide what section is next.
+ *
+ * @param in XmlPullParser instance pointing to the XML stream.
+ * @param headerName An array of one string, used to return the name of the next section.
+ * @param outerDepth Find section within this depth.
+ * @return {@code true} if a next section is found, {@code false} if there are no more sections.
+ * @throws XmlPullParserException if parsing errors occur.
+ */
+ public static boolean gotoNextSectionOrEnd(
+ XmlPullParser in, String[] headerName, int outerDepth)
+ throws XmlPullParserException, IOException {
+ if (XmlUtils.nextElementWithin(in, outerDepth)) {
+ headerName[0] = in.getName();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Move the XML stream to the next section header or indicate if there are no more sections.
+ * If a section, exists ensure that the name matches the provided name.
+ * The provided outerDepth is used to find sub sections within that depth.
+ *
+ * Use this to move across repeated sections until the end.
+ *
+ * @param in XmlPullParser instance pointing to the XML stream.
+ * @param expectedName expected name for the section header.
+ * @param outerDepth Find section within this depth.
+ * @return {@code true} if a next section is found, {@code false} if there are no more sections.
+ * @throws XmlPullParserException if the section header name does not match |expectedName|,
+ * or if parsing errors occur.
+ */
+ public static boolean gotoNextSectionWithNameOrEnd(
+ XmlPullParser in, String expectedName, int outerDepth)
+ throws XmlPullParserException, IOException {
+ String[] headerName = new String[1];
+ if (gotoNextSectionOrEnd(in, headerName, outerDepth)) {
+ if (headerName[0].equals(expectedName)) {
+ return true;
+ }
+ throw new XmlPullParserException(
+ "Next section name does not match expected name: " + expectedName);
+ }
+ return false;
+ }
+
+ /**
+ * Move the XML stream to the next section header and ensure that the name matches the provided
+ * name.
+ * The provided outerDepth is used to find sub sections within that depth.
+ *
+ * Use this to move across sections if the ordering of sections are fixed.
+ *
+ * @param in XmlPullParser instance pointing to the XML stream.
+ * @param expectedName expected name for the section header.
+ * @param outerDepth Find section within this depth.
+ * @throws XmlPullParserException if the section header name does not match |expectedName|,
+ * there are no more sections or if parsing errors occur.
+ */
+ public static void gotoNextSectionWithName(
+ XmlPullParser in, String expectedName, int outerDepth)
+ throws XmlPullParserException, IOException {
+ if (!gotoNextSectionWithNameOrEnd(in, expectedName, outerDepth)) {
+ throw new XmlPullParserException("Section not found. Expected: " + expectedName);
+ }
+ }
+
+ /**
+ * Checks if the stream is at the end of a section of values. This moves the stream to next tag
+ * and checks if it finds an end tag at the specified depth.
+ *
+ * @param in XmlPullParser instance pointing to the XML stream.
+ * @param sectionDepth depth of the start tag of this section. Used to match the end tag.
+ * @return {@code true} if a end tag at the provided depth is found, {@code false} otherwise
+ * @throws XmlPullParserException if parsing errors occur.
+ */
+ public static boolean isNextSectionEnd(XmlPullParser in, int sectionDepth)
+ throws XmlPullParserException, IOException {
+ return !XmlUtils.nextElementWithin(in, sectionDepth);
+ }
+
+ /**
+ * Read the current value in the XML stream using core XmlUtils and stores the retrieved
+ * value name in the string provided. This method reads the value contained in current start
+ * tag.
+ * Note: Because there could be genuine null values being read from the XML, this method raises
+ * an exception to indicate errors.
+ *
+ * @param in XmlPullParser instance pointing to the XML stream.
+ * @param valueName An array of one string, used to return the name attribute
+ * of the value's tag.
+ * @return value retrieved from the XML stream.
+ * @throws XmlPullParserException if parsing errors occur.
+ */
+ public static Object readCurrentValue(XmlPullParser in, String[] valueName)
+ throws XmlPullParserException, IOException {
+ Object value = XmlUtils.readValueXml(in, valueName);
+ // XmlUtils.readValue does not always move the stream to the end of the tag. So, move
+ // it to the end tag before returning from here.
+ gotoEndTag(in);
+ return value;
+ }
+
+ /**
+ * Read the next value in the XML stream using core XmlUtils and ensure that it matches the
+ * provided name. This method moves the stream to the next start tag and reads the value
+ * contained in it.
+ * Note: Because there could be genuine null values being read from the XML, this method raises
+ * an exception to indicate errors.
+ *
+ * @param in XmlPullParser instance pointing to the XML stream.
+ * @return value retrieved from the XML stream.
+ * @throws XmlPullParserException if the value read does not match |expectedName|,
+ * or if parsing errors occur.
+ */
+ public static Object readNextValueWithName(XmlPullParser in, String expectedName)
+ throws XmlPullParserException, IOException {
+ String[] valueName = new String[1];
+ XmlUtils.nextElement(in);
+ Object value = readCurrentValue(in, valueName);
+ if (valueName[0].equals(expectedName)) {
+ return value;
+ }
+ throw new XmlPullParserException(
+ "Value not found. Expected: " + expectedName + ", but got: " + valueName[0]);
+ }
+
+ /**
+ * Write the XML document start with the provided document header name.
+ *
+ * @param out XmlSerializer instance pointing to the XML stream.
+ * @param headerName name for the start tag.
+ */
+ public static void writeDocumentStart(XmlSerializer out, String headerName)
+ throws IOException {
+ out.startDocument(null, true);
+ out.startTag(null, headerName);
+ }
+
+ /**
+ * Write the XML document end with the provided document header name.
+ *
+ * @param out XmlSerializer instance pointing to the XML stream.
+ * @param headerName name for the end tag.
+ */
+ public static void writeDocumentEnd(XmlSerializer out, String headerName)
+ throws IOException {
+ out.endTag(null, headerName);
+ out.endDocument();
+ }
+
+ /**
+ * Write a section start header tag with the provided section name.
+ *
+ * @param out XmlSerializer instance pointing to the XML stream.
+ * @param headerName name for the start tag.
+ */
+ public static void writeNextSectionStart(XmlSerializer out, String headerName)
+ throws IOException {
+ out.startTag(null, headerName);
+ }
+
+ /**
+ * Write a section end header tag with the provided section name.
+ *
+ * @param out XmlSerializer instance pointing to the XML stream.
+ * @param headerName name for the end tag.
+ */
+ public static void writeNextSectionEnd(XmlSerializer out, String headerName)
+ throws IOException {
+ out.endTag(null, headerName);
+ }
+
+ /**
+ * Write the value with the provided name in the XML stream using core XmlUtils.
+ *
+ * @param out XmlSerializer instance pointing to the XML stream.
+ * @param name name of the value.
+ * @param value value to be written.
+ */
+ public static void writeNextValue(XmlSerializer out, String name, Object value)
+ throws XmlPullParserException, IOException {
+ XmlUtils.writeValueXml(value, name, out);
+ }
+
+ /**
+ * Utility class to serialize and deseriaize {@link WifiConfiguration} object to XML &
+ * vice versa.
+ * This is used by both {@link com.android.server.wifi.WifiConfigStore} &
+ * {@link com.android.server.wifi.WifiBackupRestore} modules.
+ * The |writeConfigurationToXml| has 2 versions, one for backup and one for config store.
+ * There is only 1 version of |parseXmlToConfiguration| for both backup & config store.
+ * The parse method is written so that any element added/deleted in future revisions can
+ * be easily handled.
+ */
+ public static class WifiConfigurationXmlUtil {
+ /**
+ * List of XML tags corresponding to WifiConfiguration object elements.
+ */
+ public static final String XML_TAG_SSID = "SSID";
+ public static final String XML_TAG_BSSID = "BSSID";
+ public static final String XML_TAG_CONFIG_KEY = "ConfigKey";
+ public static final String XML_TAG_PRE_SHARED_KEY = "PreSharedKey";
+ public static final String XML_TAG_WEP_KEYS = "WEPKeys";
+ public static final String XML_TAG_WEP_TX_KEY_INDEX = "WEPTxKeyIndex";
+ public static final String XML_TAG_HIDDEN_SSID = "HiddenSSID";
+ public static final String XML_TAG_ALLOWED_KEY_MGMT = "AllowedKeyMgmt";
+ public static final String XML_TAG_ALLOWED_PROTOCOLS = "AllowedProtocols";
+ public static final String XML_TAG_ALLOWED_AUTH_ALGOS = "AllowedAuthAlgos";
+ public static final String XML_TAG_SHARED = "Shared";
+ public static final String XML_TAG_FQDN = "FQDN";
+ public static final String XML_TAG_PROVIDER_FRIENDLY_NAME = "ProviderFriendlyName";
+ public static final String XML_TAG_LINKED_NETWORKS_LIST = "LinkedNetworksList";
+ public static final String XML_TAG_DEFAULT_GW_MAC_ADDRESS = "DefaultGwMacAddress";
+ public static final String XML_TAG_VALIDATED_INTERNET_ACCESS = "ValidatedInternetAccess";
+ public static final String XML_TAG_NO_INTERNET_ACCESS_EXPECTED = "NoInternetAccessExpected";
+ public static final String XML_TAG_USER_APPROVED = "UserApproved";
+ public static final String XML_TAG_METERED_HINT = "MeteredHint";
+ public static final String XML_TAG_USE_EXTERNAL_SCORES = "UseExternalScores";
+ public static final String XML_TAG_NUM_ASSOCIATION = "NumAssociation";
+ public static final String XML_TAG_CREATOR_UID = "CreatorUid";
+ public static final String XML_TAG_CREATOR_NAME = "CreatorName";
+ public static final String XML_TAG_CREATION_TIME = "CreationTime";
+ public static final String XML_TAG_LAST_UPDATE_UID = "LastUpdateUid";
+ public static final String XML_TAG_LAST_UPDATE_NAME = "LastUpdateName";
+ public static final String XML_TAG_LAST_CONNECT_UID = "LastConnectUid";
+
+ /**
+ * Write WepKeys to the XML stream.
+ * WepKeys array is intialized in WifiConfiguration constructor, but all of the elements
+ * are set to null. User may chose to set any one of the key elements in WifiConfiguration.
+ * XmlUtils serialization doesn't handle this array of nulls well .
+ * So, write empty strings if some of the keys are not initialized and null if all of
+ * the elements are empty.
+ */
+ private static void writeWepKeysToXml(XmlSerializer out, String[] wepKeys)
+ throws XmlPullParserException, IOException {
+ String[] wepKeysToWrite = new String[wepKeys.length];
+ boolean hasWepKey = false;
+ for (int i = 0; i < wepKeys.length; i++) {
+ if (wepKeys[i] == null) {
+ wepKeysToWrite[i] = new String();
+ } else {
+ wepKeysToWrite[i] = wepKeys[i];
+ hasWepKey = true;
+ }
+ }
+ if (hasWepKey) {
+ XmlUtil.writeNextValue(out, XML_TAG_WEP_KEYS, wepKeysToWrite);
+ } else {
+ XmlUtil.writeNextValue(out, XML_TAG_WEP_KEYS, null);
+ }
+ }
+
+ /**
+ * Write the Configuration data elements that are common for backup & config store to the
+ * XML stream.
+ *
+ * @param out XmlSerializer instance pointing to the XML stream.
+ * @param configuration WifiConfiguration object to be serialized.
+ */
+ public static void writeCommonElementsToXml(
+ XmlSerializer out, WifiConfiguration configuration)
+ throws XmlPullParserException, IOException {
+ XmlUtil.writeNextValue(out, XML_TAG_CONFIG_KEY, configuration.configKey());
+ XmlUtil.writeNextValue(out, XML_TAG_SSID, configuration.SSID);
+ XmlUtil.writeNextValue(out, XML_TAG_BSSID, configuration.BSSID);
+ XmlUtil.writeNextValue(out, XML_TAG_PRE_SHARED_KEY, configuration.preSharedKey);
+ writeWepKeysToXml(out, configuration.wepKeys);
+ XmlUtil.writeNextValue(out, XML_TAG_WEP_TX_KEY_INDEX, configuration.wepTxKeyIndex);
+ XmlUtil.writeNextValue(out, XML_TAG_HIDDEN_SSID, configuration.hiddenSSID);
+ XmlUtil.writeNextValue(
+ out, XML_TAG_ALLOWED_KEY_MGMT,
+ configuration.allowedKeyManagement.toByteArray());
+ XmlUtil.writeNextValue(
+ out, XML_TAG_ALLOWED_PROTOCOLS,
+ configuration.allowedProtocols.toByteArray());
+ XmlUtil.writeNextValue(
+ out, XML_TAG_ALLOWED_AUTH_ALGOS,
+ configuration.allowedAuthAlgorithms.toByteArray());
+ XmlUtil.writeNextValue(out, XML_TAG_SHARED, configuration.shared);
+ }
+
+ /**
+ * Write the Configuration data elements for backup from the provided Configuration to the
+ * XML stream.
+ * Note: This is a subset of the elements serialized for config store.
+ *
+ * @param out XmlSerializer instance pointing to the XML stream.
+ * @param configuration WifiConfiguration object to be serialized.
+ */
+ public static void writeToXmlForBackup(XmlSerializer out, WifiConfiguration configuration)
+ throws XmlPullParserException, IOException {
+ writeCommonElementsToXml(out, configuration);
+ }
+
+ /**
+ * Write the Configuration data elements for config store from the provided Configuration
+ * to the XML stream.
+ *
+ * @param out XmlSerializer instance pointing to the XML stream.
+ * @param configuration WifiConfiguration object to be serialized.
+ */
+ public static void writeToXmlForConfigStore(
+ XmlSerializer out, WifiConfiguration configuration)
+ throws XmlPullParserException, IOException {
+ writeCommonElementsToXml(out, configuration);
+ XmlUtil.writeNextValue(out, XML_TAG_FQDN, configuration.FQDN);
+ XmlUtil.writeNextValue(
+ out, XML_TAG_PROVIDER_FRIENDLY_NAME, configuration.providerFriendlyName);
+ XmlUtil.writeNextValue(
+ out, XML_TAG_LINKED_NETWORKS_LIST, configuration.linkedConfigurations);
+ XmlUtil.writeNextValue(
+ out, XML_TAG_DEFAULT_GW_MAC_ADDRESS, configuration.defaultGwMacAddress);
+ XmlUtil.writeNextValue(
+ out, XML_TAG_VALIDATED_INTERNET_ACCESS, configuration.validatedInternetAccess);
+ XmlUtil.writeNextValue(
+ out, XML_TAG_NO_INTERNET_ACCESS_EXPECTED,
+ configuration.noInternetAccessExpected);
+ XmlUtil.writeNextValue(out, XML_TAG_USER_APPROVED, configuration.userApproved);
+ XmlUtil.writeNextValue(out, XML_TAG_METERED_HINT, configuration.meteredHint);
+ XmlUtil.writeNextValue(
+ out, XML_TAG_USE_EXTERNAL_SCORES, configuration.useExternalScores);
+ XmlUtil.writeNextValue(out, XML_TAG_NUM_ASSOCIATION, configuration.numAssociation);
+ XmlUtil.writeNextValue(out, XML_TAG_CREATOR_UID, configuration.creatorUid);
+ XmlUtil.writeNextValue(out, XML_TAG_CREATOR_NAME, configuration.creatorName);
+ XmlUtil.writeNextValue(out, XML_TAG_CREATION_TIME, configuration.creationTime);
+ XmlUtil.writeNextValue(out, XML_TAG_LAST_UPDATE_UID, configuration.lastUpdateUid);
+ XmlUtil.writeNextValue(out, XML_TAG_LAST_UPDATE_NAME, configuration.lastUpdateName);
+ XmlUtil.writeNextValue(out, XML_TAG_LAST_CONNECT_UID, configuration.lastConnectUid);
+ }
+
+ /**
+ * Populate wepKeys array elements only if they were non-empty in the backup data.
+ *
+ * @throws XmlPullParserException if parsing errors occur.
+ */
+ private static void populateWepKeysFromXmlValue(Object value, String[] wepKeys)
+ throws XmlPullParserException, IOException {
+ String[] wepKeysInData = (String[]) value;
+ if (wepKeysInData == null) {
+ return;
+ }
+ if (wepKeysInData.length != wepKeys.length) {
+ throw new XmlPullParserException(
+ "Invalid Wep Keys length: " + wepKeysInData.length);
+ }
+ for (int i = 0; i < wepKeys.length; i++) {
+ if (wepKeysInData[i].isEmpty()) {
+ wepKeys[i] = null;
+ } else {
+ wepKeys[i] = wepKeysInData[i];
+ }
+ }
+ }
+
+ /**
+ * Parses the configuration data elements from the provided XML stream to a
+ * WifiConfiguration object.
+ * Note: This is used for parsing both backup data and config store data. Looping through
+ * the tags make it easy to add or remove elements in the future versions if needed.
+ *
+ * @param in XmlPullParser instance pointing to the XML stream.
+ * @param outerTagDepth depth of the outer tag in the XML document.
+ * @return Pair<Config key, WifiConfiguration object> if parsing is successful,
+ * null otherwise.
+ */
+ public static Pair<String, WifiConfiguration> parseFromXml(
+ XmlPullParser in, int outerTagDepth)
+ throws XmlPullParserException, IOException {
+ WifiConfiguration configuration = new WifiConfiguration();
+ String configKeyInData = null;
+
+ // Loop through and parse out all the elements from the stream within this section.
+ while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
+ String[] valueName = new String[1];
+ Object value = XmlUtil.readCurrentValue(in, valueName);
+ if (valueName[0] == null) {
+ throw new XmlPullParserException("Missing value name");
+ }
+ switch (valueName[0]) {
+ case XML_TAG_CONFIG_KEY:
+ configKeyInData = (String) value;
+ break;
+ case XML_TAG_SSID:
+ configuration.SSID = (String) value;
+ break;
+ case XML_TAG_BSSID:
+ configuration.BSSID = (String) value;
+ break;
+ case XML_TAG_PRE_SHARED_KEY:
+ configuration.preSharedKey = (String) value;
+ break;
+ case XML_TAG_WEP_KEYS:
+ populateWepKeysFromXmlValue(value, configuration.wepKeys);
+ break;
+ case XML_TAG_WEP_TX_KEY_INDEX:
+ configuration.wepTxKeyIndex = (int) value;
+ break;
+ case XML_TAG_HIDDEN_SSID:
+ configuration.hiddenSSID = (boolean) value;
+ break;
+ case XML_TAG_ALLOWED_KEY_MGMT:
+ byte[] allowedKeyMgmt = (byte[]) value;
+ configuration.allowedKeyManagement = BitSet.valueOf(allowedKeyMgmt);
+ break;
+ case XML_TAG_ALLOWED_PROTOCOLS:
+ byte[] allowedProtocols = (byte[]) value;
+ configuration.allowedProtocols = BitSet.valueOf(allowedProtocols);
+ break;
+ case XML_TAG_ALLOWED_AUTH_ALGOS:
+ byte[] allowedAuthAlgorithms = (byte[]) value;
+ configuration.allowedAuthAlgorithms = BitSet.valueOf(allowedAuthAlgorithms);
+ break;
+ case XML_TAG_SHARED:
+ configuration.shared = (boolean) value;
+ break;
+ case XML_TAG_FQDN:
+ configuration.FQDN = (String) value;
+ break;
+ case XML_TAG_PROVIDER_FRIENDLY_NAME:
+ configuration.providerFriendlyName = (String) value;
+ break;
+ case XML_TAG_LINKED_NETWORKS_LIST:
+ configuration.linkedConfigurations = (HashMap<String, Integer>) value;
+ break;
+ case XML_TAG_DEFAULT_GW_MAC_ADDRESS:
+ configuration.defaultGwMacAddress = (String) value;
+ break;
+ case XML_TAG_VALIDATED_INTERNET_ACCESS:
+ configuration.validatedInternetAccess = (boolean) value;
+ break;
+ case XML_TAG_NO_INTERNET_ACCESS_EXPECTED:
+ configuration.noInternetAccessExpected = (boolean) value;
+ break;
+ case XML_TAG_USER_APPROVED:
+ configuration.userApproved = (int) value;
+ break;
+ case XML_TAG_METERED_HINT:
+ configuration.meteredHint = (boolean) value;
+ break;
+ case XML_TAG_USE_EXTERNAL_SCORES:
+ configuration.useExternalScores = (boolean) value;
+ break;
+ case XML_TAG_NUM_ASSOCIATION:
+ configuration.numAssociation = (int) value;
+ break;
+ case XML_TAG_CREATOR_UID:
+ configuration.creatorUid = (int) value;
+ break;
+ case XML_TAG_CREATOR_NAME:
+ configuration.creatorName = (String) value;
+ break;
+ case XML_TAG_CREATION_TIME:
+ configuration.creationTime = (String) value;
+ break;
+ case XML_TAG_LAST_UPDATE_UID:
+ configuration.lastUpdateUid = (int) value;
+ break;
+ case XML_TAG_LAST_UPDATE_NAME:
+ configuration.lastUpdateName = (String) value;
+ break;
+ case XML_TAG_LAST_CONNECT_UID:
+ configuration.lastConnectUid = (int) value;
+ break;
+ default:
+ throw new XmlPullParserException(
+ "Unknown value name found: " + valueName[0]);
+ }
+ }
+ return Pair.create(configKeyInData, configuration);
+ }
+ }
+
+ /**
+ * Utility class to serialize and deseriaize {@link IpConfiguration} object to XML & vice versa.
+ * This is used by both {@link com.android.server.wifi.WifiConfigStore} &
+ * {@link com.android.server.wifi.WifiBackupRestore} modules.
+ */
+ public static class IpConfigurationXmlUtil {
+
+ /**
+ * List of XML tags corresponding to IpConfiguration object elements.
+ */
+ public static final String XML_TAG_IP_ASSIGNMENT = "IpAssignment";
+ public static final String XML_TAG_LINK_ADDRESS = "LinkAddress";
+ public static final String XML_TAG_LINK_PREFIX_LENGTH = "LinkPrefixLength";
+ public static final String XML_TAG_GATEWAY_ADDRESS = "GatewayAddress";
+ public static final String XML_TAG_DNS_SERVER_ADDRESSES = "DNSServers";
+ public static final String XML_TAG_PROXY_SETTINGS = "ProxySettings";
+ public static final String XML_TAG_PROXY_HOST = "ProxyHost";
+ public static final String XML_TAG_PROXY_PORT = "ProxyPort";
+ public static final String XML_TAG_PROXY_PAC_FILE = "ProxyPac";
+ public static final String XML_TAG_PROXY_EXCLUSION_LIST = "ProxyExclusionList";
+
+ /**
+ * Write the static IP configuration data elements to XML stream.
+ */
+ private static void writeStaticIpConfigurationToXml(
+ XmlSerializer out, StaticIpConfiguration staticIpConfiguration)
+ throws XmlPullParserException, IOException {
+ if (staticIpConfiguration.ipAddress != null) {
+ XmlUtil.writeNextValue(
+ out, XML_TAG_LINK_ADDRESS,
+ staticIpConfiguration.ipAddress.getAddress().getHostAddress());
+ XmlUtil.writeNextValue(
+ out, XML_TAG_LINK_PREFIX_LENGTH,
+ staticIpConfiguration.ipAddress.getPrefixLength());
+ } else {
+ XmlUtil.writeNextValue(
+ out, XML_TAG_LINK_ADDRESS, null);
+ XmlUtil.writeNextValue(
+ out, XML_TAG_LINK_PREFIX_LENGTH, null);
+ }
+ if (staticIpConfiguration.gateway != null) {
+ XmlUtil.writeNextValue(
+ out, XML_TAG_GATEWAY_ADDRESS,
+ staticIpConfiguration.gateway.getHostAddress());
+ } else {
+ XmlUtil.writeNextValue(
+ out, XML_TAG_GATEWAY_ADDRESS, null);
+
+ }
+ if (staticIpConfiguration.dnsServers != null) {
+ // Create a string array of DNS server addresses
+ String[] dnsServers = new String[staticIpConfiguration.dnsServers.size()];
+ int dnsServerIdx = 0;
+ for (InetAddress inetAddr : staticIpConfiguration.dnsServers) {
+ dnsServers[dnsServerIdx++] = inetAddr.getHostAddress();
+ }
+ XmlUtil.writeNextValue(
+ out, XML_TAG_DNS_SERVER_ADDRESSES, dnsServers);
+ } else {
+ XmlUtil.writeNextValue(
+ out, XML_TAG_DNS_SERVER_ADDRESSES, null);
+ }
+ }
+
+ /**
+ * Write the IP configuration data elements from the provided Configuration to the XML
+ * stream.
+ *
+ * @param out XmlSerializer instance pointing to the XML stream.
+ * @param ipConfiguration IpConfiguration object to be serialized.
+ */
+ public static void writeToXml(XmlSerializer out, IpConfiguration ipConfiguration)
+ throws XmlPullParserException, IOException {
+ // Write IP assignment settings
+ XmlUtil.writeNextValue(out, XML_TAG_IP_ASSIGNMENT,
+ ipConfiguration.ipAssignment.toString());
+ switch (ipConfiguration.ipAssignment) {
+ case STATIC:
+ writeStaticIpConfigurationToXml(
+ out, ipConfiguration.getStaticIpConfiguration());
+ break;
+ default:
+ break;
+ }
+
+ // Write proxy settings
+ XmlUtil.writeNextValue(
+ out, XML_TAG_PROXY_SETTINGS,
+ ipConfiguration.proxySettings.toString());
+ switch (ipConfiguration.proxySettings) {
+ case STATIC:
+ XmlUtil.writeNextValue(
+ out, XML_TAG_PROXY_HOST,
+ ipConfiguration.httpProxy.getHost());
+ XmlUtil.writeNextValue(
+ out, XML_TAG_PROXY_PORT,
+ ipConfiguration.httpProxy.getPort());
+ XmlUtil.writeNextValue(
+ out, XML_TAG_PROXY_EXCLUSION_LIST,
+ ipConfiguration.httpProxy.getExclusionListAsString());
+ break;
+ case PAC:
+ XmlUtil.writeNextValue(
+ out, XML_TAG_PROXY_PAC_FILE,
+ ipConfiguration.httpProxy.getPacFileUrl().toString());
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Parse out the static IP configuration from the XML stream.
+ */
+ private static StaticIpConfiguration parseStaticIpConfigurationFromXml(XmlPullParser in)
+ throws XmlPullParserException, IOException {
+ StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration();
+
+ String linkAddressString =
+ (String) XmlUtil.readNextValueWithName(in, XML_TAG_LINK_ADDRESS);
+ Integer linkPrefixLength =
+ (Integer) XmlUtil.readNextValueWithName(in, XML_TAG_LINK_PREFIX_LENGTH);
+ if (linkAddressString != null && linkPrefixLength != null) {
+ LinkAddress linkAddress = new LinkAddress(
+ NetworkUtils.numericToInetAddress(linkAddressString),
+ linkPrefixLength);
+ if (linkAddress.getAddress() instanceof Inet4Address) {
+ staticIpConfiguration.ipAddress = linkAddress;
+ } else {
+ Log.w(TAG, "Non-IPv4 address: " + linkAddress);
+ }
+ }
+ String gatewayAddressString =
+ (String) XmlUtil.readNextValueWithName(in, XML_TAG_GATEWAY_ADDRESS);
+ if (gatewayAddressString != null) {
+ LinkAddress dest = null;
+ InetAddress gateway =
+ NetworkUtils.numericToInetAddress(gatewayAddressString);
+ RouteInfo route = new RouteInfo(dest, gateway);
+ if (route.isIPv4Default()) {
+ staticIpConfiguration.gateway = gateway;
+ } else {
+ Log.w(TAG, "Non-IPv4 default route: " + route);
+ }
+ }
+ String[] dnsServerAddressesString =
+ (String[]) XmlUtil.readNextValueWithName(in, XML_TAG_DNS_SERVER_ADDRESSES);
+ if (dnsServerAddressesString != null) {
+ for (String dnsServerAddressString : dnsServerAddressesString) {
+ InetAddress dnsServerAddress =
+ NetworkUtils.numericToInetAddress(dnsServerAddressString);
+ staticIpConfiguration.dnsServers.add(dnsServerAddress);
+ }
+ }
+ return staticIpConfiguration;
+ }
+
+ /**
+ * Parses the IP configuration data elements from the provided XML stream to an
+ * IpConfiguration object.
+ *
+ * @param in XmlPullParser instance pointing to the XML stream.
+ * @param outerTagDepth depth of the outer tag in the XML document.
+ * @return IpConfiguration object if parsing is successful, null otherwise.
+ */
+ public static IpConfiguration parseFromXml(XmlPullParser in, int outerTagDepth)
+ throws XmlPullParserException, IOException {
+ IpConfiguration ipConfiguration = new IpConfiguration();
+
+ // Parse out the IP assignment info first.
+ String ipAssignmentString =
+ (String) XmlUtil.readNextValueWithName(in, XML_TAG_IP_ASSIGNMENT);
+ IpAssignment ipAssignment = IpAssignment.valueOf(ipAssignmentString);
+ ipConfiguration.setIpAssignment(ipAssignment);
+ switch (ipAssignment) {
+ case STATIC:
+ ipConfiguration.setStaticIpConfiguration(parseStaticIpConfigurationFromXml(in));
+ break;
+ case DHCP:
+ case UNASSIGNED:
+ break;
+ default:
+ throw new XmlPullParserException("Unknown ip assignment type: " + ipAssignment);
+ }
+
+ // Parse out the proxy settings next.
+ String proxySettingsString =
+ (String) XmlUtil.readNextValueWithName(in, XML_TAG_PROXY_SETTINGS);
+ ProxySettings proxySettings = ProxySettings.valueOf(proxySettingsString);
+ ipConfiguration.setProxySettings(proxySettings);
+ switch (proxySettings) {
+ case STATIC:
+ String proxyHost =
+ (String) XmlUtil.readNextValueWithName(in, XML_TAG_PROXY_HOST);
+ int proxyPort =
+ (int) XmlUtil.readNextValueWithName(in, XML_TAG_PROXY_PORT);
+ String proxyExclusionList =
+ (String) XmlUtil.readNextValueWithName(
+ in, XML_TAG_PROXY_EXCLUSION_LIST);
+ ipConfiguration.setHttpProxy(
+ new ProxyInfo(proxyHost, proxyPort, proxyExclusionList));
+ break;
+ case PAC:
+ String proxyPacFile =
+ (String) XmlUtil.readNextValueWithName(in, XML_TAG_PROXY_PAC_FILE);
+ ipConfiguration.setHttpProxy(new ProxyInfo(proxyPacFile));
+ break;
+ case NONE:
+ case UNASSIGNED:
+ break;
+ default:
+ throw new XmlPullParserException(
+ "Unknown proxy settings type: " + proxySettings);
+ }
+ return ipConfiguration;
+ }
+ }
+
+ /**
+ * Utility class to serialize and deseriaize {@link NetworkSelectionStatus} object to XML &
+ * vice versa. This is used by {@link com.android.server.wifi.WifiConfigStore} module.
+ */
+ public static class NetworkSelectionStatusXmlUtil {
+
+ /**
+ * List of XML tags corresponding to NetworkSelectionStatus object elements.
+ */
+ public static final String XML_TAG_SELECTION_STATUS = "SelectionStatus";
+ public static final String XML_TAG_DISABLE_REASON = "DisableReason";
+ public static final String XML_TAG_CONNECT_CHOICE = "ConnectChoice";
+ public static final String XML_TAG_CONNECT_CHOICE_TIMESTAMP = "ConnectChoiceTimeStamp";
+ public static final String XML_TAG_HAS_EVER_CONNECTED = "HasEverConnected";
+
+ /**
+ * Write the NetworkSelectionStatus data elements from the provided status to the XML
+ * stream.
+ *
+ * @param out XmlSerializer instance pointing to the XML stream.
+ * @param status NetworkSelectionStatus object to be serialized.
+ */
+ public static void writeToXml(XmlSerializer out, NetworkSelectionStatus status)
+ throws XmlPullParserException, IOException {
+ // Don't persist blacklists across reboots. So, if the status is temporarily disabled,
+ // store the status as enabled. This will ensure that when the device reboots, it is
+ // still considered for network selection.
+ int selectionStatus = status.getNetworkSelectionStatus();
+ if (selectionStatus == NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED) {
+ selectionStatus = NetworkSelectionStatus.NETWORK_SELECTION_ENABLED;
+ }
+ XmlUtil.writeNextValue(out, XML_TAG_SELECTION_STATUS, selectionStatus);
+ XmlUtil.writeNextValue(
+ out, XML_TAG_DISABLE_REASON, status.getNetworkSelectionDisableReason());
+ XmlUtil.writeNextValue(out, XML_TAG_CONNECT_CHOICE, status.getConnectChoice());
+ XmlUtil.writeNextValue(
+ out, XML_TAG_CONNECT_CHOICE_TIMESTAMP, status.getConnectChoiceTimestamp());
+ XmlUtil.writeNextValue(out, XML_TAG_HAS_EVER_CONNECTED, status.getHasEverConnected());
+ }
+
+ /**
+ * Parses the NetworkSelectionStatus data elements from the provided XML stream to a
+ * NetworkSelectionStatus object.
+ *
+ * @param in XmlPullParser instance pointing to the XML stream.
+ * @param outerTagDepth depth of the outer tag in the XML document.
+ * @return NetworkSelectionStatus object if parsing is successful, null otherwise.
+ */
+ public static NetworkSelectionStatus parseFromXml(XmlPullParser in, int outerTagDepth)
+ throws XmlPullParserException, IOException {
+ NetworkSelectionStatus status = new NetworkSelectionStatus();
+
+ // Loop through and parse out all the elements from the stream within this section.
+ while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
+ String[] valueName = new String[1];
+ Object value = XmlUtil.readCurrentValue(in, valueName);
+ if (valueName[0] == null) {
+ throw new XmlPullParserException("Missing value name");
+ }
+ switch (valueName[0]) {
+ case XML_TAG_SELECTION_STATUS:
+ status.setNetworkSelectionStatus((int) value);
+ break;
+ case XML_TAG_DISABLE_REASON:
+ status.setNetworkSelectionDisableReason((int) value);
+ break;
+ case XML_TAG_CONNECT_CHOICE:
+ status.setConnectChoice((String) value);
+ break;
+ case XML_TAG_CONNECT_CHOICE_TIMESTAMP:
+ status.setConnectChoiceTimestamp((long) value);
+ break;
+ case XML_TAG_HAS_EVER_CONNECTED:
+ status.setHasEverConnected((boolean) value);
+ break;
+ default:
+ throw new XmlPullParserException(
+ "Unknown value name found: " + valueName[0]);
+ }
+ }
+ return status;
+ }
+ }
+
+ /**
+ * Utility class to serialize and deseriaize {@link WifiEnterpriseConfig} object to XML &
+ * vice versa. This is used by {@link com.android.server.wifi.WifiConfigStore} module.
+ */
+ public static class WifiEnterpriseConfigXmlUtil {
+
+ /**
+ * List of XML tags corresponding to WifiEnterpriseConfig object elements.
+ */
+ public static final String XML_TAG_IDENTITY = "Identity";
+ public static final String XML_TAG_ANON_IDENTITY = "AnonIdentity";
+ public static final String XML_TAG_PASSWORD = "Password";
+ public static final String XML_TAG_CLIENT_CERT = "ClientCert";
+ public static final String XML_TAG_CA_CERT = "CaCert";
+ public static final String XML_TAG_SUBJECT_MATCH = "SubjectMatch";
+ public static final String XML_TAG_ENGINE = "Engine";
+ public static final String XML_TAG_ENGINE_ID = "EngineId";
+ public static final String XML_TAG_PRIVATE_KEY_ID = "PrivateKeyId";
+ public static final String XML_TAG_ALT_SUBJECT_MATCH = "AltSubjectMatch";
+ public static final String XML_TAG_DOM_SUFFIX_MATCH = "DomSuffixMatch";
+ public static final String XML_TAG_CA_PATH = "CaPath";
+ public static final String XML_TAG_EAP_METHOD = "EapMethod";
+ public static final String XML_TAG_PHASE2_METHOD = "Phase2Method";
+
+ /**
+ * Write the WifiEnterpriseConfig data elements from the provided config to the XML
+ * stream.
+ *
+ * @param out XmlSerializer instance pointing to the XML stream.
+ * @param enterpriseConfig WifiEnterpriseConfig object to be serialized.
+ */
+ public static void writeToXml(XmlSerializer out, WifiEnterpriseConfig enterpriseConfig)
+ throws XmlPullParserException, IOException {
+ XmlUtil.writeNextValue(out, XML_TAG_IDENTITY,
+ enterpriseConfig.getFieldValue(WifiEnterpriseConfig.IDENTITY_KEY, ""));
+ XmlUtil.writeNextValue(out, XML_TAG_ANON_IDENTITY,
+ enterpriseConfig.getFieldValue(WifiEnterpriseConfig.ANON_IDENTITY_KEY, ""));
+ XmlUtil.writeNextValue(out, XML_TAG_PASSWORD,
+ enterpriseConfig.getFieldValue(WifiEnterpriseConfig.PASSWORD_KEY, ""));
+ XmlUtil.writeNextValue(out, XML_TAG_CLIENT_CERT,
+ enterpriseConfig.getFieldValue(WifiEnterpriseConfig.CLIENT_CERT_KEY, ""));
+ XmlUtil.writeNextValue(out, XML_TAG_CA_CERT,
+ enterpriseConfig.getFieldValue(WifiEnterpriseConfig.CA_CERT_KEY, ""));
+ XmlUtil.writeNextValue(out, XML_TAG_SUBJECT_MATCH,
+ enterpriseConfig.getFieldValue(WifiEnterpriseConfig.SUBJECT_MATCH_KEY, ""));
+ XmlUtil.writeNextValue(out, XML_TAG_ENGINE,
+ enterpriseConfig.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY, ""));
+ XmlUtil.writeNextValue(out, XML_TAG_ENGINE_ID,
+ enterpriseConfig.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, ""));
+ XmlUtil.writeNextValue(out, XML_TAG_PRIVATE_KEY_ID,
+ enterpriseConfig.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, ""));
+ XmlUtil.writeNextValue(out, XML_TAG_ALT_SUBJECT_MATCH,
+ enterpriseConfig.getFieldValue(WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY, ""));
+ XmlUtil.writeNextValue(out, XML_TAG_DOM_SUFFIX_MATCH,
+ enterpriseConfig.getFieldValue(WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY, ""));
+ XmlUtil.writeNextValue(out, XML_TAG_CA_PATH,
+ enterpriseConfig.getFieldValue(WifiEnterpriseConfig.CA_PATH_KEY, ""));
+ XmlUtil.writeNextValue(out, XML_TAG_EAP_METHOD, enterpriseConfig.getEapMethod());
+ XmlUtil.writeNextValue(out, XML_TAG_PHASE2_METHOD, enterpriseConfig.getPhase2Method());
+ }
+
+ /**
+ * Parses the data elements from the provided XML stream to a WifiEnterpriseConfig object.
+ *
+ * @param in XmlPullParser instance pointing to the XML stream.
+ * @param outerTagDepth depth of the outer tag in the XML document.
+ * @return WifiEnterpriseConfig object if parsing is successful, null otherwise.
+ */
+ public static WifiEnterpriseConfig parseFromXml(XmlPullParser in, int outerTagDepth)
+ throws XmlPullParserException, IOException {
+ WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+
+ // Loop through and parse out all the elements from the stream within this section.
+ while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
+ String[] valueName = new String[1];
+ Object value = XmlUtil.readCurrentValue(in, valueName);
+ if (valueName[0] == null) {
+ throw new XmlPullParserException("Missing value name");
+ }
+ switch (valueName[0]) {
+ case XML_TAG_IDENTITY:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.IDENTITY_KEY, (String) value);
+ break;
+ case XML_TAG_ANON_IDENTITY:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.ANON_IDENTITY_KEY, (String) value);
+ break;
+ case XML_TAG_PASSWORD:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.PASSWORD_KEY, (String) value);
+ break;
+ case XML_TAG_CLIENT_CERT:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.CLIENT_CERT_KEY, (String) value);
+ break;
+ case XML_TAG_CA_CERT:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.CA_CERT_KEY, (String) value);
+ break;
+ case XML_TAG_SUBJECT_MATCH:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.SUBJECT_MATCH_KEY, (String) value);
+ break;
+ case XML_TAG_ENGINE:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.ENGINE_KEY, (String) value);
+ break;
+ case XML_TAG_ENGINE_ID:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.ENGINE_ID_KEY, (String) value);
+ break;
+ case XML_TAG_PRIVATE_KEY_ID:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, (String) value);
+ break;
+ case XML_TAG_ALT_SUBJECT_MATCH:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY, (String) value);
+ break;
+ case XML_TAG_DOM_SUFFIX_MATCH:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY, (String) value);
+ break;
+ case XML_TAG_CA_PATH:
+ enterpriseConfig.setFieldValue(
+ WifiEnterpriseConfig.CA_PATH_KEY, (String) value);
+ break;
+ case XML_TAG_EAP_METHOD:
+ enterpriseConfig.setEapMethod((int) value);
+ break;
+ case XML_TAG_PHASE2_METHOD:
+ enterpriseConfig.setPhase2Method((int) value);
+ break;
+ default:
+ throw new XmlPullParserException(
+ "Unknown value name found: " + valueName[0]);
+ }
+ }
+ return enterpriseConfig;
+ }
+ }
+}
+
diff --git a/service/jni/com_android_server_wifi_WifiNative.cpp b/service/jni/com_android_server_wifi_WifiNative.cpp
index 71e3971..1467503 100644
--- a/service/jni/com_android_server_wifi_WifiNative.cpp
+++ b/service/jni/com_android_server_wifi_WifiNative.cpp
@@ -16,33 +16,41 @@
#define LOG_TAG "wifi"
-#include "jni.h"
-#include "JniConstants.h"
-#include <ScopedUtfChars.h>
-#include <ScopedBytes.h>
-#include <utils/misc.h>
-#include <utils/Log.h>
-#include <utils/String16.h>
#include <ctype.h>
#include <stdlib.h>
-#include <sys/socket.h>
#include <sys/klog.h>
-#include <linux/if.h>
+#include <sys/socket.h>
+/* We need linux/if_arp.h for ARPHRD_ETHER. Sadly, it forward declares
+ struct sockaddr and must be included after sys/socket.h. */
#include <linux/if_arp.h>
#include <algorithm>
#include <limits>
#include <vector>
-#include "wifi.h"
-#include "wifi_hal.h"
+#include <hardware_legacy/rtt.h>
+#include <hardware_legacy/wifi.h>
+#include <hardware_legacy/wifi_hal.h>
+#include <log/log.h>
+#include <nativehelper/JniConstants.h>
+#include <nativehelper/ScopedBytes.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <nativehelper/jni.h>
+#include <utils/String16.h>
+#include <utils/misc.h>
+#include <wifi_system/hal_tool.h>
+#include <wifi_system/interface_tool.h>
+#include <wifi_system/wifi.h>
+
#include "jni_helper.h"
-#include "rtt.h"
-#include "wifi_hal_stub.h"
+
#define REPLY_BUF_SIZE (4096 + 1) // wpa_supplicant's maximum size + 1 for nul
#define EVENT_BUF_SIZE 2048
#define WAKE_REASON_TYPE_MAX 10
+using android::wifi_system::HalTool;
+using android::wifi_system::InterfaceTool;
+
namespace android {
extern "C"
@@ -64,7 +72,7 @@
}
--reply_len; // Ensure we have room to add NUL termination.
- if (::wifi_command(command.c_str(), reply, &reply_len) != 0) {
+ if (wifi_system::wifi_command(command.c_str(), reply, &reply_len) != 0) {
return false;
}
@@ -124,28 +132,28 @@
static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jclass, jboolean p2pSupported)
{
- return (::wifi_start_supplicant(p2pSupported) == 0);
+ return (wifi_system::wifi_start_supplicant(p2pSupported) == 0);
}
static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jclass, jboolean p2pSupported)
{
- return (::wifi_stop_supplicant(p2pSupported) == 0);
+ return (wifi_system::wifi_stop_supplicant(p2pSupported) == 0);
}
static jboolean android_net_wifi_connectToSupplicant(JNIEnv* env, jclass)
{
- return (::wifi_connect_to_supplicant() == 0);
+ return (wifi_system::wifi_connect_to_supplicant() == 0);
}
static void android_net_wifi_closeSupplicantConnection(JNIEnv* env, jclass)
{
- ::wifi_close_supplicant_connection();
+ wifi_system::wifi_close_supplicant_connection();
}
static jstring android_net_wifi_waitForEvent(JNIEnv* env, jclass)
{
char buf[EVENT_BUF_SIZE];
- int nread = ::wifi_wait_for_event(buf, sizeof buf);
+ int nread = wifi_system::wifi_wait_for_event(buf, sizeof buf);
if (nread > 0) {
return env->NewStringUTF(buf);
} else {
@@ -237,96 +245,38 @@
return scanResult;
}
-int set_iface_flags(const char *ifname, bool dev_up) {
- struct ifreq ifr;
- int ret;
- int sock = socket(PF_INET, SOCK_DGRAM, 0);
- if (sock < 0) {
- ALOGD("Bad socket: %d\n", sock);
- return -errno;
- }
-
- //ALOGD("setting interface %s flags (%s)\n", ifname, dev_up ? "UP" : "DOWN");
-
- memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-
- //ALOGD("reading old value\n");
-
- if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
- ret = errno ? -errno : -999;
- ALOGE("Could not read interface %s flags: %d\n", ifname, errno);
- close(sock);
- return ret;
- } else {
- //ALOGD("writing new value\n");
- }
-
- if (dev_up) {
- if (ifr.ifr_flags & IFF_UP) {
- // ALOGD("interface %s is already up\n", ifname);
- close(sock);
- return 0;
- }
- ifr.ifr_flags |= IFF_UP;
- } else {
- if (!(ifr.ifr_flags & IFF_UP)) {
- // ALOGD("interface %s is already down\n", ifname);
- close(sock);
- return 0;
- }
- ifr.ifr_flags &= ~IFF_UP;
- }
-
- if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) {
- ALOGE("Could not set interface %s flags: %d\n", ifname, errno);
- ret = errno ? -errno : -999;
- close(sock);
- return ret;
- } else {
- ALOGD("set interface %s flags (%s)\n", ifname, dev_up ? "UP" : "DOWN");
- }
- close(sock);
- return 0;
-}
-
static jboolean android_net_wifi_set_interface_up(JNIEnv* env, jclass cls, jboolean up) {
- return (set_iface_flags("wlan0", (bool)up) == 0);
+ InterfaceTool if_tool;
+ return if_tool.SetWifiUpState((bool)up);
}
static jboolean android_net_wifi_startHal(JNIEnv* env, jclass cls) {
+ InterfaceTool if_tool;
JNIHelper helper(env);
wifi_handle halHandle = getWifiHandle(helper, cls);
- if (halHandle == NULL) {
-
- if(init_wifi_stub_hal_func_table(&hal_fn) != 0 ) {
- ALOGE("Can not initialize the basic function pointer table");
- return false;
- }
-
- wifi_error res = init_wifi_vendor_hal_func_table(&hal_fn);
- if (res != WIFI_SUCCESS) {
- ALOGE("Can not initialize the vendor function pointer table");
- return false;
- }
-
- int ret = set_iface_flags("wlan0", true);
- if(ret != 0) {
- return false;
- }
-
- res = hal_fn.wifi_initialize(&halHandle);
- if (res == WIFI_SUCCESS) {
- helper.setStaticLongField(cls, WifiHandleVarName, (jlong)halHandle);
- ALOGD("Did set static halHandle = %p", halHandle);
- }
- env->GetJavaVM(&mVM);
- mCls = (jclass) env->NewGlobalRef(cls);
- ALOGD("halHandle = %p, mVM = %p, mCls = %p", halHandle, mVM, mCls);
- return res == WIFI_SUCCESS;
- } else {
- return (set_iface_flags("wlan0", true) == 0);
+ if (halHandle != NULL) {
+ return if_tool.SetWifiUpState(true);
}
+
+ HalTool hal_tool;
+ if (!hal_tool.InitFunctionTable(&hal_fn)) {
+ return false;
+ }
+
+ if(!if_tool.SetWifiUpState(true)) {
+ ALOGE("Failed to set WiFi interface up");
+ return false;
+ }
+
+ const bool was_started = hal_fn.wifi_initialize(&halHandle) == WIFI_SUCCESS;
+ if (was_started) {
+ helper.setStaticLongField(cls, WifiHandleVarName, (jlong)halHandle);
+ ALOGD("Did set static halHandle = %p", halHandle);
+ }
+ env->GetJavaVM(&mVM);
+ mCls = (jclass) env->NewGlobalRef(cls);
+ ALOGD("halHandle = %p, mVM = %p, mCls = %p", halHandle, mVM, mCls);
+ return was_started;
}
void android_net_wifi_hal_cleaned_up_handler(wifi_handle handle) {
@@ -359,7 +309,8 @@
JNIHelper helper(env);
wifi_handle halHandle = getWifiHandle(helper, cls);
hal_fn.wifi_event_loop(halHandle);
- set_iface_flags("wlan0", false);
+ InterfaceTool if_tool;
+ if_tool.SetWifiUpState(false);
}
static int android_net_wifi_getInterfaces(JNIEnv *env, jclass cls) {
@@ -1388,7 +1339,8 @@
}
static jboolean android_net_wifi_is_get_channels_for_band_supported(JNIEnv *env, jclass cls){
- return (hal_fn.wifi_get_valid_channels == wifi_get_valid_channels_stub);
+ HalTool hal_tool;
+ return hal_tool.CanGetValidChannels(&hal_fn);
}
static jintArray android_net_wifi_getValidChannels(JNIEnv *env, jclass cls,
diff --git a/service/jni/com_android_server_wifi_nan_WifiNanNative.cpp b/service/jni/com_android_server_wifi_nan_WifiNanNative.cpp
index cae441a..83c2490 100644
--- a/service/jni/com_android_server_wifi_nan_WifiNanNative.cpp
+++ b/service/jni/com_android_server_wifi_nan_WifiNanNative.cpp
@@ -16,19 +16,20 @@
#define LOG_TAG "wifinan"
-#include "jni.h"
-#include "JniConstants.h"
-#include <ScopedUtfChars.h>
-#include <ScopedBytes.h>
-#include <utils/misc.h>
-#include <utils/Log.h>
-#include <utils/String16.h>
#include <ctype.h>
#include <stdlib.h>
#include <sys/socket.h>
-#include <linux/if.h>
-#include "wifi.h"
-#include "wifi_hal.h"
+
+#include <hardware_legacy/wifi_hal.h>
+#include <log/log.h>
+#include <nativehelper/JniConstants.h>
+#include <nativehelper/ScopedBytes.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <nativehelper/jni.h>
+#include <utils/String16.h>
+#include <utils/misc.h>
+#include <wifi_system/wifi.h>
+
#include "jni_helper.h"
namespace android {
@@ -98,6 +99,8 @@
(int) msg->body.nan_capabilities.max_ndp_sessions);
helper.setIntField(data, "maxAppInfoLen",
(int) msg->body.nan_capabilities.max_app_info_len);
+ helper.setIntField(data, "maxQueuedTransmitMessages",
+ (int) msg->body.nan_capabilities.max_queued_transmit_followup_msgs);
helper.reportEvent(
mCls, "onNanNotifyResponseCapabilities",
@@ -105,6 +108,11 @@
(short) id, (int) msg->status, (int) msg->value, data.get());
break;
}
+ case NAN_DP_INITIATOR_RESPONSE:
+ helper.reportEvent(mCls, "onNanNotifyResponseDataPathInitiate", "(SIII)V", (short) id,
+ (int) msg->status, (int) msg->value,
+ msg->body.data_request_response.ndp_instance_id);
+ break;
default:
helper.reportEvent(mCls, "onNanNotifyResponse", "(SIII)V", (short) id,
(int) msg->response_type, (int) msg->status,
@@ -208,6 +216,63 @@
ALOGD("OnNanEventSdfPayload");
}
+static void OnNanEventDataRequest(NanDataPathRequestInd* event) {
+ ALOGD("OnNanEventDataRequest");
+ JNIHelper helper(mVM);
+
+ JNIObject<jbyteArray> peerBytes = helper.newByteArray(6);
+ helper.setByteArrayRegion(peerBytes, 0, 6,
+ (jbyte *)event->peer_disc_mac_addr);
+
+ JNIObject<jbyteArray> msgBytes =
+ helper.newByteArray(event->app_info.ndp_app_info_len);
+ helper.setByteArrayRegion(msgBytes, 0, event->app_info.ndp_app_info_len,
+ (jbyte *)event->app_info.ndp_app_info);
+
+ helper.reportEvent(mCls, "onDataPathRequest", "(I[BI[BI)V",
+ event->service_instance_id, peerBytes.get(),
+ event->ndp_instance_id, msgBytes.get(),
+ event->app_info.ndp_app_info_len);
+}
+
+static void OnNanEventDataConfirm(NanDataPathConfirmInd* event) {
+ ALOGD("OnNanEventDataConfirm");
+ JNIHelper helper(mVM);
+
+ JNIObject<jbyteArray> peerBytes = helper.newByteArray(6);
+ helper.setByteArrayRegion(peerBytes, 0, 6, (jbyte *)event->peer_ndi_mac_addr);
+
+ JNIObject<jbyteArray> msgBytes =
+ helper.newByteArray(event->app_info.ndp_app_info_len);
+ helper.setByteArrayRegion(msgBytes, 0, event->app_info.ndp_app_info_len,
+ (jbyte *)event->app_info.ndp_app_info);
+
+ helper.reportEvent(
+ mCls, "onDataPathConfirm", "(I[BZI[BI)V", event->ndp_instance_id,
+ peerBytes.get(), event->rsp_code == NAN_DP_REQUEST_ACCEPT,
+ event->reason_code, msgBytes.get(), event->app_info.ndp_app_info_len);
+}
+
+static void OnNanEventDataEnd(NanDataPathEndInd* event) {
+ ALOGD("OnNanEventDataEnd");
+ JNIHelper helper(mVM);
+
+ for (int i = 0; i < event->num_ndp_instances; ++i) {
+ helper.reportEvent(mCls, "onDataPathEnd", "(I)V",
+ event->ndp_instance_id[i]);
+ }
+}
+
+static void OnNanEventTransmitFollowup(NanTransmitFollowupInd* event) {
+ ALOGD("OnNanEventTransmitFollowup: transaction_id=%d, reason=%d", event->id,
+ event->reason);
+
+ JNIHelper helper(mVM);
+
+ helper.reportEvent(mCls, "onTransmitFollowupEvent", "(SI)V",
+ (short) event->id, (int) event->reason);
+}
+
static jint android_net_wifi_nan_register_handler(JNIEnv *env, jclass cls,
jclass wifi_native_cls,
jint iface) {
@@ -217,6 +282,7 @@
ALOGD("android_net_wifi_nan_register_handler handle=%p", handle);
NanCallbackHandler handlers;
+ memset(&handlers, 0, sizeof(NanCallbackHandler));
handlers.NotifyResponse = OnNanNotifyResponse;
handlers.EventPublishTerminated = OnNanEventPublishTerminated;
handlers.EventMatch = OnNanEventMatch;
@@ -227,6 +293,10 @@
handlers.EventDisabled = OnNanEventDisabled;
handlers.EventTca = OnNanEventTca;
handlers.EventBeaconSdfPayload = OnNanEventBeaconSdfPayload;
+ handlers.EventDataRequest = OnNanEventDataRequest;
+ handlers.EventDataConfirm = OnNanEventDataConfirm;
+ handlers.EventDataEnd = OnNanEventDataEnd;
+ handlers.EventTransmitFollowup = OnNanEventTransmitFollowup;
if (mVM == NULL) {
env->GetJavaVM(&mVM);
@@ -260,6 +330,27 @@
return hal_fn.wifi_nan_enable_request(transaction_id, handle, &msg);
}
+static jint android_net_wifi_nan_config_request(JNIEnv *env, jclass cls,
+ jshort transaction_id,
+ jclass wifi_native_cls,
+ jint iface,
+ jobject config_request) {
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, wifi_native_cls, iface);
+
+ ALOGD("android_net_wifi_nan_config_request handle=%p, id=%d",
+ handle, transaction_id);
+
+ NanConfigRequest msg;
+ memset(&msg, 0, sizeof(NanConfigRequest));
+
+ /* configurable settings */
+ msg.config_master_pref = 1;
+ msg.master_pref = helper.getIntField(config_request, "mMasterPreference");
+
+ return hal_fn.wifi_nan_config_request(transaction_id, handle, &msg);
+}
+
static jint android_net_wifi_nan_get_capabilities(JNIEnv *env, jclass cls,
jshort transaction_id,
jclass wifi_native_cls,
@@ -291,8 +382,7 @@
jint publish_id,
jclass wifi_native_cls,
jint iface,
- jobject publish_data,
- jobject publish_settings) {
+ jobject publish_config) {
JNIHelper helper(env);
wifi_interface_handle handle = getIfaceHandle(helper, wifi_native_cls, iface);
@@ -310,47 +400,47 @@
/* configurable settings */
msg.publish_id = publish_id;
- JNIObject<jstring> objStr1 = helper.getStringField(publish_data, "mServiceName");
- if (objStr1 == NULL) {
- ALOGE("Error accessing mServiceName field");
+ size_t service_name_len;
+ helper.getByteArrayField(publish_config, "mServiceName", msg.service_name,
+ &service_name_len, NAN_MAX_SERVICE_NAME_LEN);
+ if (service_name_len > NAN_MAX_SERVICE_NAME_LEN) {
+ ALOGE("Length of service name field larger than max allowed");
return 0;
}
- ScopedUtfChars chars1(env, objStr1);
- const char *serviceName = chars1.c_str();
- if (serviceName == NULL) {
- ALOGE("Error getting mServiceName");
- return 0;
- }
- msg.service_name_len = strlen(serviceName);
- strcpy((char*)msg.service_name, serviceName);
+ msg.service_name_len = service_name_len;
- msg.service_specific_info_len = helper.getIntField(publish_data, "mServiceSpecificInfoLength");
+ msg.service_specific_info_len = helper.getIntField(publish_config, "mServiceSpecificInfoLength");
if (msg.service_specific_info_len != 0) {
- helper.getByteArrayField(publish_data, "mServiceSpecificInfo",
+ helper.getByteArrayField(publish_config, "mServiceSpecificInfo",
msg.service_specific_info, msg.service_specific_info_len);
}
- msg.tx_match_filter_len = helper.getIntField(publish_data, "mTxFilterLength");
+ msg.tx_match_filter_len = helper.getIntField(publish_config, "mTxFilterLength");
if (msg.tx_match_filter_len != 0) {
- helper.getByteArrayField(publish_data, "mTxFilter",
+ helper.getByteArrayField(publish_config, "mTxFilter",
msg.tx_match_filter, msg.tx_match_filter_len);
}
- msg.rx_match_filter_len = helper.getIntField(publish_data, "mRxFilterLength");
+ msg.rx_match_filter_len = helper.getIntField(publish_config, "mRxFilterLength");
if (msg.rx_match_filter_len != 0) {
- helper.getByteArrayField(publish_data, "mRxFilter",
+ helper.getByteArrayField(publish_config, "mRxFilter",
msg.rx_match_filter, msg.rx_match_filter_len);
}
- msg.publish_type = (NanPublishType)helper.getIntField(publish_settings, "mPublishType");
- msg.publish_count = helper.getIntField(publish_settings, "mPublishCount");
- msg.ttl = helper.getIntField(publish_settings, "mTtlSec");
+ msg.publish_type = (NanPublishType)helper.getIntField(publish_config, "mPublishType");
+ msg.publish_count = helper.getIntField(publish_config, "mPublishCount");
+ msg.ttl = helper.getIntField(publish_config, "mTtlSec");
msg.tx_type = NAN_TX_TYPE_BROADCAST;
if (msg.publish_type != NAN_PUBLISH_TYPE_UNSOLICITED)
msg.tx_type = NAN_TX_TYPE_UNICAST;
+ msg.recv_indication_cfg = 0;
+ if (!helper.getBoolField(publish_config, "mEnableTerminateNotification")) {
+ msg.recv_indication_cfg |= 0x1;
+ }
+
return hal_fn.wifi_nan_publish_request(transaction_id, handle, &msg);
}
@@ -359,8 +449,7 @@
jint subscribe_id,
jclass wifi_native_cls,
jint iface,
- jobject subscribe_data,
- jobject subscribe_settings) {
+ jobject subscribe_config) {
JNIHelper helper(env);
wifi_interface_handle handle = getIfaceHandle(helper, wifi_native_cls, iface);
@@ -375,7 +464,6 @@
msg.serviceResponseInclude = NAN_SRF_INCLUDE_RESPOND;
msg.useServiceResponseFilter = NAN_DO_NOT_USE_SRF;
msg.ssiRequiredForMatchIndication = NAN_SSI_NOT_REQUIRED_IN_MATCH_IND;
- msg.subscribe_match_indicator = NAN_MATCH_ALG_MATCH_ONCE;
msg.rssi_threshold_flag = 0;
msg.connmap = 0;
msg.num_intf_addr_present = 0;
@@ -383,41 +471,43 @@
/* configurable settings */
msg.subscribe_id = subscribe_id;
- JNIObject<jstring> objStr1 = helper.getStringField(subscribe_data, "mServiceName");
- if (objStr1 == NULL) {
- ALOGE("Error accessing mServiceName field");
+ size_t service_name_len;
+ helper.getByteArrayField(subscribe_config, "mServiceName", msg.service_name,
+ &service_name_len, NAN_MAX_SERVICE_NAME_LEN);
+ if (service_name_len > NAN_MAX_SERVICE_NAME_LEN) {
+ ALOGE("Length of service name field larger than max allowed");
return 0;
}
- ScopedUtfChars chars1(env, objStr1);
- const char *serviceName = chars1.c_str();
- if (serviceName == NULL) {
- ALOGE("Error getting mServiceName");
- return 0;
- }
- msg.service_name_len = strlen(serviceName);
- strcpy((char*)msg.service_name, serviceName);
+ msg.service_name_len = service_name_len;
- msg.service_specific_info_len = helper.getIntField(subscribe_data, "mServiceSpecificInfoLength");
+ msg.service_specific_info_len = helper.getIntField(subscribe_config, "mServiceSpecificInfoLength");
if (msg.service_specific_info_len != 0) {
- helper.getByteArrayField(subscribe_data, "mServiceSpecificInfo",
+ helper.getByteArrayField(subscribe_config, "mServiceSpecificInfo",
msg.service_specific_info, msg.service_specific_info_len);
}
- msg.tx_match_filter_len = helper.getIntField(subscribe_data, "mTxFilterLength");
+ msg.tx_match_filter_len = helper.getIntField(subscribe_config, "mTxFilterLength");
if (msg.tx_match_filter_len != 0) {
- helper.getByteArrayField(subscribe_data, "mTxFilter",
+ helper.getByteArrayField(subscribe_config, "mTxFilter",
msg.tx_match_filter, msg.tx_match_filter_len);
}
- msg.rx_match_filter_len = helper.getIntField(subscribe_data, "mRxFilterLength");
+ msg.rx_match_filter_len = helper.getIntField(subscribe_config, "mRxFilterLength");
if (msg.rx_match_filter_len != 0) {
- helper.getByteArrayField(subscribe_data, "mRxFilter",
+ helper.getByteArrayField(subscribe_config, "mRxFilter",
msg.rx_match_filter, msg.rx_match_filter_len);
}
- msg.subscribe_type = (NanSubscribeType)helper.getIntField(subscribe_settings, "mSubscribeType");
- msg.subscribe_count = helper.getIntField(subscribe_settings, "mSubscribeCount");
- msg.ttl = helper.getIntField(subscribe_settings, "mTtlSec");
+ msg.subscribe_type = (NanSubscribeType)helper.getIntField(subscribe_config, "mSubscribeType");
+ msg.subscribe_count = helper.getIntField(subscribe_config, "mSubscribeCount");
+ msg.ttl = helper.getIntField(subscribe_config, "mTtlSec");
+ msg.subscribe_match_indicator = (NanMatchAlg) helper.getIntField(
+ subscribe_config, "mMatchStyle");
+
+ msg.recv_indication_cfg = 0;
+ if (!helper.getBoolField(subscribe_config, "mEnableTerminateNotification")) {
+ msg.recv_indication_cfg |= 0x1;
+ }
return hal_fn.wifi_nan_subscribe_request(transaction_id, handle, &msg);
}
@@ -493,6 +583,142 @@
return hal_fn.wifi_nan_subscribe_cancel_request(transaction_id, handle, &msg);
}
+static jint android_net_wifi_nan_create_nan_network_interface(
+ JNIEnv *env, jclass cls, jshort transaction_id, jclass wifi_native_cls,
+ jint iface, jstring interface_name) {
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, wifi_native_cls, iface);
+
+ ALOGD("android_net_wifi_nan_create_nan_network_interface handle=%p, id=%d",
+ handle, transaction_id);
+
+ ScopedUtfChars chars(env, interface_name);
+ if (chars.c_str() == NULL) {
+ return 0;
+ }
+
+ return hal_fn.wifi_nan_data_interface_create(transaction_id, handle,
+ (char*) chars.c_str());
+}
+
+static jint android_net_wifi_nan_delete_nan_network_interface(
+ JNIEnv *env, jclass cls, jshort transaction_id, jclass wifi_native_cls,
+ jint iface, jstring interface_name) {
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, wifi_native_cls, iface);
+
+ ALOGD("android_net_wifi_nan_delete_nan_network_interface handle=%p, id=%d",
+ handle, transaction_id);
+
+ ScopedUtfChars chars(env, interface_name);
+ if (chars.c_str() == NULL) {
+ return 0;
+ }
+
+ return hal_fn.wifi_nan_data_interface_delete(transaction_id, handle,
+ (char*) chars.c_str());
+}
+
+static jint android_net_wifi_nan_initiate_nan_data_path(
+ JNIEnv *env, jclass cls, jshort transaction_id, jclass wifi_native_cls,
+ jint iface, jint pub_sub_id, jint channel_request_type, jint channel,
+ jbyteArray peer, jstring interface_name, jbyteArray message,
+ jint message_length) {
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, wifi_native_cls, iface);
+
+ ALOGD("android_net_wifi_nan_initiate_nan_data_path handle=%p, id=%d", handle,
+ transaction_id);
+
+ NanDataPathInitiatorRequest msg;
+ memset(&msg, 0, sizeof(NanDataPathInitiatorRequest));
+
+ msg.service_instance_id = pub_sub_id;
+ msg.channel_request_type = (NanDataPathChannelCfg) channel_request_type;
+ msg.channel = channel;
+
+ ScopedBytesRO peerBytes(env, peer);
+ memcpy(msg.peer_disc_mac_addr, (byte *)peerBytes.get(), 6);
+
+ ScopedUtfChars chars(env, interface_name);
+ if (chars.c_str() == NULL) {
+ return 0;
+ }
+ strcpy(msg.ndp_iface, chars.c_str());
+
+ // TODO: b/26564544: add security configuration
+ msg.ndp_cfg.security_cfg = NAN_DP_CONFIG_NO_SECURITY;
+
+ // TODO: b/29065317: add QoS configuration
+ msg.ndp_cfg.qos_cfg = NAN_DP_CONFIG_NO_QOS;
+
+ msg.app_info.ndp_app_info_len = message_length;
+
+ ScopedBytesRO messageBytes(env, message);
+ memcpy(msg.app_info.ndp_app_info, (byte *)messageBytes.get(), message_length);
+
+ return hal_fn.wifi_nan_data_request_initiator(transaction_id, handle, &msg);
+}
+
+static jint android_net_wifi_nan_respond_nan_data_path_request(
+ JNIEnv *env, jclass cls, jshort transaction_id, jclass wifi_native_cls,
+ jint iface, jboolean accept, jint ndp_id, jstring interface_name,
+ jbyteArray message, jint message_length) {
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, wifi_native_cls, iface);
+
+ ALOGD("android_net_wifi_nan_respond_nan_data_path_request handle=%p, id=%d",
+ handle, transaction_id);
+
+ NanDataPathIndicationResponse msg;
+ memset(&msg, 0, sizeof(NanDataPathIndicationResponse));
+
+ msg.ndp_instance_id = ndp_id;
+
+ ScopedUtfChars chars(env, interface_name);
+ if (chars.c_str() == NULL) {
+ return 0;
+ }
+ strcpy(msg.ndp_iface, chars.c_str());
+
+ // TODO: b/26564544: add security configuration
+ msg.ndp_cfg.security_cfg = NAN_DP_CONFIG_NO_SECURITY;
+
+ // TODO: b/29065317: add QoS configuration
+ msg.ndp_cfg.qos_cfg = NAN_DP_CONFIG_NO_QOS;
+
+ msg.app_info.ndp_app_info_len = message_length;
+
+ ScopedBytesRO messageBytes(env, message);
+ memcpy(msg.app_info.ndp_app_info, (byte *)messageBytes.get(), message_length);
+
+ msg.rsp_code = accept ? NAN_DP_REQUEST_ACCEPT : NAN_DP_REQUEST_REJECT;
+
+ return hal_fn.wifi_nan_data_indication_response(transaction_id, handle, &msg);
+}
+
+static jint android_net_wifi_nan_end_nan_data_path(JNIEnv *env, jclass cls,
+ jshort transaction_id,
+ jclass wifi_native_cls,
+ jint iface, jint ndp_id) {
+ JNIHelper helper(env);
+ wifi_interface_handle handle = getIfaceHandle(helper, wifi_native_cls, iface);
+
+ ALOGD("android_net_wifi_nan_end_nan_data_path handle=%p, id=%d", handle,
+ transaction_id);
+
+ NanDataPathEndRequest* msg = (NanDataPathEndRequest*)malloc(sizeof(NanDataPathEndRequest) + sizeof(NanDataPathId));
+
+ msg->num_ndp_instances = 1;
+ msg->ndp_instance_id[0] = ndp_id;
+
+ jint status = hal_fn.wifi_nan_data_end(transaction_id, handle, msg);
+
+ free(msg);
+
+ return status;
+}
+
// ----------------------------------------------------------------------------
/*
@@ -501,16 +727,21 @@
static JNINativeMethod gWifiNanMethods[] = {
/* name, signature, funcPtr */
-
- {"initNanHandlersNative", "(Ljava/lang/Object;I)I", (void*)android_net_wifi_nan_register_handler },
- {"getCapabilitiesNative", "(SLjava/lang/Object;I)I", (void*)android_net_wifi_nan_get_capabilities },
- {"enableAndConfigureNative", "(SLjava/lang/Object;ILandroid/net/wifi/nan/ConfigRequest;)I", (void*)android_net_wifi_nan_enable_request },
- {"disableNative", "(SLjava/lang/Object;I)I", (void*)android_net_wifi_nan_disable_request },
- {"publishNative", "(SILjava/lang/Object;ILandroid/net/wifi/nan/PublishData;Landroid/net/wifi/nan/PublishSettings;)I", (void*)android_net_wifi_nan_publish },
- {"subscribeNative", "(SILjava/lang/Object;ILandroid/net/wifi/nan/SubscribeData;Landroid/net/wifi/nan/SubscribeSettings;)I", (void*)android_net_wifi_nan_subscribe },
- {"sendMessageNative", "(SLjava/lang/Object;III[B[BI)I", (void*)android_net_wifi_nan_send_message },
- {"stopPublishNative", "(SLjava/lang/Object;II)I", (void*)android_net_wifi_nan_stop_publish },
- {"stopSubscribeNative", "(SLjava/lang/Object;II)I", (void*)android_net_wifi_nan_stop_subscribe },
+ {"initNanHandlersNative", "(Ljava/lang/Class;I)I", (void*)android_net_wifi_nan_register_handler },
+ {"getCapabilitiesNative", "(SLjava/lang/Class;I)I", (void*)android_net_wifi_nan_get_capabilities },
+ {"enableAndConfigureNative", "(SLjava/lang/Class;ILandroid/net/wifi/nan/ConfigRequest;)I", (void*)android_net_wifi_nan_enable_request },
+ {"updateConfigurationNative", "(SLjava/lang/Class;ILandroid/net/wifi/nan/ConfigRequest;)I", (void*)android_net_wifi_nan_config_request },
+ {"disableNative", "(SLjava/lang/Class;I)I", (void*)android_net_wifi_nan_disable_request },
+ {"publishNative", "(SILjava/lang/Class;ILandroid/net/wifi/nan/PublishConfig;)I", (void*)android_net_wifi_nan_publish },
+ {"subscribeNative", "(SILjava/lang/Class;ILandroid/net/wifi/nan/SubscribeConfig;)I", (void*)android_net_wifi_nan_subscribe },
+ {"sendMessageNative", "(SLjava/lang/Class;III[B[BI)I", (void*)android_net_wifi_nan_send_message },
+ {"stopPublishNative", "(SLjava/lang/Class;II)I", (void*)android_net_wifi_nan_stop_publish },
+ {"stopSubscribeNative", "(SLjava/lang/Class;II)I", (void*)android_net_wifi_nan_stop_subscribe },
+ {"createNanNetworkInterfaceNative", "(SLjava/lang/Class;ILjava/lang/String;)I", (void*)android_net_wifi_nan_create_nan_network_interface },
+ {"deleteNanNetworkInterfaceNative", "(SLjava/lang/Class;ILjava/lang/String;)I", (void*)android_net_wifi_nan_delete_nan_network_interface },
+ {"initiateDataPathNative", "(SLjava/lang/Class;IIII[BLjava/lang/String;[BI)I", (void*)android_net_wifi_nan_initiate_nan_data_path },
+ {"respondToDataPathRequestNative", "(SLjava/lang/Class;IZILjava/lang/String;[BI)I", (void*)android_net_wifi_nan_respond_nan_data_path_request },
+ {"endDataPathNative", "(SLjava/lang/Class;II)I", (void*)android_net_wifi_nan_end_nan_data_path },
};
/* User to register native functions */
diff --git a/service/jni/jni_helper.cpp b/service/jni/jni_helper.cpp
index c9b4edd..52f6bf2 100644
--- a/service/jni/jni_helper.cpp
+++ b/service/jni/jni_helper.cpp
@@ -16,15 +16,15 @@
#define LOG_TAG "wifi"
-#include "jni.h"
-#include <ScopedUtfChars.h>
-#include <utils/misc.h>
#include <android_runtime/AndroidRuntime.h>
+#include <hardware_legacy/wifi_hal.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <nativehelper/jni.h>
#include <utils/Log.h>
#include <utils/String16.h>
+#include <utils/misc.h>
+#include <wifi_system/wifi.h>
-#include "wifi.h"
-#include "wifi_hal.h"
#include "jni_helper.h"
namespace android {
@@ -241,7 +241,7 @@
return value;
}
-void JNIHelper::getByteArrayField(jobject obj, const char *name, byte* buf, int size) {
+void JNIHelper::getByteArrayField(jobject obj, const char *name, byte *buf, size_t size) {
JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
jfieldID field = mEnv->GetFieldID(cls, name, "[B");
if (field == 0) {
@@ -265,6 +265,37 @@
mEnv->ReleaseByteArrayElements(array, elem, 0);
}
+void JNIHelper::getByteArrayField(jobject obj, const char *name, byte *buf, size_t *size, int max_size) {
+ JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
+ jfieldID field = mEnv->GetFieldID(cls, name, "[B");
+ if (field == 0) {
+ THROW(*this, "Error in accessing field definition");
+ return;
+ }
+
+ jbyteArray byteArray = (jbyteArray)mEnv->GetObjectField(obj, field);
+ JNIObject<jbyteArray> array(*this, byteArray);
+ if (array == NULL) {
+ THROW(*this, "Error in accessing array");
+ return;
+ }
+
+ *size = getArrayLength(byteArray);
+ int use_size = *size;
+ if (use_size > max_size) {
+ use_size = max_size;
+ }
+
+ jbyte *elem = mEnv->GetByteArrayElements(array, 0);
+ if (elem == NULL) {
+ THROW(*this, "Error in accessing index element");
+ return;
+ }
+
+ memcpy(buf, elem, use_size);
+ mEnv->ReleaseByteArrayElements(array, elem, 0);
+}
+
jlong JNIHelper::getStaticLongArrayField(jobject obj, const char *name, int index)
{
JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
@@ -514,6 +545,7 @@
jmethodID methodID = mEnv->GetStaticMethodID(cls, method, signature);
if (methodID == 0) {
ALOGE("Error in getting method ID");
+ va_end(params);
return;
}
@@ -535,6 +567,7 @@
jmethodID methodID = mEnv->GetMethodID(cls, method, signature);
if (methodID == 0) {
ALOGE("Error in getting method ID");
+ va_end(params);
return;
}
@@ -555,6 +588,7 @@
jmethodID methodID = mEnv->GetStaticMethodID(cls, method, signature);
if (methodID == 0) {
ALOGE("Error in getting method ID");
+ va_end(params);
return false;
}
@@ -562,6 +596,7 @@
if (mEnv->ExceptionCheck()) {
mEnv->ExceptionDescribe();
mEnv->ExceptionClear();
+ va_end(params);
return false;
}
@@ -582,18 +617,21 @@
JNIObject<jclass> cls(*this, mEnv->FindClass(className));
if (cls == NULL) {
ALOGE("Error in finding class %s", className);
+ va_end(params);
return JNIObject<jobject>(*this, NULL);
}
jmethodID constructor = mEnv->GetMethodID(cls, "<init>", signature);
if (constructor == 0) {
ALOGE("Error in constructor ID for %s", className);
+ va_end(params);
return JNIObject<jobject>(*this, NULL);
}
JNIObject<jobject> obj(*this, mEnv->NewObjectV(cls, constructor, params));
if (obj == NULL) {
ALOGE("Could not create new object of %s", className);
+ va_end(params);
return JNIObject<jobject>(*this, NULL);
}
diff --git a/service/jni/jni_helper.h b/service/jni/jni_helper.h
index bb65f1c..f665fa7 100644
--- a/service/jni/jni_helper.h
+++ b/service/jni/jni_helper.h
@@ -82,7 +82,8 @@
bool getStringFieldValue(jobject obj, const char *name, char *buf, int size);
JNIObject<jobject> getObjectField(jobject obj, const char *name, const char *type);
JNIObject<jobjectArray> getArrayField(jobject obj, const char *name, const char *type);
- void getByteArrayField(jobject obj, const char *name, byte* buf, int size);
+ void getByteArrayField(jobject obj, const char *name, byte *buf, size_t size);
+ void getByteArrayField(jobject obj, const char *name, byte *buf, size_t *size, int max_size);
jlong getLongArrayField(jobject obj, const char *name, int index);
JNIObject<jobject> getObjectArrayField(
jobject obj, const char *name, const char *type, int index);
diff --git a/service/jni/wifi_hal_stub.h b/service/jni/wifi_hal_stub.h
deleted file mode 100644
index 6ec3d8e..0000000
--- a/service/jni/wifi_hal_stub.h
+++ /dev/null
@@ -1,167 +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.
- */
-
-#ifndef __WIFI_HAL_STUB_H__
-#define __WIFI_HAL_STUB_H__
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-#include "wifi_hal.h"
-
-int init_wifi_stub_hal_func_table(wifi_hal_fn *hal_fn);
-
-/* declare all HAL stub API here*/
-wifi_error wifi_initialize_stub(wifi_handle *handle);
-void wifi_cleanup_stub(wifi_handle handle, wifi_cleaned_up_handler handler);
-void wifi_event_loop_stub(wifi_handle handle);
-void wifi_get_error_info_stub(wifi_error err, const char **msg);
-wifi_error wifi_get_supported_feature_set_stub(wifi_interface_handle handle, feature_set *set);
-wifi_error wifi_get_concurrency_matrix_stub(wifi_interface_handle handle, int set_size_max,
- feature_set set[], int *set_size);
-wifi_error wifi_get_ifaces_stub(wifi_handle handle, int *num_ifaces, wifi_interface_handle **ifaces);
-wifi_error wifi_get_iface_name_stub(wifi_interface_handle iface, char *name, size_t size);
-wifi_error wifi_set_iface_event_handler_stub(wifi_request_id id, wifi_interface_handle iface,
- wifi_event_handler eh);
-wifi_error wifi_reset_iface_event_handler_stub(wifi_request_id id, wifi_interface_handle iface);
-wifi_error wifi_set_nodfs_flag_stub(wifi_interface_handle handle, u32 nodfs);
-wifi_error wifi_set_scanning_mac_oui_stub(wifi_interface_handle handle, unsigned char *oui);
-wifi_error wifi_get_supported_channels_stub(wifi_handle handle, int *size, wifi_channel *list);
-wifi_error wifi_is_epr_supported_stub(wifi_handle handle);
-wifi_error wifi_start_gscan_stub(wifi_request_id id, wifi_interface_handle iface,
- wifi_scan_cmd_params params, wifi_scan_result_handler handler);
-wifi_error wifi_stop_gscan_stub(wifi_request_id id, wifi_interface_handle iface);
-wifi_error wifi_get_cached_gscan_results_stub(wifi_interface_handle iface, byte flush,
- int max, wifi_cached_scan_results *results, int *num);
-wifi_error wifi_set_bssid_hotlist_stub(wifi_request_id id, wifi_interface_handle iface,
- wifi_bssid_hotlist_params params, wifi_hotlist_ap_found_handler handler);
-wifi_error wifi_reset_bssid_hotlist_stub(wifi_request_id id, wifi_interface_handle iface);
-wifi_error wifi_set_significant_change_handler_stub(wifi_request_id id, wifi_interface_handle iface,
- wifi_significant_change_params params, wifi_significant_change_handler handler);
-wifi_error wifi_reset_significant_change_handler_stub(wifi_request_id id,
- wifi_interface_handle iface);
-wifi_error wifi_get_gscan_capabilities_stub(wifi_interface_handle handle,
- wifi_gscan_capabilities *capabilities);
-wifi_error wifi_set_link_stats_stub(wifi_interface_handle iface, wifi_link_layer_params params);
-wifi_error wifi_get_link_stats_stub(wifi_request_id id,
- wifi_interface_handle iface, wifi_stats_result_handler handler);
-wifi_error wifi_clear_link_stats_stub(wifi_interface_handle iface,
- u32 stats_clear_req_mask, u32 *stats_clear_rsp_mask, u8 stop_req, u8 *stop_rsp);
-wifi_error wifi_get_valid_channels_stub(wifi_interface_handle handle,
- int band, int max_channels, wifi_channel *channels, int *num_channels);
-wifi_error wifi_rtt_range_request_stub(wifi_request_id id, wifi_interface_handle iface,
- unsigned num_rtt_config, wifi_rtt_config rtt_config[], wifi_rtt_event_handler handler);
-wifi_error wifi_rtt_range_cancel_stub(wifi_request_id id, wifi_interface_handle iface,
- unsigned num_devices, mac_addr addr[]);
-wifi_error wifi_get_rtt_capabilities_stub(wifi_interface_handle iface,
- wifi_rtt_capabilities *capabilities);
-wifi_error wifi_rtt_get_available_channel_stub(wifi_interface_handle iface,
- wifi_channel_info* channel);
-wifi_error wifi_enable_responder_stub(wifi_request_id id, wifi_interface_handle iface,
- wifi_channel_info channel_hint, unsigned max_duration_seconds,
- wifi_channel_info* channel_used);
-wifi_error wifi_disable_responder_stub(wifi_request_id id, wifi_interface_handle iface);
-
-wifi_error wifi_set_nodfs_flag_stub(wifi_interface_handle iface, u32 nodfs);
-wifi_error wifi_start_logging_stub(wifi_interface_handle iface, u32 verbose_level, u32 flags,
- u32 max_interval_sec, u32 min_data_size, char *buffer_name);
-wifi_error wifi_set_epno_list_stub(wifi_request_id id, wifi_interface_info *iface,
- const wifi_epno_params *params, wifi_epno_handler handler);
-wifi_error wifi_reset_epno_list_stub(wifi_request_id id, wifi_interface_info *iface);
-wifi_error wifi_set_country_code_stub(wifi_interface_handle iface, const char *code);
-wifi_error wifi_get_firmware_memory_dump_stub( wifi_interface_handle iface,
- wifi_firmware_memory_dump_handler handler);
-wifi_error wifi_set_log_handler_stub(wifi_request_id id, wifi_interface_handle iface,
- wifi_ring_buffer_data_handler handler);
-wifi_error wifi_reset_log_handler_stub(wifi_request_id id, wifi_interface_handle iface);
-wifi_error wifi_set_alert_handler_stub(wifi_request_id id, wifi_interface_handle iface,
- wifi_alert_handler handler);
-wifi_error wifi_reset_alert_handler_stub(wifi_request_id id, wifi_interface_handle iface);
-wifi_error wifi_get_firmware_version_stub(wifi_interface_handle iface, char *buffer,
- int buffer_size);
-wifi_error wifi_get_ring_buffers_status_stub(wifi_interface_handle iface,
- u32 *num_rings, wifi_ring_buffer_status *status);
-wifi_error wifi_get_logger_supported_feature_set_stub(wifi_interface_handle iface,
- unsigned int *support);
-wifi_error wifi_get_ring_data_stub(wifi_interface_handle iface, char *ring_name);
-wifi_error wifi_enable_tdls_stub(wifi_interface_handle iface, mac_addr addr,
- wifi_tdls_params *params, wifi_tdls_handler handler);
-wifi_error wifi_disable_tdls_stub(wifi_interface_handle iface, mac_addr addr);
-wifi_error wifi_get_tdls_status_stub(wifi_interface_handle iface, mac_addr addr,
- wifi_tdls_status *status);
-wifi_error wifi_get_tdls_capabilities_stub(wifi_interface_handle iface,
- wifi_tdls_capabilities *capabilities);
-wifi_error wifi_get_driver_version_stub(wifi_interface_handle iface, char *buffer,
- int buffer_size);
- wifi_error wifi_set_country_code_stub(wifi_interface_handle iface, const char *code);
-wifi_error wifi_set_bssid_blacklist_stub(wifi_request_id id, wifi_interface_handle iface,
- wifi_bssid_params params);
-wifi_error wifi_start_sending_offloaded_packet_stub(wifi_request_id id,
- wifi_interface_handle iface, u8 *ip_packet, u16 ip_packet_len,
- u8 *src_mac_addr, u8 *dst_mac_addr, u32 period_msec);
-wifi_error wifi_stop_sending_offloaded_packet_stub(wifi_request_id id, wifi_interface_handle iface);
-wifi_error wifi_get_wake_reason_stats_stub(wifi_interface_handle iface,
- WLAN_DRIVER_WAKE_REASON_CNT *wifi_wake_reason_cnt);
-wifi_error wifi_configure_nd_offload_stub(wifi_interface_handle iface, u8 enable);
-wifi_error wifi_nan_enable_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanEnableRequest* msg);
-wifi_error wifi_nan_disable_request_stub(transaction_id id,
- wifi_interface_handle iface);
-wifi_error wifi_nan_publish_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanPublishRequest* msg);
-wifi_error wifi_nan_publish_cancel_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanPublishCancelRequest* msg);
-wifi_error wifi_nan_subscribe_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanSubscribeRequest* msg);
-wifi_error wifi_nan_subscribe_cancel_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanSubscribeCancelRequest* msg);
-wifi_error wifi_nan_transmit_followup_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanTransmitFollowupRequest* msg);
-wifi_error wifi_nan_stats_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanStatsRequest* msg);
-wifi_error wifi_nan_config_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanConfigRequest* msg);
-wifi_error wifi_nan_tca_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanTCARequest* msg);
-wifi_error wifi_nan_beacon_sdf_payload_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanBeaconSdfPayloadRequest* msg);
-wifi_error wifi_nan_register_handler_stub(wifi_interface_handle iface,
- NanCallbackHandler handlers);
-wifi_error wifi_nan_get_version_stub(wifi_handle handle,
- NanVersion* version);
-wifi_error wifi_nan_get_capabilities_stub(transaction_id id,
- wifi_interface_handle iface);
-wifi_error wifi_get_packet_filter_capabilities_stub(wifi_interface_handle handle,
- u32 *version, u32 *max_len);
-wifi_error wifi_set_packet_filter_stub(wifi_interface_handle handle,
- const u8 *program, u32 len);
-
-#ifdef __cplusplus
-}
-#endif
-#endif //__WIFI_HAL_STUB_H__
diff --git a/service/lib/wifi_hal.cpp b/service/lib/wifi_hal.cpp
deleted file mode 100644
index 25bb373..0000000
--- a/service/lib/wifi_hal.cpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#include <stdint.h>
-#include "wifi_hal.h"
-
-wifi_error init_wifi_vendor_hal_func_table(wifi_hal_fn *fn) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
diff --git a/service/lib/wifi_hal_stub.cpp b/service/lib/wifi_hal_stub.cpp
deleted file mode 100644
index bd75b3d..0000000
--- a/service/lib/wifi_hal_stub.cpp
+++ /dev/null
@@ -1,464 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#include <stdint.h>
-#include "wifi_hal.h"
-#include "wifi_hal_stub.h"
-
-wifi_error wifi_initialize_stub(wifi_handle *handle) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-void wifi_cleanup_stub(wifi_handle handle, wifi_cleaned_up_handler handler) {
-}
-
-void wifi_event_loop_stub(wifi_handle handle) {
-
-}
-
-void wifi_get_error_info_stub(wifi_error err, const char **msg) {
- *msg = NULL;
-}
-
-wifi_error wifi_get_supported_feature_set_stub(wifi_interface_handle handle, feature_set *set) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_get_concurrency_matrix_stub(wifi_interface_handle handle, int max_size,
- feature_set *matrix, int *size) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_set_scanning_mac_oui_stub(wifi_interface_handle handle, unsigned char *oui_data) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-/* List of all supported channels, including 5GHz channels */
-wifi_error wifi_get_supported_channels_stub(wifi_handle handle, int *size, wifi_channel *list) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-/* Enhanced power reporting */
-wifi_error wifi_is_epr_supported_stub(wifi_handle handle) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-/* multiple interface support */
-wifi_error wifi_get_ifaces_stub(wifi_handle handle, int *num_ifaces, wifi_interface_handle **ifaces) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_get_iface_name_stub(wifi_interface_handle iface, char *name, size_t size) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_set_iface_event_handler_stub(wifi_request_id id,
- wifi_interface_handle iface, wifi_event_handler eh) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_reset_iface_event_handler_stub(wifi_request_id id, wifi_interface_handle iface) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_start_gscan_stub(wifi_request_id id, wifi_interface_handle iface,
- wifi_scan_cmd_params params, wifi_scan_result_handler handler) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_stop_gscan_stub(wifi_request_id id, wifi_interface_handle iface) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_get_cached_gscan_results_stub(wifi_interface_handle iface, byte flush,
- int max, wifi_cached_scan_results *results, int *num) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_set_bssid_hotlist_stub(wifi_request_id id, wifi_interface_handle iface,
- wifi_bssid_hotlist_params params, wifi_hotlist_ap_found_handler handler) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_reset_bssid_hotlist_stub(wifi_request_id id, wifi_interface_handle iface) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_set_significant_change_handler_stub(wifi_request_id id, wifi_interface_handle iface,
- wifi_significant_change_params params, wifi_significant_change_handler handler) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_reset_significant_change_handler_stub(wifi_request_id id, wifi_interface_handle iface) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_get_gscan_capabilities_stub(wifi_interface_handle handle,
- wifi_gscan_capabilities *capabilities) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_set_link_stats_stub(wifi_interface_handle iface, wifi_link_layer_params params) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_get_link_stats_stub(wifi_request_id id,
- wifi_interface_handle iface, wifi_stats_result_handler handler) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_clear_link_stats_stub(wifi_interface_handle iface,
- u32 stats_clear_req_mask, u32 *stats_clear_rsp_mask, u8 stop_req, u8 *stop_rsp) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_get_valid_channels_stub(wifi_interface_handle handle,
- int band, int max_channels, wifi_channel *channels, int *num_channels) {
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-/* API to request RTT measurement */
-wifi_error wifi_rtt_range_request_stub(wifi_request_id id, wifi_interface_handle iface,
- unsigned num_rtt_config, wifi_rtt_config rtt_config[], wifi_rtt_event_handler handler) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-/* API to cancel RTT measurements */
-wifi_error wifi_rtt_range_cancel_stub(wifi_request_id id, wifi_interface_handle iface,
- unsigned num_devices, mac_addr addr[]) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-/* API to get RTT capability */
-wifi_error wifi_get_rtt_capabilities_stub(wifi_interface_handle iface,
- wifi_rtt_capabilities *capabilities)
-{
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-/* API to enable RTT responder role */
-wifi_error wifi_enable_responder_stub(wifi_request_id id, wifi_interface_handle iface,
- wifi_channel_info channel_hint, unsigned max_duration_seconds,
- wifi_channel_info* channel_used) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-/* API to disable RTT responder role */
-wifi_error wifi_disable_responder_stub(wifi_request_id id, wifi_interface_handle iface) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-/* API to get available channel for RTT responder role */
-wifi_error wifi_rtt_get_available_channel_stub(wifi_interface_handle iface, wifi_channel_info* channel) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_set_nodfs_flag_stub(wifi_interface_handle iface, u32 nodfs) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_start_logging_stub(wifi_interface_handle iface, u32 verbose_level, u32 flags,
- u32 max_interval_sec, u32 min_data_size, char *buffer_name) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_set_epno_list_stub(int id, wifi_interface_info *iface,
- const wifi_epno_params *params, wifi_epno_handler handler) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_reset_epno_list_stub(int id, wifi_interface_info *iface) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_set_country_code_stub(wifi_interface_handle iface, const char *code) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_get_firmware_memory_dump_stub( wifi_interface_handle iface,
- wifi_firmware_memory_dump_handler handler){
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_set_log_handler_stub(wifi_request_id id, wifi_interface_handle iface,
- wifi_ring_buffer_data_handler handler) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_reset_log_handler_stub(wifi_request_id id, wifi_interface_handle iface) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_set_alert_handler_stub(wifi_request_id id, wifi_interface_handle iface,
- wifi_alert_handler handler) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_reset_alert_handler_stub(wifi_request_id id, wifi_interface_handle iface) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_get_firmware_version_stub( wifi_interface_handle iface, char *buffer,
- int buffer_size) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_get_ring_buffers_status_stub(wifi_interface_handle iface,
- u32 *num_rings, wifi_ring_buffer_status *status) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_get_logger_supported_feature_set_stub(wifi_interface_handle iface,
- unsigned int *support) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_get_ring_data_stub(wifi_interface_handle iface, char *ring_name) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_get_driver_version_stub(wifi_interface_handle iface, char *buffer,
- int buffer_size) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_enable_tdls_stub(wifi_interface_handle iface, mac_addr addr,
- wifi_tdls_params *params, wifi_tdls_handler handler) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_disable_tdls_stub(wifi_interface_handle iface, mac_addr addr) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_get_tdls_status_stub(wifi_interface_handle iface, mac_addr addr,
- wifi_tdls_status *status) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_get_tdls_capabilities_stub(wifi_interface_handle iface,
- wifi_tdls_capabilities *capabilities) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_set_bssid_blacklist_stub(wifi_request_id id, wifi_interface_handle iface,
- wifi_bssid_params params) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_start_sending_offloaded_packet_stub(wifi_request_id id,
- wifi_interface_handle iface, u8 *ip_packet, u16 ip_packet_len,
- u8 *src_mac_addr, u8 *dst_mac_addr, u32 period_msec) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_stop_sending_offloaded_packet_stub(wifi_request_id id, wifi_interface_handle iface) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_get_wake_reason_stats_stub(wifi_interface_handle iface,
- WLAN_DRIVER_WAKE_REASON_CNT *wifi_wake_reason_cnt) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_configure_nd_offload_stub(wifi_interface_handle iface, u8 enable) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_get_driver_memory_dump_stub(wifi_interface_handle iface,
- wifi_driver_memory_dump_callbacks callbacks) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_start_pkt_fate_monitoring_stub(wifi_interface_handle iface) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_get_tx_pkt_fates_stub(wifi_interface_handle handle,
- wifi_tx_report *tx_report_bufs, size_t n_requested_fates, size_t *n_provided_fates) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_get_rx_pkt_fates_stub(wifi_interface_handle handle,
- wifi_rx_report *rx_report_bufs, size_t n_requested_fates, size_t *n_provided_fates) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-wifi_error wifi_nan_enable_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanEnableRequest* msg) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_nan_disable_request_stub(transaction_id id,
- wifi_interface_handle iface) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_nan_publish_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanPublishRequest* msg) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_nan_publish_cancel_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanPublishCancelRequest* msg) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_nan_subscribe_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanSubscribeRequest* msg) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_nan_subscribe_cancel_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanSubscribeCancelRequest* msg) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_nan_transmit_followup_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanTransmitFollowupRequest* msg) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_nan_stats_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanStatsRequest* msg) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_nan_config_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanConfigRequest* msg) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_nan_tca_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanTCARequest* msg) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_nan_beacon_sdf_payload_request_stub(transaction_id id,
- wifi_interface_handle iface,
- NanBeaconSdfPayloadRequest* msg) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_nan_register_handler_stub(wifi_interface_handle iface,
- NanCallbackHandler handlers) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_nan_get_version_stub(wifi_handle handle,
- NanVersion* version) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_nan_get_capabilities_stub(transaction_id id,
- wifi_interface_handle iface) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_get_packet_filter_capabilities_stub(wifi_interface_handle handle,
- u32 *version, u32 *max_len) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-wifi_error wifi_set_packet_filter_stub(wifi_interface_handle handle,
- const u8 *program, u32 len) {
- return WIFI_ERROR_NOT_SUPPORTED;
-}
-
-int init_wifi_stub_hal_func_table(wifi_hal_fn *hal_fn) {
- if (hal_fn == NULL) {
- return -1;
- }
- hal_fn->wifi_initialize = wifi_initialize_stub;
- hal_fn->wifi_cleanup = wifi_cleanup_stub;
- hal_fn->wifi_event_loop = wifi_event_loop_stub;
- hal_fn->wifi_get_error_info = wifi_get_error_info_stub;
- hal_fn->wifi_get_supported_feature_set = wifi_get_supported_feature_set_stub;
- hal_fn->wifi_get_concurrency_matrix = wifi_get_concurrency_matrix_stub;
- hal_fn->wifi_set_scanning_mac_oui = wifi_set_scanning_mac_oui_stub;
- hal_fn->wifi_get_supported_channels = wifi_get_supported_channels_stub;
- hal_fn->wifi_is_epr_supported = wifi_is_epr_supported_stub;
- hal_fn->wifi_get_ifaces = wifi_get_ifaces_stub;
- hal_fn->wifi_get_iface_name = wifi_get_iface_name_stub;
- hal_fn->wifi_reset_iface_event_handler = wifi_reset_iface_event_handler_stub;
- hal_fn->wifi_start_gscan = wifi_start_gscan_stub;
- hal_fn->wifi_stop_gscan = wifi_stop_gscan_stub;
- hal_fn->wifi_get_cached_gscan_results = wifi_get_cached_gscan_results_stub;
- hal_fn->wifi_set_bssid_hotlist = wifi_set_bssid_hotlist_stub;
- hal_fn->wifi_reset_bssid_hotlist = wifi_reset_bssid_hotlist_stub;
- hal_fn->wifi_set_significant_change_handler = wifi_set_significant_change_handler_stub;
- hal_fn->wifi_reset_significant_change_handler = wifi_reset_significant_change_handler_stub;
- hal_fn->wifi_get_gscan_capabilities = wifi_get_gscan_capabilities_stub;
- hal_fn->wifi_set_link_stats = wifi_set_link_stats_stub;
- hal_fn->wifi_get_link_stats = wifi_get_link_stats_stub;
- hal_fn->wifi_clear_link_stats = wifi_clear_link_stats_stub;
- hal_fn->wifi_get_valid_channels = wifi_get_valid_channels_stub;
- hal_fn->wifi_rtt_range_request = wifi_rtt_range_request_stub;
- hal_fn->wifi_rtt_range_cancel = wifi_rtt_range_cancel_stub;
- hal_fn->wifi_get_rtt_capabilities = wifi_get_rtt_capabilities_stub;
- hal_fn->wifi_start_logging = wifi_start_logging_stub;
- hal_fn->wifi_set_epno_list = wifi_set_epno_list_stub;
- hal_fn->wifi_set_country_code = wifi_set_country_code_stub;
- hal_fn->wifi_enable_tdls = wifi_enable_tdls_stub;
- hal_fn->wifi_disable_tdls = wifi_disable_tdls_stub;
- hal_fn->wifi_get_tdls_status = wifi_get_tdls_status_stub;
- hal_fn->wifi_get_tdls_capabilities = wifi_get_tdls_capabilities_stub;
- hal_fn->wifi_set_nodfs_flag = wifi_set_nodfs_flag_stub;
- hal_fn->wifi_get_firmware_memory_dump = wifi_get_firmware_memory_dump_stub;
- hal_fn->wifi_set_log_handler = wifi_set_log_handler_stub;
- hal_fn->wifi_reset_log_handler = wifi_reset_log_handler_stub;
- hal_fn->wifi_set_alert_handler = wifi_set_alert_handler_stub;
- hal_fn->wifi_reset_alert_handler = wifi_reset_alert_handler_stub;
- hal_fn->wifi_get_firmware_version = wifi_get_firmware_version_stub;
- hal_fn->wifi_get_ring_buffers_status = wifi_get_ring_buffers_status_stub;
- hal_fn->wifi_get_logger_supported_feature_set = wifi_get_logger_supported_feature_set_stub;
- hal_fn->wifi_get_ring_data = wifi_get_ring_data_stub;
- hal_fn->wifi_get_driver_version = wifi_get_driver_version_stub;
- hal_fn->wifi_set_bssid_blacklist = wifi_set_bssid_blacklist_stub;
- hal_fn->wifi_start_sending_offloaded_packet = wifi_start_sending_offloaded_packet_stub;
- hal_fn->wifi_stop_sending_offloaded_packet = wifi_stop_sending_offloaded_packet_stub;
- hal_fn->wifi_get_wake_reason_stats = wifi_get_wake_reason_stats_stub;
- hal_fn->wifi_configure_nd_offload = wifi_configure_nd_offload_stub;
- hal_fn->wifi_get_driver_memory_dump = wifi_get_driver_memory_dump_stub;
- hal_fn->wifi_start_pkt_fate_monitoring = wifi_start_pkt_fate_monitoring_stub;
- hal_fn->wifi_get_tx_pkt_fates = wifi_get_tx_pkt_fates_stub;
- hal_fn->wifi_get_rx_pkt_fates = wifi_get_rx_pkt_fates_stub;
- hal_fn->wifi_nan_enable_request = wifi_nan_enable_request_stub;
- hal_fn->wifi_nan_disable_request = wifi_nan_disable_request_stub;
- hal_fn->wifi_nan_publish_request = wifi_nan_publish_request_stub;
- hal_fn->wifi_nan_publish_cancel_request = wifi_nan_publish_cancel_request_stub;
- hal_fn->wifi_nan_subscribe_request = wifi_nan_subscribe_request_stub;
- hal_fn->wifi_nan_subscribe_cancel_request = wifi_nan_subscribe_cancel_request_stub;
- hal_fn->wifi_nan_transmit_followup_request = wifi_nan_transmit_followup_request_stub;
- hal_fn->wifi_nan_stats_request = wifi_nan_stats_request_stub;
- hal_fn->wifi_nan_config_request = wifi_nan_config_request_stub;
- hal_fn->wifi_nan_tca_request = wifi_nan_tca_request_stub;
- hal_fn->wifi_nan_beacon_sdf_payload_request = wifi_nan_beacon_sdf_payload_request_stub;
- hal_fn->wifi_nan_register_handler = wifi_nan_register_handler_stub;
- hal_fn->wifi_nan_get_version = wifi_nan_get_version_stub;
- hal_fn->wifi_get_packet_filter_capabilities = wifi_get_packet_filter_capabilities_stub;
- hal_fn->wifi_set_packet_filter = wifi_set_packet_filter_stub;
-
- return 0;
-}
diff --git a/tests/wifitests/Android.mk b/tests/wifitests/Android.mk
index e2489c4..c65a9d0 100644
--- a/tests/wifitests/Android.mk
+++ b/tests/wifitests/Android.mk
@@ -28,30 +28,29 @@
$(JNI_H_INCLUDE) \
$(LOCAL_PATH)/../../service/jni \
$(call include-path-for, libhardware)/hardware \
- $(call include-path-for, libhardware_legacy)/hardware_legacy \
+ $(call include-path-for, libhardware_legacy) \
packages/apps/Test/connectivity/sl4n/rapidjson/include \
libcore/include
LOCAL_SRC_FILES := \
jni/wifi_hal_mock.cpp
-ifdef INCLUDE_NAN_FEATURE
+ifeq ($(BOARD_HAS_NAN), true)
LOCAL_SRC_FILES += \
jni/wifi_nan_hal_mock.cpp
endif
LOCAL_MODULE := libwifi-hal-mock
-LOCAL_STATIC_LIBRARIES += libwifi-hal
LOCAL_SHARED_LIBRARIES += \
libnativehelper \
libcutils \
libutils \
libhardware \
- libhardware_legacy \
libnl \
libdl \
- libwifi-service
+ libwifi-service \
+ libwifi-system
include $(BUILD_SHARED_LIBRARY)
@@ -63,7 +62,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
-ifndef INCLUDE_NAN_FEATURE
+ifneq ($(BOARD_HAS_NAN), true)
LOCAL_SRC_FILES := $(filter-out $(call all-java-files-under, \
src/com/android/server/wifi/nan),$(LOCAL_SRC_FILES))
endif
@@ -103,6 +102,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-test \
mockito-target \
+ frameworks-base-testutils \
services \
wifi-service \
@@ -114,20 +114,40 @@
# These must be explicitly included because they are not normally accessible
# from apps.
LOCAL_JNI_SHARED_LIBRARIES := \
+ libcrypto \
libwifi-service \
- libc++ \
- libLLVM \
- libutils \
- libunwind \
- libhardware_legacy \
- libbase \
- libhardware \
- libnl \
- libcutils \
- libnetutils \
+ libEGL \
+ libGLESv2 \
+ libaudioutils \
libbacktrace \
- libnativehelper \
+ libbase \
+ libbinder \
+ libc++ \
+ libcamera_client \
+ libcamera_metadata \
+ libcutils \
+ libexpat \
+ libgui \
+ libhardware \
+ libicui18n \
+ libicuuc \
liblzma \
+ libmedia \
+ libnativehelper \
+ libnbaio \
+ libnetutils \
+ libnl \
+ libpowermanager \
+ libsonivox \
+ libspeexresampler \
+ libstagefright_foundation \
+ libstdc++ \
+ libsync \
+ libwifi-hal \
+ libwifi-system \
+ libui \
+ libunwind \
+ libutils \
ifdef WPA_SUPPLICANT_VERSION
LOCAL_JNI_SHARED_LIBRARIES += libwpa_client
diff --git a/tests/wifitests/README.md b/tests/wifitests/README.md
index c68014e..8812f83 100644
--- a/tests/wifitests/README.md
+++ b/tests/wifitests/README.md
@@ -8,21 +8,22 @@
The easiest way to run tests is simply run
```
-runtest frameworks-wifi
+frameworks/opt/net/wifi/tests/wifitests/runtests.sh
```
-`runtest` will build the test project and push the APK to the connected device. It will then run the
-tests on the device. See `runtest --help` for options to specify individual test classes or methods.
+`runtests.sh` will build the test project and all of its dependencies and push the APK to the
+connected device. It will then run the tests on the device.
-**WARNING:** You have to build the components under test (wifi-service, etc) first before you run
-runtest for changes there to take effect. You can use the following command from your build root to
-build the wifi service and run tests.
+See below for a few example of options to limit which tests are run.
+See the
+[AndroidJUnitRunner Documentation](https://developer.android.com/reference/android/support/test/runner/AndroidJUnitRunner.html)
+for more details on the supported options.
```
-mmma frameworks/opt/net/wifi/tests && runtest frameworks-wifi
+runtests.sh -e package com.android.server.wifi.util
+runtests.sh -e class com.android.server.wifi.WifiStateMachineTest
```
-
If you manually build and push the test APK to the device you can run tests using
```
@@ -41,7 +42,7 @@
## Code Coverage
If you would like to collect code coverage information you can run the `coverage.sh` script located
in this directory. It will rebuild parts of your tree with coverage enabled and then run the tests,
-similar to runtest. If you have multiple devices connected to your machine make sure to set the
+similar to runtests.sh. If you have multiple devices connected to your machine make sure to set the
`ANDROID_SERIAL` environment variable before running the script. You must supply an output directory
for results. By default the results are generated as a set of HTML pages. For example, you can use
the following from the root out your source tree to generate results in the wifi_coverage directory
diff --git a/tests/wifitests/jni/wifi_hal_mock.cpp b/tests/wifitests/jni/wifi_hal_mock.cpp
index 06b2d23..96c0dd5 100644
--- a/tests/wifitests/jni/wifi_hal_mock.cpp
+++ b/tests/wifitests/jni/wifi_hal_mock.cpp
@@ -25,8 +25,8 @@
#include <ctype.h>
#include <sys/socket.h>
#include <linux/if.h>
-#include "wifi.h"
-#include "wifi_hal.h"
+#include "wifi_system/wifi.h"
+#include "hardware_legacy/wifi_hal.h"
#include "jni_helper.h"
#include "wifi_hal_mock.h"
#include <sstream>
diff --git a/tests/wifitests/jni/wifi_hal_mock.h b/tests/wifitests/jni/wifi_hal_mock.h
index 2b9cad4..f316122 100644
--- a/tests/wifitests/jni/wifi_hal_mock.h
+++ b/tests/wifitests/jni/wifi_hal_mock.h
@@ -17,7 +17,7 @@
#ifndef __WIFI_HAL_MOCK_H__
#define __WIFI_HAL_MOCK_H__
-#include "wifi_hal.h"
+#include "hardware_legacy/wifi_hal.h"
#include <rapidjson/document.h>
#include <rapidjson/stringbuffer.h>
@@ -90,6 +90,21 @@
wifi_error wifi_nan_get_version_mock(wifi_handle handle, NanVersion* version);
wifi_error wifi_nan_get_capabilities_mock(transaction_id id,
wifi_interface_handle iface);
+wifi_error wifi_nan_data_interface_create_mock(transaction_id id,
+ wifi_interface_handle iface,
+ char* iface_name);
+wifi_error wifi_nan_data_interface_delete_mock(transaction_id id,
+ wifi_interface_handle iface,
+ char* iface_name);
+wifi_error wifi_nan_data_request_initiator_mock(
+ transaction_id id, wifi_interface_handle iface,
+ NanDataPathInitiatorRequest* msg);
+wifi_error wifi_nan_data_indication_response_mock(
+ transaction_id id, wifi_interface_handle iface,
+ NanDataPathIndicationResponse* msg);
+wifi_error wifi_nan_data_end_mock(transaction_id id,
+ wifi_interface_handle iface,
+ NanDataPathEndRequest* msg);
} // namespace android
diff --git a/tests/wifitests/jni/wifi_nan_hal_mock.cpp b/tests/wifitests/jni/wifi_nan_hal_mock.cpp
index 022fadd..01da519 100644
--- a/tests/wifitests/jni/wifi_nan_hal_mock.cpp
+++ b/tests/wifitests/jni/wifi_nan_hal_mock.cpp
@@ -25,8 +25,8 @@
#include <ctype.h>
#include <sys/socket.h>
#include <linux/if.h>
-#include "wifi.h"
-#include "wifi_hal.h"
+#include "wifi_system/wifi.h"
+#include "hardware_legacy/wifi_hal.h"
#include "jni_helper.h"
#include "wifi_hal_mock.h"
#include <sstream>
@@ -161,6 +161,7 @@
msg->tx_match_filter_len);
jsonW.put_int("rssi_threshold_flag", msg->rssi_threshold_flag);
jsonW.put_int("connmap", msg->connmap);
+ jsonW.put_int("recv_indication_cfg", msg->recv_indication_cfg);
std::string str = jsonW.to_string();
JNIObject < jstring > json_write_string = helper.newStringUTF(str.c_str());
@@ -222,6 +223,7 @@
jsonW.put_int("connmap", msg->connmap);
jsonW.put_int("num_intf_addr_present", msg->num_intf_addr_present);
// TODO: jsonW.put_byte_array("intf_addr", msg->intf_addr, NAN_MAX_SUBSCRIBE_MAX_ADDRESS * NAN_MAC_ADDR_LEN);
+ jsonW.put_int("recv_indication_cfg", msg->recv_indication_cfg);
std::string str = jsonW.to_string();
JNIObject < jstring > json_write_string = helper.newStringUTF(str.c_str());
@@ -286,8 +288,41 @@
wifi_error wifi_nan_config_request_mock(transaction_id id,
wifi_interface_handle iface,
NanConfigRequest* msg) {
+ JNIHelper helper(mock_mVM);
+
ALOGD("wifi_nan_config_request_mock");
- return WIFI_ERROR_UNINITIALIZED;
+ HalMockJsonWriter jsonW;
+ jsonW.put_int("config_sid_beacon", msg->config_sid_beacon);
+ jsonW.put_int("sid_beacon", msg->sid_beacon);
+ jsonW.put_int("config_rssi_proximity", msg->config_rssi_proximity);
+ jsonW.put_int("rssi_proximity", msg->rssi_proximity);
+ jsonW.put_int("config_master_pref", msg->config_master_pref);
+ jsonW.put_int("master_pref", msg->master_pref);
+ jsonW.put_int("config_5g_rssi_close_proximity", msg->config_5g_rssi_close_proximity);
+ jsonW.put_int("rssi_close_proximity_5g_val", msg->rssi_close_proximity_5g_val);
+ jsonW.put_int("config_rssi_window_size", msg->config_rssi_window_size);
+ jsonW.put_int("rssi_window_size_val", msg->rssi_window_size_val);
+ jsonW.put_int("config_cluster_attribute_val", msg->config_cluster_attribute_val);
+ jsonW.put_int("config_scan_params", msg->config_scan_params);
+ // TODO: NanSocialChannelScanParams scan_params_val
+ jsonW.put_int("config_random_factor_force", msg->config_random_factor_force);
+ jsonW.put_int("random_factor_force_val", msg->random_factor_force_val);
+ jsonW.put_int("config_hop_count_force", msg->config_hop_count_force);
+ jsonW.put_int("hop_count_force_val", msg->hop_count_force_val);
+ jsonW.put_int("config_conn_capability", msg->config_conn_capability);
+ // TODO: NanTransmitPostConnectivityCapability conn_capability_val
+ jsonW.put_int("num_config_discovery_attr", msg->num_config_discovery_attr);
+ // TODO: NanTransmitPostDiscovery discovery_attr_val[NAN_MAX_POSTDISCOVERY_LEN]
+ jsonW.put_int("config_fam", msg->config_fam);
+ // TODO: NanFurtherAvailabilityMap fam_val
+ std::string str = jsonW.to_string();
+
+ JNIObject < jstring > json_write_string = helper.newStringUTF(str.c_str());
+
+ helper.callMethod(mock_mObj, "configHalMockNative", "(SLjava/lang/String;)V",
+ (short) id, json_write_string.get());
+
+ return WIFI_SUCCESS;
}
wifi_error wifi_nan_tca_request_mock(transaction_id id,
@@ -327,6 +362,119 @@
return WIFI_SUCCESS;
}
+wifi_error wifi_nan_data_interface_create_mock(transaction_id id,
+ wifi_interface_handle iface,
+ char* iface_name) {
+ JNIHelper helper(mock_mVM);
+
+ ALOGD("wifi_nan_data_interface_create_mock");
+ HalMockJsonWriter jsonW;
+ jsonW.put_byte_array("iface_name", (u8*) iface_name, strlen(iface_name));
+ std::string str = jsonW.to_string();
+
+ JNIObject<jstring> json_write_string = helper.newStringUTF(str.c_str());
+
+ helper.callMethod(mock_mObj, "createNanNetworkInterfaceMockNative",
+ "(SLjava/lang/String;)V", (short)id,
+ json_write_string.get());
+
+ return WIFI_SUCCESS;
+}
+
+wifi_error wifi_nan_data_interface_delete_mock(transaction_id id,
+ wifi_interface_handle iface,
+ char* iface_name) {
+ JNIHelper helper(mock_mVM);
+
+ ALOGD("wifi_nan_data_interface_delete_mock");
+ HalMockJsonWriter jsonW;
+ jsonW.put_byte_array("iface_name", (u8*) iface_name, strlen(iface_name));
+ std::string str = jsonW.to_string();
+
+ JNIObject<jstring> json_write_string = helper.newStringUTF(str.c_str());
+
+ helper.callMethod(mock_mObj, "deleteNanNetworkInterfaceMockNative",
+ "(SLjava/lang/String;)V", (short)id,
+ json_write_string.get());
+
+ return WIFI_SUCCESS;
+}
+
+wifi_error wifi_nan_data_request_initiator_mock(
+ transaction_id id, wifi_interface_handle iface,
+ NanDataPathInitiatorRequest* msg) {
+ JNIHelper helper(mock_mVM);
+
+ ALOGD("wifi_nan_data_request_initiator_mock");
+ HalMockJsonWriter jsonW;
+ jsonW.put_int("service_instance_id", msg->service_instance_id);
+ jsonW.put_int("channel_request_type", (int)msg->channel_request_type);
+ jsonW.put_int("channel", (int)msg->channel);
+ jsonW.put_byte_array("peer_disc_mac_addr", msg->peer_disc_mac_addr, 6);
+ jsonW.put_byte_array("ndp_iface", (u8*) msg->ndp_iface, strlen(msg->ndp_iface));
+ jsonW.put_int("ndp_cfg.security_cfg", (int)msg->ndp_cfg.security_cfg);
+ jsonW.put_int("ndp_cfg.qos_cfg", (int)msg->ndp_cfg.qos_cfg);
+ jsonW.put_int("app_info.ndp_app_info_len", msg->app_info.ndp_app_info_len);
+ jsonW.put_byte_array("app_info.ndp_app_info", msg->app_info.ndp_app_info,
+ msg->app_info.ndp_app_info_len);
+ std::string str = jsonW.to_string();
+
+ JNIObject<jstring> json_write_string = helper.newStringUTF(str.c_str());
+
+ helper.callMethod(mock_mObj, "initiateDataPathMockNative",
+ "(SLjava/lang/String;)V", (short)id,
+ json_write_string.get());
+
+ return WIFI_SUCCESS;
+}
+
+wifi_error wifi_nan_data_indication_response_mock(
+ transaction_id id, wifi_interface_handle iface,
+ NanDataPathIndicationResponse* msg) {
+ JNIHelper helper(mock_mVM);
+
+ ALOGD("wifi_nan_data_indication_response_mock");
+ HalMockJsonWriter jsonW;
+ jsonW.put_int("ndp_instance_id", msg->ndp_instance_id);
+ jsonW.put_byte_array("ndp_iface", (u8*) msg->ndp_iface, strlen(msg->ndp_iface));
+ jsonW.put_int("ndp_cfg.security_cfg", (int)msg->ndp_cfg.security_cfg);
+ jsonW.put_int("ndp_cfg.qos_cfg", (int)msg->ndp_cfg.qos_cfg);
+ jsonW.put_int("app_info.ndp_app_info_len", msg->app_info.ndp_app_info_len);
+ jsonW.put_byte_array("app_info.ndp_app_info", msg->app_info.ndp_app_info,
+ msg->app_info.ndp_app_info_len);
+ jsonW.put_int("rsp_code", (int)msg->rsp_code);
+ std::string str = jsonW.to_string();
+
+ JNIObject<jstring> json_write_string = helper.newStringUTF(str.c_str());
+
+ helper.callMethod(mock_mObj, "respondToDataPathRequestMockNative",
+ "(SLjava/lang/String;)V", (short)id,
+ json_write_string.get());
+
+ return WIFI_SUCCESS;
+}
+
+wifi_error wifi_nan_data_end_mock(transaction_id id,
+ wifi_interface_handle iface,
+ NanDataPathEndRequest* msg) {
+ JNIHelper helper(mock_mVM);
+
+ ALOGD("wifi_nan_data_end_mock");
+ HalMockJsonWriter jsonW;
+ jsonW.put_int("num_ndp_instances", msg->num_ndp_instances);
+ if (msg->num_ndp_instances == 1) {
+ jsonW.put_int("ndp_instance_id", msg->ndp_instance_id[0]);
+ }
+ std::string str = jsonW.to_string();
+
+ JNIObject<jstring> json_write_string = helper.newStringUTF(str.c_str());
+
+ helper.callMethod(mock_mObj, "endDataPathMockNative", "(SLjava/lang/String;)V",
+ (short)id, json_write_string.get());
+
+ return WIFI_SUCCESS;
+}
+
// Callbacks
extern "C" void Java_com_android_server_wifi_nan_WifiNanHalMock_callNotifyResponse(
@@ -374,6 +522,11 @@
"body.nan_capabilities.max_ndp_sessions", &error);
msg.body.nan_capabilities.max_app_info_len = jsonR.get_int(
"body.nan_capabilities.max_app_info_len", &error);
+ msg.body.nan_capabilities.max_queued_transmit_followup_msgs = jsonR.get_int(
+ "body.nan_capabilities.max_queued_transmit_followup_msgs", &error);
+ } else if (msg.response_type == NAN_DP_INITIATOR_RESPONSE) {
+ msg.body.data_request_response.ndp_instance_id = jsonR.get_int(
+ "body.data_request_response.ndp_instance_id", &error);
}
if (error) {
@@ -541,8 +694,123 @@
mCallbackHandlers.EventDisabled(&msg);
}
+extern "C" void Java_com_android_server_wifi_nan_WifiNanHalMock_callTransmitFollowup(
+ JNIEnv* env, jclass clazz, jstring json_args_jstring) {
+ ScopedUtfChars chars(env, json_args_jstring);
+ HalMockJsonReader jsonR(chars.c_str());
+ bool error = false;
+
+ ALOGD("Java_com_android_server_wifi_nan_WifiNanHalMock_callTransmitFollowup: '%s'",
+ chars.c_str());
+
+ NanTransmitFollowupInd msg;
+ msg.id = (transaction_id) jsonR.get_int("id", &error);
+ msg.reason = (NanStatusType) jsonR.get_int("reason", &error);
+
+ if (error) {
+ ALOGE("Java_com_android_server_wifi_nan_WifiNanHalMock_callTransmitFollowup: "
+ "error parsing args");
+ return;
+ }
+
+ mCallbackHandlers.EventTransmitFollowup(&msg);
+}
+
+extern "C" void Java_com_android_server_wifi_nan_WifiNanHalMock_callDataPathRequest(
+ JNIEnv* env, jclass clazz, jstring json_args_jstring) {
+ ScopedUtfChars chars(env, json_args_jstring);
+ HalMockJsonReader jsonR(chars.c_str());
+ bool error = false;
+
+ ALOGD("Java_com_android_server_wifi_nan_WifiNanHalMock_callDataPathRequest: '%s'",
+ chars.c_str());
+
+ NanDataPathRequestInd msg;
+ msg.service_instance_id = jsonR.get_int("service_instance_id", &error);
+ jsonR.get_byte_array("peer_disc_mac_addr", &error, msg.peer_disc_mac_addr, 6);
+ msg.ndp_instance_id = (NanDataPathId)jsonR.get_int("ndp_instance_id", &error);
+ msg.ndp_cfg.security_cfg = (NanDataPathSecurityCfgStatus)jsonR.get_int(
+ "ndp_cfg.security_cfg", &error);
+ msg.ndp_cfg.qos_cfg =
+ (NanDataPathQosCfg)jsonR.get_int("ndp_cfg.qos_cfg", &error);
+ msg.app_info.ndp_app_info_len =
+ jsonR.get_int("app_info.ndp_app_info_len", &error);
+ jsonR.get_byte_array("app_info.ndp_app_info", &error,
+ msg.app_info.ndp_app_info,
+ msg.app_info.ndp_app_info_len);
+
+ if (error) {
+ ALOGE(
+ "Java_com_android_server_wifi_nan_WifiNanHalMock_callDataPathRequest: "
+ "error parsing args");
+ return;
+ }
+
+ mCallbackHandlers.EventDataRequest(&msg);
+}
+
+extern "C" void Java_com_android_server_wifi_nan_WifiNanHalMock_callDataPathConfirm(
+ JNIEnv* env, jclass clazz, jstring json_args_jstring) {
+ ScopedUtfChars chars(env, json_args_jstring);
+ HalMockJsonReader jsonR(chars.c_str());
+ bool error = false;
+
+ ALOGD("Java_com_android_server_wifi_nan_WifiNanHalMock_callDataPathConfirm: '%s'",
+ chars.c_str());
+
+ NanDataPathConfirmInd msg;
+ msg.ndp_instance_id = (NanDataPathId)jsonR.get_int("ndp_instance_id", &error);
+ jsonR.get_byte_array("peer_ndi_mac_addr", &error, msg.peer_ndi_mac_addr, 6);
+ msg.app_info.ndp_app_info_len =
+ jsonR.get_int("app_info.ndp_app_info_len", &error);
+ jsonR.get_byte_array("app_info.ndp_app_info", &error,
+ msg.app_info.ndp_app_info,
+ msg.app_info.ndp_app_info_len);
+ msg.rsp_code = (NanDataPathResponseCode)jsonR.get_int("rsp_code", &error);
+ msg.reason_code = (NanStatusType)jsonR.get_int("reason_code", &error);
+
+ if (error) {
+ ALOGE(
+ "Java_com_android_server_wifi_nan_WifiNanHalMock_callDataPathConfirm: "
+ "error parsing args");
+ return;
+ }
+
+ mCallbackHandlers.EventDataConfirm(&msg);
+}
+
+extern "C" void Java_com_android_server_wifi_nan_WifiNanHalMock_callDataPathEnd(
+ JNIEnv* env, jclass clazz, jstring json_args_jstring) {
+ ScopedUtfChars chars(env, json_args_jstring);
+ HalMockJsonReader jsonR(chars.c_str());
+ bool error = false;
+
+ ALOGD("Java_com_android_server_wifi_nan_WifiNanHalMock_callDataPathEnd: '%s'",
+ chars.c_str());
+
+ int num_ndp_instances = jsonR.get_int("num_ndp_instances", &error);
+
+ NanDataPathEndInd* msg = (NanDataPathEndInd*) malloc(sizeof(NanDataPathEndInd)
+ + num_ndp_instances * sizeof(NanDataPathId));
+ msg->num_ndp_instances = num_ndp_instances;
+ for (int i = 0; i < num_ndp_instances; ++i) {
+ msg->ndp_instance_id[i] = jsonR.get_int("ndp_instance_id", &error) + i;
+ }
+
+ if (error) {
+ ALOGE(
+ "Java_com_android_server_wifi_nan_WifiNanHalMock_callDataPathEnd: "
+ "error parsing args");
+ return;
+ }
+
+ mCallbackHandlers.EventDataEnd(msg);
+
+ free(msg);
+}
+
// TODO: Not currently used: add as needed
-//void (*EventUnMatch) (NanUnmatchInd* event);
+//void (*EventMatchExpired) (NanUnmatchInd* event);
//void (*EventTca) (NanTCAInd* event);
//void (*EventBeaconSdfPayload) (NanBeaconSdfPayloadInd* event);
@@ -569,6 +837,11 @@
fn->wifi_nan_register_handler = wifi_nan_register_handler_mock;
fn->wifi_nan_get_version = wifi_nan_get_version_mock;
fn->wifi_nan_get_capabilities = wifi_nan_get_capabilities_mock;
+ fn->wifi_nan_data_interface_create = wifi_nan_data_interface_create_mock;
+ fn->wifi_nan_data_interface_delete = wifi_nan_data_interface_delete_mock;
+ fn->wifi_nan_data_request_initiator = wifi_nan_data_request_initiator_mock;
+ fn->wifi_nan_data_indication_response = wifi_nan_data_indication_response_mock;
+ fn->wifi_nan_data_end = wifi_nan_data_end_mock;
return 0;
}
diff --git a/tests/wifitests/runtests.sh b/tests/wifitests/runtests.sh
new file mode 100755
index 0000000..c0551a4
--- /dev/null
+++ b/tests/wifitests/runtests.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+if [ -z $ANDROID_BUILD_TOP ]; then
+ echo "You need to source and lunch before you can use this script"
+ exit 1
+fi
+
+echo "Running tests"
+
+set -e # fail early
+
+echo "+ mmma -j32 $ANDROID_BUILD_TOP/frameworks/opt/net/wifi/tests"
+# NOTE Don't actually run the command above since this shell doesn't inherit functions from the
+# caller.
+make -j32 -C $ANDROID_BUILD_TOP -f build/core/main.mk MODULES-IN-frameworks-opt-net-wifi-tests
+
+set -x # print commands
+
+adb root
+adb wait-for-device
+
+adb install -r -g "$OUT/data/app/FrameworksWifiTests/FrameworksWifiTests.apk"
+
+adb shell am instrument -w "$@" 'com.android.server.wifi.test/android.support.test.runner.AndroidJUnitRunner'
diff --git a/tests/wifitests/src/android/net/wifi/WifiEnterpriseConfigTest.java b/tests/wifitests/src/android/net/wifi/WifiEnterpriseConfigTest.java
index 75480b5..814594d 100644
--- a/tests/wifitests/src/android/net/wifi/WifiEnterpriseConfigTest.java
+++ b/tests/wifitests/src/android/net/wifi/WifiEnterpriseConfigTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNull;
import android.net.wifi.WifiEnterpriseConfig.Eap;
import android.net.wifi.WifiEnterpriseConfig.Phase2;
@@ -50,6 +51,22 @@
}
@Test
+ public void testGetEmptyCaCertificate() {
+ // A newly-constructed WifiEnterpriseConfig object should have no CA certificate.
+ assertNull(mEnterpriseConfig.getCaCertificate());
+ assertNull(mEnterpriseConfig.getCaCertificates());
+ // Setting CA certificate to null explicitly.
+ mEnterpriseConfig.setCaCertificate(null);
+ assertNull(mEnterpriseConfig.getCaCertificate());
+ // Setting CA certificate to null using setCaCertificates().
+ mEnterpriseConfig.setCaCertificates(null);
+ assertNull(mEnterpriseConfig.getCaCertificates());
+ // Setting CA certificate to zero-length array.
+ mEnterpriseConfig.setCaCertificates(new X509Certificate[0]);
+ assertNull(mEnterpriseConfig.getCaCertificates());
+ }
+
+ @Test
public void testSetGetSingleCaCertificate() {
X509Certificate cert0 = FakeKeys.CA_CERT0;
mEnterpriseConfig.setCaCertificate(cert0);
diff --git a/tests/wifitests/src/android/net/wifi/WifiScannerTest.java b/tests/wifitests/src/android/net/wifi/WifiScannerTest.java
index 5034c2a..a829eb9 100644
--- a/tests/wifitests/src/android/net/wifi/WifiScannerTest.java
+++ b/tests/wifitests/src/android/net/wifi/WifiScannerTest.java
@@ -30,10 +30,10 @@
import android.net.wifi.WifiScanner.BssidListener;
import android.os.Handler;
import android.os.Message;
+import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.server.wifi.BidirectionalAsyncChannelServer;
-import com.android.server.wifi.MockLooper;
+import com.android.internal.util.test.BidirectionalAsyncChannelServer;
import org.junit.After;
import org.junit.Before;
@@ -55,7 +55,7 @@
private BssidListener mBssidListener;
private WifiScanner mWifiScanner;
- private MockLooper mLooper;
+ private TestLooper mLooper;
private Handler mHandler;
/**
@@ -64,7 +64,7 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mLooper = new MockLooper();
+ mLooper = new TestLooper();
mHandler = mock(Handler.class);
BidirectionalAsyncChannelServer server = new BidirectionalAsyncChannelServer(
mContext, mLooper.getLooper(), mHandler);
diff --git a/tests/wifitests/src/com/android/server/wifi/AnqpCacheTest.java b/tests/wifitests/src/com/android/server/wifi/AnqpCacheTest.java
index 1a96831..9b06d13 100644
--- a/tests/wifitests/src/com/android/server/wifi/AnqpCacheTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/AnqpCacheTest.java
@@ -113,7 +113,7 @@
private void advanceTimeAndTrimCache(long howManyMillis) {
mCurrentTimeMillis += howManyMillis;
Log.d(TAG, "Time set to " + mCurrentTimeMillis);
- when(mClock.currentTimeMillis()).thenReturn(mCurrentTimeMillis);
+ when(mClock.getWallClockMillis()).thenReturn(mCurrentTimeMillis);
mCache.clear(false, true);
}
diff --git a/tests/wifitests/src/com/android/server/wifi/BidirectionalAsyncChannel.java b/tests/wifitests/src/com/android/server/wifi/BidirectionalAsyncChannel.java
deleted file mode 100644
index 75c0f87..0000000
--- a/tests/wifitests/src/com/android/server/wifi/BidirectionalAsyncChannel.java
+++ /dev/null
@@ -1,96 +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
- */
-
-package com.android.server.wifi;
-
-import static org.junit.Assert.assertEquals;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
-import android.util.Log;
-
-import com.android.internal.util.AsyncChannel;
-
-
-/**
- * Provides an AsyncChannel interface that implements the connection initiating half of a
- * bidirectional channel as described in {@link com.android.internal.util.AsyncChannel}.
- */
-public class BidirectionalAsyncChannel {
- private static final String TAG = "BidirectionalAsyncChannel";
-
- private AsyncChannel mChannel;
- public enum ChannelState { DISCONNECTED, HALF_CONNECTED, CONNECTED, FAILURE };
- private ChannelState mState = ChannelState.DISCONNECTED;
-
- public void assertConnected() {
- assertEquals("AsyncChannel was not fully connected", ChannelState.CONNECTED, mState);
- }
-
- public void connect(final Looper looper, final Messenger messenger,
- final Handler incomingMessageHandler) {
- assertEquals("AsyncChannel must be disconnected to connect",
- ChannelState.DISCONNECTED, mState);
- mChannel = new AsyncChannel();
- Handler rawMessageHandler = new Handler(looper) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
- if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- Log.d(TAG, "Successfully half connected " + this);
- mChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
- mState = ChannelState.HALF_CONNECTED;
- } else {
- Log.d(TAG, "Failed to connect channel " + this);
- mState = ChannelState.FAILURE;
- mChannel = null;
- }
- break;
- case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
- mState = ChannelState.CONNECTED;
- Log.d(TAG, "Channel fully connected" + this);
- break;
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- mState = ChannelState.DISCONNECTED;
- mChannel = null;
- Log.d(TAG, "Channel disconnected" + this);
- break;
- default:
- incomingMessageHandler.handleMessage(msg);
- break;
- }
- }
- };
- mChannel.connect(null, rawMessageHandler, messenger);
- }
-
- public void disconnect() {
- assertEquals("AsyncChannel must be connected to disconnect",
- ChannelState.CONNECTED, mState);
- mChannel.sendMessage(AsyncChannel.CMD_CHANNEL_DISCONNECT);
- mState = ChannelState.DISCONNECTED;
- mChannel = null;
- }
-
- public void sendMessage(Message msg) {
- assertEquals("AsyncChannel must be connected to send messages",
- ChannelState.CONNECTED, mState);
- mChannel.sendMessage(msg);
- }
-}
diff --git a/tests/wifitests/src/com/android/server/wifi/BidirectionalAsyncChannelServer.java b/tests/wifitests/src/com/android/server/wifi/BidirectionalAsyncChannelServer.java
deleted file mode 100644
index 6cc0e90..0000000
--- a/tests/wifitests/src/com/android/server/wifi/BidirectionalAsyncChannelServer.java
+++ /dev/null
@@ -1,86 +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.
- */
-
-package com.android.server.wifi;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
-import android.util.Log;
-
-import com.android.internal.util.AsyncChannel;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Provides an interface for the server side implementation of a bidirectional channel as described
- * in {@link com.android.internal.util.AsyncChannel}.
- */
-public class BidirectionalAsyncChannelServer {
-
- private static final String TAG = "BidirectionalAsyncChannelServer";
-
- // Keeps track of incoming clients, which are identifiable by their messengers.
- private final Map<Messenger, AsyncChannel> mClients = new HashMap<>();
-
- private Messenger mMessenger;
-
- public BidirectionalAsyncChannelServer(final Context context, final Looper looper,
- final Handler messageHandler) {
- Handler handler = new Handler(looper) {
- @Override
- public void handleMessage(Message msg) {
- AsyncChannel channel = mClients.get(msg.replyTo);
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
- if (channel != null) {
- Log.d(TAG, "duplicate client connection: " + msg.sendingUid);
- channel.replyToMessage(msg,
- AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
- AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED);
- } else {
- channel = new AsyncChannel();
- mClients.put(msg.replyTo, channel);
- channel.connected(context, this, msg.replyTo);
- channel.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
- AsyncChannel.STATUS_SUCCESSFUL);
- }
- break;
- case AsyncChannel.CMD_CHANNEL_DISCONNECT:
- channel.disconnect();
- break;
-
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- mClients.remove(msg.replyTo);
- break;
-
- default:
- messageHandler.handleMessage(msg);
- break;
- }
- }
- };
- mMessenger = new Messenger(handler);
- }
-
- public Messenger getMessenger() {
- return mMessenger;
- }
-
-}
diff --git a/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java b/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java
index 328feaf..c1e333b 100644
--- a/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java
@@ -21,6 +21,7 @@
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.WifiConfiguration;
import android.os.UserHandle;
@@ -28,8 +29,6 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.util.SparseArray;
-import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments;
-
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
diff --git a/tests/wifitests/src/com/android/server/wifi/HalMockUtils.java b/tests/wifitests/src/com/android/server/wifi/HalMockUtils.java
index 6e485f6..3235ff7 100644
--- a/tests/wifitests/src/com/android/server/wifi/HalMockUtils.java
+++ b/tests/wifitests/src/com/android/server/wifi/HalMockUtils.java
@@ -19,12 +19,11 @@
import android.os.Bundle;
import android.util.Log;
-import com.android.server.wifi.WifiNative;
-
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Iterator;
@@ -43,14 +42,24 @@
public static void initHalMockLibrary() throws Exception {
/*
- * Setting the Wi-Fi HAL handle and interface (array) to dummy
- * values. Required to fake the init checking code to think that
- * the HAL actually started.
- *
- * Note that values are not important since they are only used by
- * the real HAL - which is mocked-out in this use-case.
+ * Setting the Wi-Fi HAL handle and interface (array) to dummy values.
+ * Need to install a default WifiNative (since another mock may have
+ * installed something else). Required to fake the init checking code to
+ * think that the HAL actually started. Note that values are not
+ * important since they are only used by the real HAL - which is
+ * mocked-out in this use-case.
*/
- Field field = WifiNative.class.getDeclaredField("sWifiHalHandle");
+
+ Constructor<WifiNative> ctr = WifiNative.class.getDeclaredConstructor(String.class,
+ Boolean.TYPE);
+ ctr.setAccessible(true);
+ WifiNative wifiNativeInstance = ctr.newInstance("wlan0", true);
+
+ Field field = WifiNative.class.getDeclaredField("wlanNativeInterface");
+ field.setAccessible(true);
+ field.set(null, wifiNativeInstance);
+
+ field = WifiNative.class.getDeclaredField("sWifiHalHandle");
field.setAccessible(true);
long currentWifiHalHandle = field.getLong(null);
if (DBG) Log.d(TAG, "currentWifiHalHandle=" + currentWifiHalHandle);
diff --git a/tests/wifitests/src/com/android/server/wifi/MockAlarmManager.java b/tests/wifitests/src/com/android/server/wifi/MockAlarmManager.java
deleted file mode 100644
index 02af281..0000000
--- a/tests/wifitests/src/com/android/server/wifi/MockAlarmManager.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wifi;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyLong;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-
-import android.app.AlarmManager;
-import android.os.Handler;
-
-import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Creates an AlarmManager whose alarm dispatch can be controlled
- * Currently only supports alarm listeners
- *
- * Alarm listeners will be dispatched to the handler provided or will
- * be dispatched imediatly if they would have been sent to the main
- * looper (handler was null).
- */
-public class MockAlarmManager {
- private final AlarmManager mAlarmManager;
- private final List<PendingAlarm> mPendingAlarms;
-
- public MockAlarmManager() throws Exception {
- mPendingAlarms = new ArrayList<>();
-
- mAlarmManager = mock(AlarmManager.class);
- doAnswer(new SetListenerAnswer()).when(mAlarmManager).set(anyInt(), anyLong(), anyString(),
- any(AlarmManager.OnAlarmListener.class), any(Handler.class));
- doAnswer(new SetListenerAnswer()).when(mAlarmManager).setExact(anyInt(), anyLong(),
- anyString(), any(AlarmManager.OnAlarmListener.class), any(Handler.class));
- doAnswer(new CancelListenerAnswer())
- .when(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class));
- }
-
- public AlarmManager getAlarmManager() {
- return mAlarmManager;
- }
-
- /**
- * Dispatch a pending alarm with the given tag
- * @return if any alarm was dispatched
- */
- public boolean dispatch(String tag) {
- for (int i = 0; i < mPendingAlarms.size(); ++i) {
- PendingAlarm alarm = mPendingAlarms.get(i);
- if (Objects.equals(tag, alarm.getTag())) {
- mPendingAlarms.remove(i);
- alarm.dispatch();
- return true;
- }
- }
- return false;
- }
-
- /**
- * @return if an alarm with the given tag is pending
- */
- public boolean isPending(String tag) {
- for (int i = 0; i < mPendingAlarms.size(); ++i) {
- PendingAlarm alarm = mPendingAlarms.get(i);
- if (Objects.equals(tag, alarm.getTag())) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * @return trigger time of an pending alarm with the given tag
- * -1 if no pending alram with the given tag
- */
- public long getTriggerTimeMillis(String tag) {
- for (int i = 0; i < mPendingAlarms.size(); ++i) {
- PendingAlarm alarm = mPendingAlarms.get(i);
- if (Objects.equals(tag, alarm.getTag())) {
- return alarm.getTriggerTimeMillis();
- }
- }
- return -1;
- }
-
- private static class PendingAlarm {
- private final int mType;
- private final long mTriggerAtMillis;
- private final String mTag;
- private final Runnable mCallback;
-
- public PendingAlarm(int type, long triggerAtMillis, String tag, Runnable callback) {
- mType = type;
- mTriggerAtMillis = triggerAtMillis;
- mTag = tag;
- mCallback = callback;
- }
-
- public void dispatch() {
- if (mCallback != null) {
- mCallback.run();
- }
- }
-
- public Runnable getCallback() {
- return mCallback;
- }
-
- public String getTag() {
- return mTag;
- }
-
- public long getTriggerTimeMillis() {
- return mTriggerAtMillis;
- }
- }
-
- private class SetListenerAnswer extends AnswerWithArguments {
- public void answer(int type, long triggerAtMillis, String tag,
- AlarmManager.OnAlarmListener listener, Handler handler) {
- mPendingAlarms.add(new PendingAlarm(type, triggerAtMillis, tag,
- new AlarmListenerRunnable(listener, handler)));
- }
- }
-
- private class CancelListenerAnswer extends AnswerWithArguments {
- public void answer(AlarmManager.OnAlarmListener listener) {
- Iterator<PendingAlarm> alarmItr = mPendingAlarms.iterator();
- while (alarmItr.hasNext()) {
- PendingAlarm alarm = alarmItr.next();
- if (alarm.getCallback() instanceof AlarmListenerRunnable) {
- AlarmListenerRunnable alarmCallback =
- (AlarmListenerRunnable) alarm.getCallback();
- if (alarmCallback.getListener() == listener) {
- alarmItr.remove();
- }
- }
- }
- }
- }
-
- private static class AlarmListenerRunnable implements Runnable {
- private final AlarmManager.OnAlarmListener mListener;
- private final Handler mHandler;
- public AlarmListenerRunnable(AlarmManager.OnAlarmListener listener, Handler handler) {
- mListener = listener;
- mHandler = handler;
- }
-
- public Handler getHandler() {
- return mHandler;
- }
-
- public AlarmManager.OnAlarmListener getListener() {
- return mListener;
- }
-
- public void run() {
- if (mHandler != null) {
- mHandler.post(new Runnable() {
- public void run() {
- mListener.onAlarm();
- }
- });
- } else { // normally gets dispatched in main looper
- mListener.onAlarm();
- }
- }
- }
-}
diff --git a/tests/wifitests/src/com/android/server/wifi/MockAnswerUtil.java b/tests/wifitests/src/com/android/server/wifi/MockAnswerUtil.java
deleted file mode 100644
index 1a3dfa1..0000000
--- a/tests/wifitests/src/com/android/server/wifi/MockAnswerUtil.java
+++ /dev/null
@@ -1,62 +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.
- */
-
-package com.android.server.wifi;
-
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Arrays;
-
-/**
- * Utilities for creating Answers for mock objects
- */
-public class MockAnswerUtil {
-
- /**
- * Answer that calls the method in the Answer called "answer" that matches the type signature of
- * the method being answered. An error will be thrown at runtime if the signature does not match
- * exactly.
- */
- public static class AnswerWithArguments implements Answer<Object> {
- @Override
- public final Object answer(InvocationOnMock invocation) throws Throwable {
- Method method = invocation.getMethod();
- try {
- Method implementation = getClass().getMethod("answer", method.getParameterTypes());
- if (!implementation.getReturnType().equals(method.getReturnType())) {
- throw new RuntimeException("Found answer method does not have expected return "
- + "type. Expected: " + method.getReturnType() + ", got "
- + implementation.getReturnType());
- }
- Object[] args = invocation.getArguments();
- try {
- return implementation.invoke(this, args);
- } catch (IllegalAccessException e) {
- throw new RuntimeException("Error invoking answer method", e);
- } catch (InvocationTargetException e) {
- throw e.getCause();
- }
- } catch (NoSuchMethodException e) {
- throw new RuntimeException("Could not find answer method with the expected args "
- + Arrays.toString(method.getParameterTypes()), e);
- }
- }
- }
-
-}
diff --git a/tests/wifitests/src/com/android/server/wifi/MockLooper.java b/tests/wifitests/src/com/android/server/wifi/MockLooper.java
deleted file mode 100644
index 34d80a4..0000000
--- a/tests/wifitests/src/com/android/server/wifi/MockLooper.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wifi;
-
-import static org.junit.Assert.assertTrue;
-
-import android.os.Looper;
-import android.os.Message;
-import android.os.MessageQueue;
-import android.os.SystemClock;
-import android.util.Log;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-/**
- * Creates a looper whose message queue can be manipulated
- * This allows testing code that uses a looper to dispatch messages in a deterministic manner
- * Creating a MockLooper will also install it as the looper for the current thread
- */
-public class MockLooper {
- protected final Looper mLooper;
-
- private static final Constructor<Looper> LOOPER_CONSTRUCTOR;
- private static final Field THREAD_LOCAL_LOOPER_FIELD;
- private static final Field MESSAGE_QUEUE_MESSAGES_FIELD;
- private static final Field MESSAGE_NEXT_FIELD;
- private static final Field MESSAGE_WHEN_FIELD;
- private static final Method MESSAGE_MARK_IN_USE_METHOD;
- private static final String TAG = "MockLooper";
-
- private AutoDispatchThread mAutoDispatchThread;
-
- static {
- try {
- LOOPER_CONSTRUCTOR = Looper.class.getDeclaredConstructor(Boolean.TYPE);
- LOOPER_CONSTRUCTOR.setAccessible(true);
- THREAD_LOCAL_LOOPER_FIELD = Looper.class.getDeclaredField("sThreadLocal");
- THREAD_LOCAL_LOOPER_FIELD.setAccessible(true);
- MESSAGE_QUEUE_MESSAGES_FIELD = MessageQueue.class.getDeclaredField("mMessages");
- MESSAGE_QUEUE_MESSAGES_FIELD.setAccessible(true);
- MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
- MESSAGE_NEXT_FIELD.setAccessible(true);
- MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
- MESSAGE_WHEN_FIELD.setAccessible(true);
- MESSAGE_MARK_IN_USE_METHOD = Message.class.getDeclaredMethod("markInUse");
- MESSAGE_MARK_IN_USE_METHOD.setAccessible(true);
- } catch (NoSuchFieldException | NoSuchMethodException e) {
- throw new RuntimeException("Failed to initialize MockLooper", e);
- }
- }
-
-
- public MockLooper() {
- try {
- mLooper = LOOPER_CONSTRUCTOR.newInstance(false);
-
- ThreadLocal<Looper> threadLocalLooper = (ThreadLocal<Looper>) THREAD_LOCAL_LOOPER_FIELD
- .get(null);
- threadLocalLooper.set(mLooper);
- } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
- throw new RuntimeException("Reflection error constructing or accessing looper", e);
- }
- }
-
- public Looper getLooper() {
- return mLooper;
- }
-
- private Message getMessageLinkedList() {
- try {
- MessageQueue queue = mLooper.getQueue();
- return (Message) MESSAGE_QUEUE_MESSAGES_FIELD.get(queue);
- } catch (IllegalAccessException e) {
- throw new RuntimeException("Access failed in MockLooper: get - MessageQueue.mMessages",
- e);
- }
- }
-
- public void moveTimeForward(long milliSeconds) {
- try {
- Message msg = getMessageLinkedList();
- while (msg != null) {
- long updatedWhen = msg.getWhen() - milliSeconds;
- if (updatedWhen < 0) {
- updatedWhen = 0;
- }
- MESSAGE_WHEN_FIELD.set(msg, updatedWhen);
- msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
- }
- } catch (IllegalAccessException e) {
- throw new RuntimeException("Access failed in MockLooper: set - Message.when", e);
- }
- }
-
- private Message messageQueueNext() {
- try {
- long now = SystemClock.uptimeMillis();
-
- Message prevMsg = null;
- Message msg = getMessageLinkedList();
- if (msg != null && msg.getTarget() == null) {
- // Stalled by a barrier. Find the next asynchronous message in
- // the queue.
- do {
- prevMsg = msg;
- msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
- } while (msg != null && !msg.isAsynchronous());
- }
- if (msg != null) {
- if (now >= msg.getWhen()) {
- // Got a message.
- if (prevMsg != null) {
- MESSAGE_NEXT_FIELD.set(prevMsg, MESSAGE_NEXT_FIELD.get(msg));
- } else {
- MESSAGE_QUEUE_MESSAGES_FIELD.set(mLooper.getQueue(),
- MESSAGE_NEXT_FIELD.get(msg));
- }
- MESSAGE_NEXT_FIELD.set(msg, null);
- MESSAGE_MARK_IN_USE_METHOD.invoke(msg);
- return msg;
- }
- }
- } catch (IllegalAccessException | InvocationTargetException e) {
- throw new RuntimeException("Access failed in MockLooper", e);
- }
-
- return null;
- }
-
- /**
- * @return true if there are pending messages in the message queue
- */
- public synchronized boolean isIdle() {
- Message messageList = getMessageLinkedList();
-
- return messageList != null && SystemClock.uptimeMillis() >= messageList.getWhen();
- }
-
- /**
- * @return the next message in the Looper's message queue or null if there is none
- */
- public synchronized Message nextMessage() {
- if (isIdle()) {
- return messageQueueNext();
- } else {
- return null;
- }
- }
-
- /**
- * Dispatch the next message in the queue
- * Asserts that there is a message in the queue
- */
- public synchronized void dispatchNext() {
- assertTrue(isIdle());
- Message msg = messageQueueNext();
- if (msg == null) {
- return;
- }
- msg.getTarget().dispatchMessage(msg);
- }
-
- /**
- * Dispatch all messages currently in the queue
- * Will not fail if there are no messages pending
- * @return the number of messages dispatched
- */
- public synchronized int dispatchAll() {
- int count = 0;
- while (isIdle()) {
- dispatchNext();
- ++count;
- }
- return count;
- }
-
- /**
- * Thread used to dispatch messages when the main thread is blocked waiting for a response.
- */
- private class AutoDispatchThread extends Thread {
- private static final int MAX_LOOPS = 100;
- private static final int LOOP_SLEEP_TIME_MS = 10;
-
- private RuntimeException mAutoDispatchException = null;
-
- /**
- * Run method for the auto dispatch thread.
- * The thread loops a maximum of MAX_LOOPS times with a 10ms sleep between loops.
- * The thread continues looping and attempting to dispatch all messages until at
- * least one message has been dispatched.
- */
- @Override
- public void run() {
- int dispatchCount = 0;
- for (int i = 0; i < MAX_LOOPS; i++) {
- try {
- dispatchCount = dispatchAll();
- } catch (RuntimeException e) {
- mAutoDispatchException = e;
- }
- Log.d(TAG, "dispatched " + dispatchCount + " messages");
- if (dispatchCount > 0) {
- return;
- }
- try {
- Thread.sleep(LOOP_SLEEP_TIME_MS);
- } catch (InterruptedException e) {
- mAutoDispatchException = new IllegalStateException(
- "stopAutoDispatch called before any messages were dispatched.");
- return;
- }
- }
- Log.e(TAG, "AutoDispatchThread did not dispatch any messages.");
- mAutoDispatchException = new IllegalStateException(
- "MockLooper did not dispatch any messages before exiting.");
- }
-
- /**
- * Method allowing the MockLooper to pass any exceptions thrown by the thread to be passed
- * to the main thread.
- *
- * @return RuntimeException Exception created by stopping without dispatching a message
- */
- public RuntimeException getException() {
- return mAutoDispatchException;
- }
- }
-
- /**
- * Create and start a new AutoDispatchThread if one is not already running.
- */
- public void startAutoDispatch() {
- if (mAutoDispatchThread != null) {
- throw new IllegalStateException(
- "startAutoDispatch called with the AutoDispatchThread already running.");
- }
- mAutoDispatchThread = new AutoDispatchThread();
- mAutoDispatchThread.start();
- }
-
- /**
- * If an AutoDispatchThread is currently running, stop and clean up.
- */
- public void stopAutoDispatch() {
- if (mAutoDispatchThread != null) {
- if (mAutoDispatchThread.isAlive()) {
- mAutoDispatchThread.interrupt();
- }
- try {
- mAutoDispatchThread.join();
- } catch (InterruptedException e) {
- // Catch exception from join.
- }
-
- RuntimeException e = mAutoDispatchThread.getException();
- mAutoDispatchThread = null;
- if (e != null) {
- throw e;
- }
- } else {
- // stopAutoDispatch was called when startAutoDispatch has not created a new thread.
- throw new IllegalStateException(
- "stopAutoDispatch called without startAutoDispatch.");
- }
- }
-}
diff --git a/tests/wifitests/src/com/android/server/wifi/MockLooperTest.java b/tests/wifitests/src/com/android/server/wifi/MockLooperTest.java
deleted file mode 100644
index 74a73d6..0000000
--- a/tests/wifitests/src/com/android/server/wifi/MockLooperTest.java
+++ /dev/null
@@ -1,371 +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.
- */
-
-package com.android.server.wifi;
-
-import static org.hamcrest.core.IsEqual.equalTo;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ErrorCollector;
-import org.mockito.ArgumentCaptor;
-import org.mockito.InOrder;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Test MockLooperAbstractTime which provides control over "time". Note that
- * real-time is being used as well. Therefore small time increments are NOT
- * reliable. All tests are in "K" units (i.e. *1000).
- */
-
-@SmallTest
-public class MockLooperTest {
- private MockLooper mMockLooper;
- private Handler mHandler;
- private Handler mHandlerSpy;
-
- @Rule
- public ErrorCollector collector = new ErrorCollector();
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- mMockLooper = new MockLooper();
- mHandler = new Handler(mMockLooper.getLooper());
- mHandlerSpy = spy(mHandler);
- }
-
- /**
- * Basic test with no time stamps: dispatch 4 messages, check that all 4
- * delivered (in correct order).
- */
- @Test
- public void testNoTimeMovement() {
- final int messageA = 1;
- final int messageB = 2;
- final int messageC = 3;
-
- InOrder inOrder = inOrder(mHandlerSpy);
- ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
-
- mHandlerSpy.sendMessage(mHandler.obtainMessage(messageA));
- mHandlerSpy.sendMessage(mHandler.obtainMessage(messageA));
- mHandlerSpy.sendMessage(mHandler.obtainMessage(messageB));
- mHandlerSpy.sendMessage(mHandler.obtainMessage(messageC));
- mMockLooper.dispatchAll();
-
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("1: messageA", messageA, equalTo(messageCaptor.getValue().what));
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("2: messageA", messageA, equalTo(messageCaptor.getValue().what));
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("3: messageB", messageB, equalTo(messageCaptor.getValue().what));
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("4: messageC", messageC, equalTo(messageCaptor.getValue().what));
-
- inOrder.verify(mHandlerSpy, never()).handleMessage(any(Message.class));
- }
-
- /**
- * Test message sequence: A, B, C@5K, A@10K. Don't move time.
- * <p>
- * Expected: only get A, B
- */
- @Test
- public void testDelayedDispatchNoTimeMove() {
- final int messageA = 1;
- final int messageB = 2;
- final int messageC = 3;
-
- InOrder inOrder = inOrder(mHandlerSpy);
- ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
-
- mHandlerSpy.sendMessage(mHandler.obtainMessage(messageA));
- mHandlerSpy.sendMessage(mHandler.obtainMessage(messageB));
- mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageC), 5000);
- mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageA), 10000);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("1: messageA", messageA, equalTo(messageCaptor.getValue().what));
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("2: messageB", messageB, equalTo(messageCaptor.getValue().what));
-
- inOrder.verify(mHandlerSpy, never()).handleMessage(any(Message.class));
- }
-
- /**
- * Test message sequence: A, B, C@5K, A@10K, Advance time by 5K.
- * <p>
- * Expected: only get A, B, C
- */
- @Test
- public void testDelayedDispatchAdvanceTimeOnce() {
- final int messageA = 1;
- final int messageB = 2;
- final int messageC = 3;
-
- InOrder inOrder = inOrder(mHandlerSpy);
- ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
-
- mHandlerSpy.sendMessage(mHandler.obtainMessage(messageA));
- mHandlerSpy.sendMessage(mHandler.obtainMessage(messageB));
- mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageC), 5000);
- mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageA), 10000);
- mMockLooper.moveTimeForward(5000);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("1: messageA", messageA, equalTo(messageCaptor.getValue().what));
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("2: messageB", messageB, equalTo(messageCaptor.getValue().what));
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("3: messageC", messageC, equalTo(messageCaptor.getValue().what));
-
- inOrder.verify(mHandlerSpy, never()).handleMessage(any(Message.class));
- }
-
- /**
- * Test message sequence: A, B, C@5K, Advance time by 4K, A@1K, B@2K Advance
- * time by 1K.
- * <p>
- * Expected: get A, B, C, A
- */
- @Test
- public void testDelayedDispatchAdvanceTimeTwice() {
- final int messageA = 1;
- final int messageB = 2;
- final int messageC = 3;
-
- InOrder inOrder = inOrder(mHandlerSpy);
- ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
-
- mHandlerSpy.sendMessage(mHandler.obtainMessage(messageA));
- mHandlerSpy.sendMessage(mHandler.obtainMessage(messageB));
- mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageC), 5000);
- mMockLooper.moveTimeForward(4000);
- mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageA), 1000);
- mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageB), 2000);
- mMockLooper.moveTimeForward(1000);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("1: messageA", messageA, equalTo(messageCaptor.getValue().what));
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("2: messageB", messageB, equalTo(messageCaptor.getValue().what));
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("3: messageC", messageC, equalTo(messageCaptor.getValue().what));
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("4: messageA", messageA, equalTo(messageCaptor.getValue().what));
-
- inOrder.verify(mHandlerSpy, never()).handleMessage(any(Message.class));
- }
-
- /**
- * Test message sequence: A, B, C@5K, Advance time by 4K, A@5K, B@2K Advance
- * time by 3K.
- * <p>
- * Expected: get A, B, C, B
- */
- @Test
- public void testDelayedDispatchReverseOrder() {
- final int messageA = 1;
- final int messageB = 2;
- final int messageC = 3;
-
- InOrder inOrder = inOrder(mHandlerSpy);
- ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
-
- mHandlerSpy.sendMessage(mHandler.obtainMessage(messageA));
- mHandlerSpy.sendMessage(mHandler.obtainMessage(messageB));
- mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageC), 5000);
- mMockLooper.moveTimeForward(4000);
- mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageA), 5000);
- mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageB), 2000);
- mMockLooper.moveTimeForward(3000);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("1: messageA", messageA, equalTo(messageCaptor.getValue().what));
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("2: messageB", messageB, equalTo(messageCaptor.getValue().what));
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("3: messageC", messageC, equalTo(messageCaptor.getValue().what));
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("4: messageB", messageB, equalTo(messageCaptor.getValue().what));
-
- inOrder.verify(mHandlerSpy, never()).handleMessage(any(Message.class));
- }
-
- /**
- * Test message sequence: A, B, C@5K, Advance time by 4K, dispatch all,
- * A@5K, B@2K Advance time by 3K, dispatch all.
- * <p>
- * Expected: get A, B after first dispatch; then C, B after second dispatch
- */
- @Test
- public void testDelayedDispatchAllMultipleTimes() {
- final int messageA = 1;
- final int messageB = 2;
- final int messageC = 3;
-
- InOrder inOrder = inOrder(mHandlerSpy);
- ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
-
- mHandlerSpy.sendMessage(mHandler.obtainMessage(messageA));
- mHandlerSpy.sendMessage(mHandler.obtainMessage(messageB));
- mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageC), 5000);
- mMockLooper.moveTimeForward(4000);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("1: messageA", messageA, equalTo(messageCaptor.getValue().what));
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("2: messageB", messageB, equalTo(messageCaptor.getValue().what));
-
- mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageA), 5000);
- mHandlerSpy.sendMessageDelayed(mHandler.obtainMessage(messageB), 2000);
- mMockLooper.moveTimeForward(3000);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("3: messageC", messageC, equalTo(messageCaptor.getValue().what));
- inOrder.verify(mHandlerSpy).handleMessage(messageCaptor.capture());
- collector.checkThat("4: messageB", messageB, equalTo(messageCaptor.getValue().what));
-
- inOrder.verify(mHandlerSpy, never()).handleMessage(any(Message.class));
- }
-
- /**
- * Test AutoDispatch for a single message.
- * This test would ideally use the Channel sendMessageSynchronously. At this time, the setup to
- * get a working test channel is cumbersome. Until this is fixed, we substitute with a
- * sendMessage followed by a blocking call. The main test thread blocks until the test handler
- * receives the test message (messageA) and sets a boolean true. Once the boolean is true, the
- * main thread will exit the busy wait loop, stop autoDispatch and check the assert.
- *
- * Enable AutoDispatch, add message, block on message being handled and stop AutoDispatch.
- * <p>
- * Expected: handleMessage is called for messageA and stopAutoDispatch is called.
- */
- @Test
- public void testAutoDispatchWithSingleMessage() {
- final int mLoopSleepTimeMs = 5;
-
- final int messageA = 1;
-
- MockLooper mockLooper = new MockLooper();
- class TestHandler extends Handler {
- public volatile boolean handledMessage = false;
- TestHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == messageA) {
- handledMessage = true;
- }
- }
- }
-
- TestHandler testHandler = new TestHandler(mockLooper.getLooper());
- mockLooper.startAutoDispatch();
- testHandler.sendMessage(testHandler.obtainMessage(messageA));
- while (!testHandler.handledMessage) {
- // Block until message is handled
- try {
- Thread.sleep(mLoopSleepTimeMs);
- } catch (InterruptedException e) {
- // Interrupted while sleeping.
- }
- }
- mockLooper.stopAutoDispatch();
- assertTrue("TestHandler should have received messageA", testHandler.handledMessage);
- }
-
- /**
- * Test starting AutoDispatch while already running throws IllegalStateException
- * Enable AutoDispatch two times in a row.
- * <p>
- * Expected: catch IllegalStateException on second call.
- */
- @Test(expected = IllegalStateException.class)
- public void testRepeatedStartAutoDispatchThrowsException() {
- mMockLooper.startAutoDispatch();
- mMockLooper.startAutoDispatch();
- }
-
- /**
- * Test stopping AutoDispatch without previously starting throws IllegalStateException
- * Stop AutoDispatch
- * <p>
- * Expected: catch IllegalStateException on second call.
- */
- @Test(expected = IllegalStateException.class)
- public void testStopAutoDispatchWithoutStartThrowsException() {
- mMockLooper.stopAutoDispatch();
- }
-
- /**
- * Test AutoDispatch exits and does not dispatch a later message.
- * Start and stop AutoDispatch then add a message.
- * <p>
- * Expected: After AutoDispatch is stopped, dispatchAll will return 1.
- */
- @Test
- public void testAutoDispatchStopsCleanlyWithoutDispatchingAMessage() {
- final int messageA = 1;
-
- InOrder inOrder = inOrder(mHandlerSpy);
- ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
-
- mMockLooper.startAutoDispatch();
- try {
- mMockLooper.stopAutoDispatch();
- } catch (IllegalStateException e) {
- // Stopping without a dispatch will throw an exception.
- }
-
- mHandlerSpy.sendMessage(mHandler.obtainMessage(messageA));
- assertEquals("One message should be dispatched", 1, mMockLooper.dispatchAll());
- }
-
- /**
- * Test AutoDispatch throws an exception when no messages are dispatched.
- * Start and stop AutoDispatch
- * <p>
- * Expected: Exception is thrown with the stopAutoDispatch call.
- */
- @Test(expected = IllegalStateException.class)
- public void testAutoDispatchThrowsExceptionWhenNoMessagesDispatched() {
- mMockLooper.startAutoDispatch();
- mMockLooper.stopAutoDispatch();
- }
-}
diff --git a/tests/wifitests/src/com/android/server/wifi/MockWifiMonitor.java b/tests/wifitests/src/com/android/server/wifi/MockWifiMonitor.java
index 8b68a11..6786504 100644
--- a/tests/wifitests/src/com/android/server/wifi/MockWifiMonitor.java
+++ b/tests/wifitests/src/com/android/server/wifi/MockWifiMonitor.java
@@ -23,12 +23,11 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
import android.os.Handler;
import android.os.Message;
import android.util.SparseArray;
-import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments;
-
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
diff --git a/tests/wifitests/src/com/android/server/wifi/RttServiceTest.java b/tests/wifitests/src/com/android/server/wifi/RttServiceTest.java
index 177705c..157164a 100644
--- a/tests/wifitests/src/com/android/server/wifi/RttServiceTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/RttServiceTest.java
@@ -36,8 +36,11 @@
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.Message;
+import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.internal.util.test.BidirectionalAsyncChannel;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -60,7 +63,7 @@
Context mContext;
@Mock
WifiNative mWifiNative;
- MockLooper mLooper;
+ TestLooper mLooper;
RttService.RttServiceImpl mRttServiceImpl;
ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor = ArgumentCaptor
@@ -70,7 +73,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
TestUtil.installWlanWifiNative(mWifiNative);
- mLooper = new MockLooper();
+ mLooper = new TestLooper();
mRttServiceImpl = new RttService.RttServiceImpl(mContext, mLooper.getLooper());
mRttServiceImpl.startService();
}
diff --git a/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java b/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java
index 8228118..ce77f8a 100644
--- a/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java
+++ b/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java
@@ -233,6 +233,18 @@
return createScanDatas(freqs, new int[freqs.length] /* defaults all 0 */);
}
+ private static void assertScanResultEquals(
+ String prefix, ScanResult expected, ScanResult actual) {
+ assertEquals(prefix + "SSID", expected.SSID, actual.SSID);
+ assertEquals(prefix + "wifiSsid", expected.wifiSsid.toString(), actual.wifiSsid.toString());
+ assertEquals(prefix + "BSSID", expected.BSSID, actual.BSSID);
+ assertEquals(prefix + "capabilities", expected.capabilities, actual.capabilities);
+ assertEquals(prefix + "level", expected.level, actual.level);
+ assertEquals(prefix + "frequency", expected.frequency, actual.frequency);
+ assertEquals(prefix + "timestamp", expected.timestamp, actual.timestamp);
+ assertEquals(prefix + "seen", expected.seen, actual.seen);
+ }
+
private static void assertScanResultsEquals(String prefix, ScanResult[] expected,
ScanResult[] actual) {
assertNotNull(prefix + "expected ScanResults was null", expected);
@@ -241,26 +253,18 @@
for (int j = 0; j < expected.length; ++j) {
ScanResult expectedResult = expected[j];
ScanResult actualResult = actual[j];
- assertEquals(prefix + "results[" + j + "].SSID",
- expectedResult.SSID, actualResult.SSID);
- assertEquals(prefix + "results[" + j + "].wifiSsid",
- expectedResult.wifiSsid.toString(), actualResult.wifiSsid.toString());
- assertEquals(prefix + "results[" + j + "].BSSID",
- expectedResult.BSSID, actualResult.BSSID);
- assertEquals(prefix + "results[" + j + "].capabilities",
- expectedResult.capabilities, actualResult.capabilities);
- assertEquals(prefix + "results[" + j + "].level",
- expectedResult.level, actualResult.level);
- assertEquals(prefix + "results[" + j + "].frequency",
- expectedResult.frequency, actualResult.frequency);
- assertEquals(prefix + "results[" + j + "].timestamp",
- expectedResult.timestamp, actualResult.timestamp);
- assertEquals(prefix + "results[" + j + "].seen",
- expectedResult.seen, actualResult.seen);
+ assertScanResultEquals(prefix + "results[" + j + "]", actualResult, expectedResult);
}
}
/**
+ * Asserts if the provided scan results are the same.
+ */
+ public static void assertScanResultEquals(ScanResult expected, ScanResult actual) {
+ assertScanResultEquals("", expected, actual);
+ }
+
+ /**
* Asserts if the provided scan result arrays are the same.
*/
public static void assertScanResultsEquals(ScanResult[] expected, ScanResult[] actual) {
diff --git a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
index b91df5a..5919156 100644
--- a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
@@ -24,14 +24,14 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.IntentFilter;
-import android.net.ConnectivityManager;
import android.net.InterfaceConfiguration;
+import android.net.wifi.IApInterface;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
import android.os.INetworkManagementService;
+import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
import org.junit.Before;
@@ -54,18 +54,19 @@
private static final String TEST_INTERFACE_NAME = "TestInterface";
private static final String TEST_COUNTRY_CODE = "TestCountry";
private static final Integer[] ALLOWED_2G_CHANNELS = {1, 2, 3, 4};
- private static final String[] AVAILABLE_DEVICES = { TEST_INTERFACE_NAME };
private final ArrayList<Integer> mAllowed2GChannels =
new ArrayList<Integer>(Arrays.asList(ALLOWED_2G_CHANNELS));
- MockLooper mLooper;
- @Mock Context mContext;
+ TestLooper mLooper;
@Mock WifiNative mWifiNative;
@Mock INetworkManagementService mNmService;
- @Mock ConnectivityManager mConnectivityManager;
@Mock SoftApManager.Listener mListener;
@Mock InterfaceConfiguration mInterfaceConfiguration;
+ @Mock IApInterface mApInterface;
+ @Mock IBinder mApInterfaceBinder;
+ final ArgumentCaptor<DeathRecipient> mDeathListenerCaptor =
+ ArgumentCaptor.forClass(DeathRecipient.class);
SoftApManager mSoftApManager;
@@ -73,20 +74,20 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mLooper = new MockLooper();
+ mLooper = new TestLooper();
when(mWifiNative.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
when(mNmService.getInterfaceConfig(TEST_INTERFACE_NAME))
.thenReturn(mInterfaceConfiguration);
- when(mConnectivityManager.getTetherableWifiRegexs())
- .thenReturn(AVAILABLE_DEVICES);
+ when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder);
mSoftApManager = new SoftApManager(mLooper.getLooper(),
mWifiNative,
mNmService,
TEST_COUNTRY_CODE,
mAllowed2GChannels,
- mListener);
+ mListener,
+ mApInterface);
mLooper.dispatchAll();
}
@@ -128,9 +129,21 @@
order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0);
}
+ @Test
+ public void handlesWificondInterfaceDeath() throws Exception {
+ startSoftApAndVerifyEnabled();
+
+ mDeathListenerCaptor.getValue().binderDied();
+ mLooper.dispatchAll();
+ InOrder order = inOrder(mListener);
+ order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0);
+ order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED,
+ WifiManager.SAP_START_FAILURE_GENERAL);
+ }
+
/** Starts soft AP and verifies that it is enabled successfully. */
protected void startSoftApAndVerifyEnabled() throws Exception {
- InOrder order = inOrder(mListener);
+ InOrder order = inOrder(mListener, mApInterfaceBinder);
/**
* Only test the default configuration. Testing for different configurations
@@ -146,6 +159,7 @@
verify(mNmService).startAccessPoint(
any(WifiConfiguration.class), eq(TEST_INTERFACE_NAME));
order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLING, 0);
+ order.verify(mApInterfaceBinder).linkToDeath(mDeathListenerCaptor.capture(), eq(0));
order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLED, 0);
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java
new file mode 100644
index 0000000..c17f2ae
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java
@@ -0,0 +1,654 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.net.wifi.WifiConfiguration;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.net.IpConfigStore;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.WifiBackupRestore}.
+ */
+@SmallTest
+public class WifiBackupRestoreTest {
+
+ private final WifiBackupRestore mWifiBackupRestore = new WifiBackupRestore();
+ private boolean mCheckDump = true;
+
+ @Before
+ public void setUp() throws Exception {
+ // Enable verbose logging before tests to check the backup data dumps.
+ mWifiBackupRestore.enableVerboseLogging(1);
+ }
+
+ @After
+ public void cleanUp() throws Exception {
+ if (mCheckDump) {
+ StringWriter stringWriter = new StringWriter();
+ mWifiBackupRestore.dump(
+ new FileDescriptor(), new PrintWriter(stringWriter), new String[0]);
+ String dumpString = stringWriter.toString();
+ // Ensure that the SSID was dumped out.
+ assertTrue("Dump: " + dumpString,
+ dumpString.contains(WifiConfigurationTestUtil.TEST_SSID));
+ // Ensure that the password wasn't dumped out.
+ assertFalse("Dump: " + dumpString,
+ dumpString.contains(WifiConfigurationTestUtil.TEST_PSK));
+ assertFalse("Dump: " + dumpString,
+ dumpString.contains(WifiConfigurationTestUtil.TEST_WEP_KEYS[0]));
+ assertFalse("Dump: " + dumpString,
+ dumpString.contains(WifiConfigurationTestUtil.TEST_WEP_KEYS[1]));
+ assertFalse("Dump: " + dumpString,
+ dumpString.contains(WifiConfigurationTestUtil.TEST_WEP_KEYS[2]));
+ assertFalse("Dump: " + dumpString,
+ dumpString.contains(WifiConfigurationTestUtil.TEST_WEP_KEYS[3]));
+ }
+ }
+
+ /**
+ * Verify that a single open network configuration is serialized & deserialized correctly.
+ */
+ @Test
+ public void testSingleOpenNetworkBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ configurations.add(WifiConfigurationTestUtil.createOpenNetwork());
+
+ byte[] backupData = mWifiBackupRestore.retrieveBackupDataFromConfigurations(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromBackupData(backupData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that a single open hidden network configuration is serialized & deserialized
+ * correctly.
+ */
+ @Test
+ public void testSingleOpenHiddenNetworkBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ configurations.add(WifiConfigurationTestUtil.createOpenHiddenNetwork());
+
+ byte[] backupData = mWifiBackupRestore.retrieveBackupDataFromConfigurations(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromBackupData(backupData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that a single PSK network configuration is serialized & deserialized correctly.
+ */
+ @Test
+ public void testSinglePskNetworkBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ configurations.add(WifiConfigurationTestUtil.createPskNetwork());
+
+ byte[] backupData = mWifiBackupRestore.retrieveBackupDataFromConfigurations(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromBackupData(backupData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that a single PSK hidden network configuration is serialized & deserialized correctly.
+ */
+ @Test
+ public void testSinglePskHiddenNetworkBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ configurations.add(WifiConfigurationTestUtil.createPskHiddenNetwork());
+
+ byte[] backupData = mWifiBackupRestore.retrieveBackupDataFromConfigurations(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromBackupData(backupData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that a single WEP network configuration is serialized & deserialized correctly.
+ */
+ @Test
+ public void testSingleWepNetworkBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ configurations.add(WifiConfigurationTestUtil.createWepNetwork());
+
+ byte[] backupData = mWifiBackupRestore.retrieveBackupDataFromConfigurations(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromBackupData(backupData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that a single WEP network configuration with only 1 key is serialized & deserialized
+ * correctly.
+ */
+ @Test
+ public void testSingleWepNetworkWithSingleKeyBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ configurations.add(WifiConfigurationTestUtil.createWepNetworkWithSingleKey());
+
+ byte[] backupData = mWifiBackupRestore.retrieveBackupDataFromConfigurations(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromBackupData(backupData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that a single enterprise network configuration is not serialized.
+ */
+ @Test
+ public void testSingleEnterpriseNetworkNotBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ configurations.add(WifiConfigurationTestUtil.createEapNetwork());
+
+ byte[] backupData = mWifiBackupRestore.retrieveBackupDataFromConfigurations(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromBackupData(backupData);
+ assertTrue(retrievedConfigurations.isEmpty());
+ // No valid data to check in dump.
+ mCheckDump = false;
+ }
+
+ /**
+ * Verify that a single PSK network configuration with static ip/proxy settings is serialized &
+ * deserialized correctly.
+ */
+ @Test
+ public void testSinglePskNetworkWithStaticIpAndStaticProxyBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ pskNetwork.setIpConfiguration(
+ WifiConfigurationTestUtil.createStaticIpConfigurationWithStaticProxy());
+ configurations.add(pskNetwork);
+
+ byte[] backupData = mWifiBackupRestore.retrieveBackupDataFromConfigurations(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromBackupData(backupData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that a single PSK network configuration with static ip & PAC proxy settings is
+ * serialized & deserialized correctly.
+ */
+ @Test
+ public void testSinglePskNetworkWithStaticIpAndPACProxyBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ pskNetwork.setIpConfiguration(
+ WifiConfigurationTestUtil.createStaticIpConfigurationWithPacProxy());
+ configurations.add(pskNetwork);
+
+ byte[] backupData = mWifiBackupRestore.retrieveBackupDataFromConfigurations(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromBackupData(backupData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that a single PSK network configuration with DHCP ip & PAC proxy settings is
+ * serialized & deserialized correctly.
+ */
+ @Test
+ public void testSinglePskNetworkWithDHCPIpAndPACProxyBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ pskNetwork.setIpConfiguration(
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy());
+ configurations.add(pskNetwork);
+
+ byte[] backupData = mWifiBackupRestore.retrieveBackupDataFromConfigurations(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromBackupData(backupData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that a single PSK network configuration with partial static ip settings is serialized
+ * & deserialized correctly.
+ */
+ @Test
+ public void testSinglePskNetworkWithPartialStaticIpBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ pskNetwork.setIpConfiguration(
+ WifiConfigurationTestUtil.createPartialStaticIpConfigurationWithPacProxy());
+ configurations.add(pskNetwork);
+
+ byte[] backupData = mWifiBackupRestore.retrieveBackupDataFromConfigurations(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromBackupData(backupData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that multiple networks of different types are serialized and deserialized correctly.
+ */
+ @Test
+ public void testMultipleNetworksAllBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ configurations.add(WifiConfigurationTestUtil.createWepNetwork());
+ configurations.add(WifiConfigurationTestUtil.createWepNetwork());
+ configurations.add(WifiConfigurationTestUtil.createPskNetwork());
+ configurations.add(WifiConfigurationTestUtil.createOpenNetwork());
+
+ byte[] backupData = mWifiBackupRestore.retrieveBackupDataFromConfigurations(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromBackupData(backupData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that multiple networks of different types except enterprise ones are serialized and
+ * deserialized correctly
+ */
+ @Test
+ public void testMultipleNetworksNonEnterpriseBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ List<WifiConfiguration> expectedConfigurations = new ArrayList<>();
+
+ WifiConfiguration wepNetwork = WifiConfigurationTestUtil.createWepNetwork();
+ configurations.add(wepNetwork);
+ expectedConfigurations.add(wepNetwork);
+
+ configurations.add(WifiConfigurationTestUtil.createEapNetwork());
+
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ configurations.add(pskNetwork);
+ expectedConfigurations.add(pskNetwork);
+
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ configurations.add(openNetwork);
+ expectedConfigurations.add(openNetwork);
+
+ byte[] backupData = mWifiBackupRestore.retrieveBackupDataFromConfigurations(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromBackupData(backupData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ expectedConfigurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that multiple networks with different credential types and IpConfiguration types are
+ * serialized and deserialized correctly.
+ */
+ @Test
+ public void testMultipleNetworksWithDifferentIpConfigurationsAllBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+
+ WifiConfiguration wepNetwork = WifiConfigurationTestUtil.createWepNetwork();
+ wepNetwork.setIpConfiguration(
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy());
+ configurations.add(wepNetwork);
+
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ pskNetwork.setIpConfiguration(
+ WifiConfigurationTestUtil.createStaticIpConfigurationWithPacProxy());
+ configurations.add(pskNetwork);
+
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ openNetwork.setIpConfiguration(
+ WifiConfigurationTestUtil.createStaticIpConfigurationWithStaticProxy());
+ configurations.add(openNetwork);
+
+ byte[] backupData = mWifiBackupRestore.retrieveBackupDataFromConfigurations(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromBackupData(backupData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that a single open network configuration is serialized & deserialized correctly from
+ * old backups.
+ */
+ @Test
+ public void testSingleOpenNetworkSupplicantBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ configurations.add(WifiConfigurationTestUtil.createOpenNetwork());
+
+ byte[] supplicantData = createWpaSupplicantConfBackupData(configurations);
+ byte[] ipConfigData = createIpConfBackupData(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromSupplicantBackupData(
+ supplicantData, ipConfigData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that a single open hidden network configuration is serialized & deserialized
+ * correctly from old backups.
+ */
+ @Test
+ public void testSingleOpenHiddenNetworkSupplicantBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ configurations.add(WifiConfigurationTestUtil.createOpenHiddenNetwork());
+
+ byte[] supplicantData = createWpaSupplicantConfBackupData(configurations);
+ byte[] ipConfigData = createIpConfBackupData(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromSupplicantBackupData(
+ supplicantData, ipConfigData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that a single PSK network configuration is serialized & deserialized correctly from
+ * old backups.
+ */
+ @Test
+ public void testSinglePskNetworkSupplicantBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ configurations.add(WifiConfigurationTestUtil.createPskNetwork());
+
+ byte[] supplicantData = createWpaSupplicantConfBackupData(configurations);
+ byte[] ipConfigData = createIpConfBackupData(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromSupplicantBackupData(
+ supplicantData, ipConfigData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that a single PSK hidden network configuration is serialized & deserialized correctly
+ * from old backups.
+ */
+ @Test
+ public void testSinglePskHiddenNetworkSupplicantBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ configurations.add(WifiConfigurationTestUtil.createPskHiddenNetwork());
+
+ byte[] supplicantData = createWpaSupplicantConfBackupData(configurations);
+ byte[] ipConfigData = createIpConfBackupData(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromSupplicantBackupData(
+ supplicantData, ipConfigData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that a single WEP network configuration is serialized & deserialized correctly from
+ * old backups.
+ */
+ @Test
+ public void testSingleWepNetworkSupplicantBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ configurations.add(WifiConfigurationTestUtil.createWepNetwork());
+
+ byte[] supplicantData = createWpaSupplicantConfBackupData(configurations);
+ byte[] ipConfigData = createIpConfBackupData(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromSupplicantBackupData(
+ supplicantData, ipConfigData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that a single WEP network configuration with only 1 key is serialized & deserialized
+ * correctly from old backups.
+ */
+ @Test
+ public void testSingleWepNetworkWithSingleKeySupplicantBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ configurations.add(WifiConfigurationTestUtil.createWepNetworkWithSingleKey());
+
+ byte[] supplicantData = createWpaSupplicantConfBackupData(configurations);
+ byte[] ipConfigData = createIpConfBackupData(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromSupplicantBackupData(
+ supplicantData, ipConfigData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that a single enterprise network configuration is not serialized from old backups.
+ */
+ @Test
+ public void testSingleEnterpriseNetworkNotSupplicantBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ configurations.add(WifiConfigurationTestUtil.createEapNetwork());
+
+ byte[] supplicantData = createWpaSupplicantConfBackupData(configurations);
+ byte[] ipConfigData = createIpConfBackupData(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromSupplicantBackupData(
+ supplicantData, ipConfigData);
+ assertTrue(retrievedConfigurations.isEmpty());
+ }
+
+ /**
+ * Verify that multiple networks with different credential types and IpConfiguration types are
+ * serialized and deserialized correctly from old backups
+ */
+ @Test
+ public void testMultipleNetworksWithDifferentIpConfigurationsAllSupplicantBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+
+ WifiConfiguration wepNetwork = WifiConfigurationTestUtil.createWepNetwork();
+ wepNetwork.setIpConfiguration(
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy());
+ configurations.add(wepNetwork);
+
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ pskNetwork.setIpConfiguration(
+ WifiConfigurationTestUtil.createStaticIpConfigurationWithPacProxy());
+ configurations.add(pskNetwork);
+
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ openNetwork.setIpConfiguration(
+ WifiConfigurationTestUtil.createStaticIpConfigurationWithStaticProxy());
+ configurations.add(openNetwork);
+
+ byte[] supplicantData = createWpaSupplicantConfBackupData(configurations);
+ byte[] ipConfigData = createIpConfBackupData(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromSupplicantBackupData(
+ supplicantData, ipConfigData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that a single open network configuration is serialized & deserialized correctly from
+ * old backups with no ipconfig data.
+ */
+ @Test
+ public void testSingleOpenNetworkSupplicantBackupRestoreWithNoIpConfigData() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ configurations.add(WifiConfigurationTestUtil.createOpenNetwork());
+
+ byte[] supplicantData = createWpaSupplicantConfBackupData(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromSupplicantBackupData(
+ supplicantData, null);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that multiple networks with different credential types are serialized and
+ * deserialized correctly from old backups with no ipconfig data.
+ */
+ @Test
+ public void testMultipleNetworksAllSupplicantBackupRestoreWithNoIpConfigData() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+
+ WifiConfiguration wepNetwork = WifiConfigurationTestUtil.createWepNetwork();
+ configurations.add(wepNetwork);
+
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ configurations.add(pskNetwork);
+
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ configurations.add(openNetwork);
+
+ byte[] supplicantData = createWpaSupplicantConfBackupData(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromSupplicantBackupData(
+ supplicantData, null);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ configurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verify that any corrupted data provided by Backup/Restore is ignored correctly.
+ */
+ @Test
+ public void testCorruptBackupRestore() {
+ Random random = new Random();
+ byte[] backupData = new byte[100];
+ random.nextBytes(backupData);
+
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromBackupData(backupData);
+ assertNull(retrievedConfigurations);
+ // No valid data to check in dump.
+ mCheckDump = false;
+ }
+
+ /**
+ * Helper method to write a list of networks in wpa_supplicant.conf format to the output stream.
+ */
+ private byte[] createWpaSupplicantConfBackupData(List<WifiConfiguration> configurations) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ OutputStreamWriter out = new OutputStreamWriter(bos);
+ try {
+ for (WifiConfiguration configuration : configurations) {
+ writeConfigurationToWpaSupplicantConf(out, configuration);
+ }
+ out.flush();
+ return bos.toByteArray();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Helper method to write a network in wpa_supplicant.conf format to the output stream.
+ * This was created using a sample wpa_supplicant.conf file. Using the raw key strings here
+ * (instead of consts in WifiBackupRestore).
+ */
+ private void writeConfigurationToWpaSupplicantConf(
+ OutputStreamWriter out, WifiConfiguration configuration)
+ throws IOException {
+ out.write("network={\n");
+ out.write(" " + "ssid=" + configuration.SSID + "\n");
+ String allowedKeyManagement = "";
+ if (configuration.hiddenSSID) {
+ out.write(" " + "scan_ssid=1" + "\n");
+ }
+ if (configuration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) {
+ allowedKeyManagement += "NONE";
+ }
+ if (configuration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
+ allowedKeyManagement += "WPA-PSK ";
+ }
+ if (configuration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)) {
+ allowedKeyManagement += "WPA-EAP ";
+ }
+ if (configuration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) {
+ allowedKeyManagement += "IEEE8021X ";
+ }
+ out.write(" " + "key_mgmt=" + allowedKeyManagement + "\n");
+ if (configuration.preSharedKey != null) {
+ out.write(" " + "psk=" + configuration.preSharedKey + "\n");
+ }
+ if (configuration.wepKeys[0] != null) {
+ out.write(" " + "wep_key0=" + configuration.wepKeys[0] + "\n");
+ }
+ if (configuration.wepKeys[1] != null) {
+ out.write(" " + "wep_key1=" + configuration.wepKeys[1] + "\n");
+ }
+ if (configuration.wepKeys[2] != null) {
+ out.write(" " + "wep_key2=" + configuration.wepKeys[2] + "\n");
+ }
+ if (configuration.wepKeys[3] != null) {
+ out.write(" " + "wep_key3=" + configuration.wepKeys[3] + "\n");
+ }
+ if (configuration.wepKeys[0] != null || configuration.wepKeys[1] != null
+ || configuration.wepKeys[2] != null || configuration.wepKeys[3] != null) {
+ out.write(" " + "wep_tx_keyidx=" + configuration.wepTxKeyIndex + "\n");
+ }
+ Map<String, String> extras = new HashMap<>();
+ extras.put(WifiSupplicantControl.ID_STRING_KEY_CONFIG_KEY, configuration.configKey());
+ extras.put(WifiSupplicantControl.ID_STRING_KEY_CREATOR_UID,
+ Integer.toString(configuration.creatorUid));
+ String idString = WifiNative.createNetworkExtra(extras);
+ if (idString != null) {
+ idString = "\"" + idString + "\"";
+ out.write(" " + "id_str=" + idString + "\n");
+ }
+ out.write("}\n");
+ out.write("\n");
+ }
+
+ /**
+ * Helper method to write a list of networks in ipconfig.txt format to the output stream.
+ */
+ private byte[] createIpConfBackupData(List<WifiConfiguration> configurations) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ DataOutputStream out = new DataOutputStream(bos);
+ try {
+ // write version first.
+ out.writeInt(2);
+ for (WifiConfiguration configuration : configurations) {
+ IpConfigStore.writeConfig(out, configuration.configKey().hashCode(),
+ configuration.getIpConfiguration());
+ }
+ out.flush();
+ return bos.toByteArray();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerNewTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerNewTest.java
new file mode 100644
index 0000000..2ff6de2
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerNewTest.java
@@ -0,0 +1,1361 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.IpConfiguration;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiSsid;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.WifiConfigManagerNew}.
+ */
+@SmallTest
+public class WifiConfigManagerNewTest {
+
+ private static final String TEST_BSSID = "0a:08:5c:67:89:00";
+ private static final long TEST_WALLCLOCK_CREATION_TIME_MILLIS = 9845637;
+ private static final long TEST_WALLCLOCK_UPDATE_TIME_MILLIS = 75455637;
+ private static final long TEST_ELAPSED_UPDATE_NETWORK_SELECTION_TIME_MILLIS = 29457631;
+ private static final int TEST_CREATOR_UID = 5;
+ private static final int TEST_UPDATE_UID = 4;
+ private static final String TEST_CREATOR_NAME = "com.wificonfigmanagerNew.creator";
+ private static final String TEST_UPDATE_NAME = "com.wificonfigmanagerNew.update";
+
+ @Mock private Context mContext;
+ @Mock private FrameworkFacade mFrameworkFacade;
+ @Mock private Clock mClock;
+ @Mock private UserManager mUserManager;
+ @Mock private WifiKeyStore mWifiKeyStore;
+ @Mock private WifiConfigStoreNew mWifiConfigStore;
+ @Mock private PackageManager mPackageManager;
+
+ private InOrder mContextConfigStoreMockOrder;
+
+ private WifiConfigManagerNew mWifiConfigManager;
+
+ /**
+ * Setup the mocks and an instance of WifiConfigManagerNew before each test.
+ */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ // Set up the inorder for verifications. This is needed to verify that the broadcasts,
+ // store writes for network updates followed by network additions are in the expected order.
+ mContextConfigStoreMockOrder = inOrder(mContext, mWifiConfigStore);
+
+ // Set up the package name stuff & permission override.
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ doAnswer(new AnswerWithArguments() {
+ public String answer(int uid) throws Exception {
+ if (uid == TEST_CREATOR_UID) {
+ return TEST_CREATOR_NAME;
+ } else if (uid == TEST_UPDATE_UID) {
+ return TEST_UPDATE_NAME;
+ }
+ fail("Unexpected UID: " + uid);
+ return "";
+ }
+ }).when(mPackageManager).getNameForUid(anyInt());
+
+ // Both the UID's in the test have the configuration override permission granted by
+ // default. This maybe modified for particular tests if needed.
+ doAnswer(new AnswerWithArguments() {
+ public int answer(String permName, int uid) throws Exception {
+ if (uid == TEST_CREATOR_UID || uid == TEST_UPDATE_UID) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ return PackageManager.PERMISSION_DENIED;
+ }
+ }).when(mFrameworkFacade).checkUidPermission(anyString(), anyInt());
+
+ when(mWifiKeyStore
+ .updateNetworkKeys(any(WifiConfiguration.class), any(WifiConfiguration.class)))
+ .thenReturn(true);
+
+ mWifiConfigManager =
+ new WifiConfigManagerNew(
+ mContext, mFrameworkFacade, mClock, mUserManager, mWifiKeyStore,
+ mWifiConfigStore);
+ mWifiConfigManager.enableVerboseLogging(1);
+ }
+
+ /**
+ * Verifies the addition of a single network using
+ * {@link WifiConfigManagerNew#addOrUpdateNetwork(WifiConfiguration, int)}
+ */
+ @Test
+ public void testAddSingleOpenNetwork() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ List<WifiConfiguration> networks = new ArrayList<>();
+ networks.add(openNetwork);
+
+ verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworksWithPasswords();
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ networks, retrievedNetworks);
+ }
+
+ /**
+ * Verifies the modification of a single network using
+ * {@link WifiConfigManagerNew#addOrUpdateNetwork(WifiConfiguration, int)}
+ */
+ @Test
+ public void testUpdateSingleOpenNetwork() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ List<WifiConfiguration> networks = new ArrayList<>();
+ networks.add(openNetwork);
+
+ verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ // Now change BSSID for the network.
+ assertAndSetNetworkBSSID(openNetwork, TEST_BSSID);
+ verifyUpdateNetworkToWifiConfigManagerWithoutIpChange(openNetwork);
+
+ // Now verify that the modification has been effective.
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworksWithPasswords();
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ networks, retrievedNetworks);
+ }
+
+ /**
+ * Verifies the addition of a single ephemeral network using
+ * {@link WifiConfigManagerNew#addOrUpdateNetwork(WifiConfiguration, int)} and verifies that
+ * the {@link WifiConfigManagerNew#getSavedNetworks()} does not return this network.
+ */
+ @Test
+ public void testAddSingleEphemeralNetwork() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ openNetwork.ephemeral = true;
+ List<WifiConfiguration> networks = new ArrayList<>();
+ networks.add(openNetwork);
+
+ verifyAddEphemeralNetworkToWifiConfigManager(openNetwork);
+
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworksWithPasswords();
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ networks, retrievedNetworks);
+
+ // Ensure that this is not returned in the saved network list.
+ assertTrue(mWifiConfigManager.getSavedNetworks().isEmpty());
+ }
+
+ /**
+ * Verifies that the modification of a single open network using
+ * {@link WifiConfigManagerNew#addOrUpdateNetwork(WifiConfiguration, int)} with a UID which
+ * has no permission to modify the network fails.
+ */
+ @Test
+ public void testUpdateSingleOpenNetworkFailedDueToPermissionDenied() throws Exception {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ List<WifiConfiguration> networks = new ArrayList<>();
+ networks.add(openNetwork);
+
+ verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ // Now change BSSID of the network.
+ assertAndSetNetworkBSSID(openNetwork, TEST_BSSID);
+
+ // Deny permission for |UPDATE_UID|.
+ doAnswer(new AnswerWithArguments() {
+ public int answer(String permName, int uid) throws Exception {
+ if (uid == TEST_CREATOR_UID) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ return PackageManager.PERMISSION_DENIED;
+ }
+ }).when(mFrameworkFacade).checkUidPermission(anyString(), anyInt());
+
+ // Update the same configuration and ensure that the operation failed.
+ NetworkUpdateResult result = updateNetworkToWifiConfigManager(openNetwork);
+ assertTrue(result.getNetworkId() == WifiConfiguration.INVALID_NETWORK_ID);
+ }
+
+ /**
+ * Verifies that the modification of a single open network using
+ * {@link WifiConfigManagerNew#addOrUpdateNetwork(WifiConfiguration, int)} with the creator UID
+ * should always succeed.
+ */
+ @Test
+ public void testUpdateSingleOpenNetworkSuccessWithCreatorUID() throws Exception {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ List<WifiConfiguration> networks = new ArrayList<>();
+ networks.add(openNetwork);
+
+ verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ // Now change BSSID of the network.
+ assertAndSetNetworkBSSID(openNetwork, TEST_BSSID);
+
+ // Deny permission for all UIDs.
+ doAnswer(new AnswerWithArguments() {
+ public int answer(String permName, int uid) throws Exception {
+ return PackageManager.PERMISSION_DENIED;
+ }
+ }).when(mFrameworkFacade).checkUidPermission(anyString(), anyInt());
+
+ // Update the same configuration using the creator UID.
+ NetworkUpdateResult result =
+ mWifiConfigManager.addOrUpdateNetwork(openNetwork, TEST_CREATOR_UID);
+ assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
+
+ // Now verify that the modification has been effective.
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworksWithPasswords();
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ networks, retrievedNetworks);
+ }
+
+ /**
+ * Verifies the addition of a single PSK network using
+ * {@link WifiConfigManagerNew#addOrUpdateNetwork(WifiConfiguration, int)} and verifies that
+ * {@link WifiConfigManagerNew#getSavedNetworks()} masks the password.
+ */
+ @Test
+ public void testAddSinglePskNetwork() {
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ List<WifiConfiguration> networks = new ArrayList<>();
+ networks.add(pskNetwork);
+
+ verifyAddNetworkToWifiConfigManager(pskNetwork);
+
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworksWithPasswords();
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ networks, retrievedNetworks);
+
+ List<WifiConfiguration> retrievedSavedNetworks = mWifiConfigManager.getSavedNetworks();
+ assertEquals(retrievedSavedNetworks.size(), 1);
+ assertEquals(retrievedSavedNetworks.get(0).configKey(), pskNetwork.configKey());
+ assertPasswordsMaskedInWifiConfiguration(retrievedSavedNetworks.get(0));
+ }
+
+ /**
+ * Verifies the addition of a single WEP network using
+ * {@link WifiConfigManagerNew#addOrUpdateNetwork(WifiConfiguration, int)} and verifies that
+ * {@link WifiConfigManagerNew#getSavedNetworks()} masks the password.
+ */
+ @Test
+ public void testAddSingleWepNetwork() {
+ WifiConfiguration wepNetwork = WifiConfigurationTestUtil.createWepNetwork();
+ List<WifiConfiguration> networks = new ArrayList<>();
+ networks.add(wepNetwork);
+
+ verifyAddNetworkToWifiConfigManager(wepNetwork);
+
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworksWithPasswords();
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ networks, retrievedNetworks);
+
+ List<WifiConfiguration> retrievedSavedNetworks = mWifiConfigManager.getSavedNetworks();
+ assertEquals(retrievedSavedNetworks.size(), 1);
+ assertEquals(retrievedSavedNetworks.get(0).configKey(), wepNetwork.configKey());
+ assertPasswordsMaskedInWifiConfiguration(retrievedSavedNetworks.get(0));
+ }
+
+ /**
+ * Verifies the modification of an IpConfiguration using
+ * {@link WifiConfigManagerNew#addOrUpdateNetwork(WifiConfiguration, int)}
+ */
+ @Test
+ public void testUpdateIpConfiguration() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ List<WifiConfiguration> networks = new ArrayList<>();
+ networks.add(openNetwork);
+
+ verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ // Now change BSSID of the network.
+ assertAndSetNetworkBSSID(openNetwork, TEST_BSSID);
+
+ // Update the same configuration and ensure that the IP configuration change flags
+ // are not set.
+ verifyUpdateNetworkToWifiConfigManagerWithoutIpChange(openNetwork);
+
+ // Change the IpConfiguration now and ensure that the IP configuration flags are set now.
+ assertAndSetNetworkIpConfiguration(
+ openNetwork,
+ WifiConfigurationTestUtil.createStaticIpConfigurationWithStaticProxy());
+ verifyUpdateNetworkToWifiConfigManagerWithIpChange(openNetwork);
+
+ // Now verify that all the modifications have been effective.
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworksWithPasswords();
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ networks, retrievedNetworks);
+ }
+
+ /**
+ * Verifies the removal of a single network using
+ * {@link WifiConfigManagerNew#removeNetwork(int)}
+ */
+ @Test
+ public void testRemoveSingleOpenNetwork() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ List<WifiConfiguration> networks = new ArrayList<>();
+ networks.add(openNetwork);
+
+ verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ verifyRemoveNetworkFromWifiConfigManager(openNetwork);
+
+ // Ensure that configured network list is empty now.
+ assertTrue(mWifiConfigManager.getConfiguredNetworks().isEmpty());
+ }
+
+ /**
+ * Verifies the addition & update of multiple networks using
+ * {@link WifiConfigManagerNew#addOrUpdateNetwork(WifiConfiguration, int)} and the
+ * removal of networks using
+ * {@link WifiConfigManagerNew#removeNetwork(int)}
+ */
+ @Test
+ public void testAddUpdateRemoveMultipleNetworks() {
+ List<WifiConfiguration> networks = new ArrayList<>();
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ WifiConfiguration wepNetwork = WifiConfigurationTestUtil.createWepNetwork();
+ networks.add(openNetwork);
+ networks.add(pskNetwork);
+ networks.add(wepNetwork);
+
+ verifyAddNetworkToWifiConfigManager(openNetwork);
+ verifyAddNetworkToWifiConfigManager(pskNetwork);
+ verifyAddNetworkToWifiConfigManager(wepNetwork);
+
+ // Now verify that all the additions has been effective.
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworksWithPasswords();
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ networks, retrievedNetworks);
+
+ // Modify all the 3 configurations and update it to WifiConfigManager.
+ assertAndSetNetworkBSSID(openNetwork, TEST_BSSID);
+ assertAndSetNetworkBSSID(pskNetwork, TEST_BSSID);
+ assertAndSetNetworkIpConfiguration(
+ wepNetwork,
+ WifiConfigurationTestUtil.createStaticIpConfigurationWithPacProxy());
+
+ verifyUpdateNetworkToWifiConfigManagerWithoutIpChange(openNetwork);
+ verifyUpdateNetworkToWifiConfigManagerWithoutIpChange(pskNetwork);
+ verifyUpdateNetworkToWifiConfigManagerWithIpChange(wepNetwork);
+ // Now verify that all the modifications has been effective.
+ retrievedNetworks = mWifiConfigManager.getConfiguredNetworksWithPasswords();
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ networks, retrievedNetworks);
+
+ // Now remove all 3 networks.
+ verifyRemoveNetworkFromWifiConfigManager(openNetwork);
+ verifyRemoveNetworkFromWifiConfigManager(pskNetwork);
+ verifyRemoveNetworkFromWifiConfigManager(wepNetwork);
+
+ // Ensure that configured network list is empty now.
+ assertTrue(mWifiConfigManager.getConfiguredNetworks().isEmpty());
+ }
+
+ /**
+ * Verifies the update of network status using
+ * {@link WifiConfigManagerNew#updateNetworkSelectionStatus(int, int)}.
+ */
+ @Test
+ public void testNetworkSelectionStatus() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+
+ NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ // First set it to enabled.
+ verifyUpdateNetworkSelectionStatus(
+ result.getNetworkId(), NetworkSelectionStatus.NETWORK_SELECTION_ENABLE, 0);
+
+ // Now set it to temporarily disabled. The threshold for association rejection is 5, so
+ // disable it 5 times to actually mark it temporarily disabled.
+ int assocRejectReason = NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION;
+ int assocRejectThreshold =
+ WifiConfigManagerNew.NETWORK_SELECTION_DISABLE_THRESHOLD[assocRejectReason];
+ for (int i = 1; i <= assocRejectThreshold; i++) {
+ verifyUpdateNetworkSelectionStatus(result.getNetworkId(), assocRejectReason, i);
+ }
+
+ // Now set it to permanently disabled.
+ verifyUpdateNetworkSelectionStatus(
+ result.getNetworkId(), NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER, 0);
+
+ // Now set it back to enabled.
+ verifyUpdateNetworkSelectionStatus(
+ result.getNetworkId(), NetworkSelectionStatus.NETWORK_SELECTION_ENABLE, 0);
+ }
+
+ /**
+ * Verifies the update of network status using
+ * {@link WifiConfigManagerNew#updateNetworkSelectionStatus(int, int)} and ensures that
+ * enabling a network clears out all the temporary disable counters.
+ */
+ @Test
+ public void testNetworkSelectionStatusEnableClearsDisableCounters() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+
+ NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ // First set it to enabled.
+ verifyUpdateNetworkSelectionStatus(
+ result.getNetworkId(), NetworkSelectionStatus.NETWORK_SELECTION_ENABLE, 0);
+
+ // Now set it to temporarily disabled 2 times for 2 different reasons.
+ verifyUpdateNetworkSelectionStatus(
+ result.getNetworkId(), NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION, 1);
+ verifyUpdateNetworkSelectionStatus(
+ result.getNetworkId(), NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION, 2);
+ verifyUpdateNetworkSelectionStatus(
+ result.getNetworkId(), NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE, 1);
+ verifyUpdateNetworkSelectionStatus(
+ result.getNetworkId(), NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE, 2);
+
+ // Now set it back to enabled.
+ verifyUpdateNetworkSelectionStatus(
+ result.getNetworkId(), NetworkSelectionStatus.NETWORK_SELECTION_ENABLE, 0);
+
+ // Ensure that the counters have all been reset now.
+ verifyUpdateNetworkSelectionStatus(
+ result.getNetworkId(), NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION, 1);
+ verifyUpdateNetworkSelectionStatus(
+ result.getNetworkId(), NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE, 1);
+ }
+
+ /**
+ * Verifies the enabling of temporarily disabled network using
+ * {@link WifiConfigManagerNew#tryEnableNetwork(int)}.
+ */
+ @Test
+ public void testTryEnableNetwork() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+
+ NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ // First set it to enabled.
+ verifyUpdateNetworkSelectionStatus(
+ result.getNetworkId(), NetworkSelectionStatus.NETWORK_SELECTION_ENABLE, 0);
+
+ // Now set it to temporarily disabled. The threshold for association rejection is 5, so
+ // disable it 5 times to actually mark it temporarily disabled.
+ int assocRejectReason = NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION;
+ int assocRejectThreshold =
+ WifiConfigManagerNew.NETWORK_SELECTION_DISABLE_THRESHOLD[assocRejectReason];
+ for (int i = 1; i <= assocRejectThreshold; i++) {
+ verifyUpdateNetworkSelectionStatus(result.getNetworkId(), assocRejectReason, i);
+ }
+
+ // Now let's try enabling this network without changing the time, this should fail and the
+ // status remains temporarily disabled.
+ assertFalse(mWifiConfigManager.tryEnableNetwork(result.getNetworkId()));
+ NetworkSelectionStatus retrievedStatus =
+ mWifiConfigManager.getConfiguredNetwork(result.getNetworkId())
+ .getNetworkSelectionStatus();
+ assertTrue(retrievedStatus.isNetworkTemporaryDisabled());
+
+ // Now advance time by the timeout for association rejection and ensure that the network
+ // is now enabled.
+ int assocRejectTimeout =
+ WifiConfigManagerNew.NETWORK_SELECTION_DISABLE_TIMEOUT_MS[assocRejectReason];
+ when(mClock.getElapsedSinceBootMillis())
+ .thenReturn(TEST_ELAPSED_UPDATE_NETWORK_SELECTION_TIME_MILLIS + assocRejectTimeout);
+
+ assertTrue(mWifiConfigManager.tryEnableNetwork(result.getNetworkId()));
+ retrievedStatus =
+ mWifiConfigManager.getConfiguredNetwork(result.getNetworkId())
+ .getNetworkSelectionStatus();
+ assertTrue(retrievedStatus.isNetworkEnabled());
+ }
+
+ /**
+ * Verifies the enabling of network using
+ * {@link WifiConfigManagerNew#enableNetwork(int, int)} and
+ * {@link WifiConfigManagerNew#disableNetwork(int, int)}.
+ */
+ @Test
+ public void testEnableDisableNetwork() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+
+ NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ assertTrue(mWifiConfigManager.enableNetwork(result.getNetworkId(), TEST_CREATOR_UID));
+ WifiConfiguration retrievedNetwork =
+ mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
+ NetworkSelectionStatus retrievedStatus = retrievedNetwork.getNetworkSelectionStatus();
+ assertTrue(retrievedStatus.isNetworkEnabled());
+ verifyUpdateNetworkStatus(retrievedNetwork, WifiConfiguration.Status.ENABLED);
+
+ // Now set it disabled.
+ assertTrue(mWifiConfigManager.disableNetwork(result.getNetworkId(), TEST_CREATOR_UID));
+ retrievedNetwork = mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
+ retrievedStatus = retrievedNetwork.getNetworkSelectionStatus();
+ assertTrue(retrievedStatus.isNetworkPermanentlyDisabled());
+ verifyUpdateNetworkStatus(retrievedNetwork, WifiConfiguration.Status.DISABLED);
+ }
+
+ /**
+ * Verifies the enabling of network using
+ * {@link WifiConfigManagerNew#enableNetwork(int, int)} with a UID which
+ * has no permission to modify the network fails..
+ */
+ @Test
+ public void testEnableDisableNetworkFailedDueToPermissionDenied() throws Exception {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+
+ NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ assertTrue(mWifiConfigManager.enableNetwork(result.getNetworkId(), TEST_CREATOR_UID));
+ WifiConfiguration retrievedNetwork =
+ mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
+ NetworkSelectionStatus retrievedStatus = retrievedNetwork.getNetworkSelectionStatus();
+ assertTrue(retrievedStatus.isNetworkEnabled());
+ verifyUpdateNetworkStatus(retrievedNetwork, WifiConfiguration.Status.ENABLED);
+
+ // Deny permission for |UPDATE_UID|.
+ doAnswer(new AnswerWithArguments() {
+ public int answer(String permName, int uid) throws Exception {
+ if (uid == TEST_CREATOR_UID) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ return PackageManager.PERMISSION_DENIED;
+ }
+ }).when(mFrameworkFacade).checkUidPermission(anyString(), anyInt());
+
+ // Now try to set it disabled with |TEST_UPDATE_UID|, it should fail and the network
+ // should remain enabled.
+ assertFalse(mWifiConfigManager.disableNetwork(result.getNetworkId(), TEST_UPDATE_UID));
+ retrievedStatus =
+ mWifiConfigManager.getConfiguredNetwork(result.getNetworkId())
+ .getNetworkSelectionStatus();
+ assertTrue(retrievedStatus.isNetworkEnabled());
+ assertEquals(WifiConfiguration.Status.ENABLED, retrievedNetwork.status);
+ }
+
+ /**
+ * Verifies the updation of network's connectUid using
+ * {@link WifiConfigManagerNew#checkAndUpdateLastConnectUid(int, int)}.
+ */
+ @Test
+ public void testUpdateLastConnectUid() throws Exception {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+
+ NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ assertTrue(
+ mWifiConfigManager.checkAndUpdateLastConnectUid(result.getNetworkId(), TEST_CREATOR_UID));
+ WifiConfiguration retrievedNetwork =
+ mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
+ assertEquals(TEST_CREATOR_UID, retrievedNetwork.lastConnectUid);
+
+ // Deny permission for |UPDATE_UID|.
+ doAnswer(new AnswerWithArguments() {
+ public int answer(String permName, int uid) throws Exception {
+ if (uid == TEST_CREATOR_UID) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ return PackageManager.PERMISSION_DENIED;
+ }
+ }).when(mFrameworkFacade).checkUidPermission(anyString(), anyInt());
+
+ // Now try to update the last connect UID with |TEST_UPDATE_UID|, it should fail and
+ // the lastConnectUid should remain the same.
+ assertFalse(
+ mWifiConfigManager.checkAndUpdateLastConnectUid(
+ result.getNetworkId(), TEST_UPDATE_UID));
+ retrievedNetwork = mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
+ assertEquals(TEST_CREATOR_UID, retrievedNetwork.lastConnectUid);
+ }
+
+ /**
+ * Verifies that any configuration update attempt with an null config is gracefully
+ * handled.
+ * This invokes {@link WifiConfigManagerNew#addOrUpdateNetwork(WifiConfiguration, int)}.
+ */
+ @Test
+ public void testAddOrUpdateNetworkWithNullConfig() {
+ NetworkUpdateResult result = mWifiConfigManager.addOrUpdateNetwork(null, TEST_CREATOR_UID);
+ assertFalse(result.isSuccess());
+ }
+
+ /**
+ * Verifies that any configuration removal attempt with an invalid networkID is gracefully
+ * handled.
+ * This invokes {@link WifiConfigManagerNew#removeNetwork(int)}.
+ */
+ @Test
+ public void testRemoveNetworkWithInvalidNetworkId() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+
+ verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ // Change the networkID to an invalid one.
+ openNetwork.networkId++;
+ assertFalse(mWifiConfigManager.removeNetwork(openNetwork.networkId));
+ }
+
+ /**
+ * Verifies that any configuration update attempt with an invalid networkID is gracefully
+ * handled.
+ * This invokes {@link WifiConfigManagerNew#enableNetwork(int, int)},
+ * {@link WifiConfigManagerNew#disableNetwork(int, int)},
+ * {@link WifiConfigManagerNew#updateNetworkSelectionStatus(int, int)} and
+ * {@link WifiConfigManagerNew#checkAndUpdateLastConnectUid(int, int)}.
+ */
+ @Test
+ public void testChangeConfigurationWithInvalidNetworkId() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+
+ NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ assertFalse(mWifiConfigManager.enableNetwork(result.getNetworkId() + 1, TEST_CREATOR_UID));
+ assertFalse(mWifiConfigManager.disableNetwork(result.getNetworkId() + 1, TEST_CREATOR_UID));
+ assertFalse(mWifiConfigManager.updateNetworkSelectionStatus(
+ result.getNetworkId() + 1, NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER));
+ assertFalse(mWifiConfigManager.checkAndUpdateLastConnectUid(
+ result.getNetworkId() + 1, TEST_CREATOR_UID));
+ }
+
+ /**
+ * Verifies multiple modification of a single network using
+ * {@link WifiConfigManagerNew#addOrUpdateNetwork(WifiConfiguration, int)}.
+ * This test is basically checking if the apps can reset some of the fields of the config after
+ * addition. The fields being reset in this test are the |preSharedKey| and |wepKeys|.
+ * 1. Create an open network initially.
+ * 2. Modify the added network config to a WEP network config with all the 4 keys set.
+ * 3. Modify the added network config to a WEP network config with only 1 key set.
+ * 4. Modify the added network config to a PSK network config.
+ */
+ @Test
+ public void testMultipleUpdatesSingleNetwork() {
+ WifiConfiguration network = WifiConfigurationTestUtil.createOpenNetwork();
+ verifyAddNetworkToWifiConfigManager(network);
+
+ // Now add |wepKeys| to the network. We don't need to update the |allowedKeyManagement|
+ // fields for open to WEP conversion.
+ String[] wepKeys =
+ Arrays.copyOf(WifiConfigurationTestUtil.TEST_WEP_KEYS,
+ WifiConfigurationTestUtil.TEST_WEP_KEYS.length);
+ int wepTxKeyIdx = WifiConfigurationTestUtil.TEST_WEP_TX_KEY_INDEX;
+ assertAndSetNetworkWepKeysAndTxIndex(network, wepKeys, wepTxKeyIdx);
+
+ verifyUpdateNetworkToWifiConfigManagerWithoutIpChange(network);
+ WifiConfigurationTestUtil.assertConfigurationEqualForConfigManagerAddOrUpdate(
+ network, mWifiConfigManager.getConfiguredNetworkWithPassword(network.networkId));
+
+ // Now empty out 3 of the |wepKeys[]| and ensure that those keys have been reset correctly.
+ for (int i = 1; i < network.wepKeys.length; i++) {
+ wepKeys[i] = "";
+ }
+ wepTxKeyIdx = 0;
+ assertAndSetNetworkWepKeysAndTxIndex(network, wepKeys, wepTxKeyIdx);
+
+ verifyUpdateNetworkToWifiConfigManagerWithoutIpChange(network);
+ WifiConfigurationTestUtil.assertConfigurationEqualForConfigManagerAddOrUpdate(
+ network, mWifiConfigManager.getConfiguredNetworkWithPassword(network.networkId));
+
+ // Now change the config to a PSK network config by resetting the remaining |wepKey[0]|
+ // field and setting the |preSharedKey| and |allowedKeyManagement| fields.
+ wepKeys[0] = "";
+ wepTxKeyIdx = -1;
+ assertAndSetNetworkWepKeysAndTxIndex(network, wepKeys, wepTxKeyIdx);
+ network.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ assertAndSetNetworkPreSharedKey(network, WifiConfigurationTestUtil.TEST_PSK);
+
+ verifyUpdateNetworkToWifiConfigManagerWithoutIpChange(network);
+ WifiConfigurationTestUtil.assertConfigurationEqualForConfigManagerAddOrUpdate(
+ network, mWifiConfigManager.getConfiguredNetworkWithPassword(network.networkId));
+ }
+
+ /**
+ * Verifies the modification of a WifiEnteriseConfig using
+ * {@link WifiConfigManagerNew#addOrUpdateNetwork(WifiConfiguration, int)}.
+ */
+ @Test
+ public void testUpdateWifiEnterpriseConfig() {
+ WifiConfiguration network = WifiConfigurationTestUtil.createEapNetwork();
+ verifyAddNetworkToWifiConfigManager(network);
+
+ // Set the |password| field in WifiEnterpriseConfig and modify the config to PEAP/GTC.
+ network.enterpriseConfig =
+ WifiConfigurationTestUtil.createPEAPWifiEnterpriseConfigWithGTCPhase2();
+ assertAndSetNetworkEnterprisePassword(network, "test");
+
+ verifyUpdateNetworkToWifiConfigManagerWithoutIpChange(network);
+ WifiConfigurationTestUtil.assertConfigurationEqualForConfigManagerAddOrUpdate(
+ network, mWifiConfigManager.getConfiguredNetworkWithPassword(network.networkId));
+
+ // Reset the |password| field in WifiEnterpriseConfig and modify the config to TLS/None.
+ network.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ network.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.NONE);
+ assertAndSetNetworkEnterprisePassword(network, "");
+
+ verifyUpdateNetworkToWifiConfigManagerWithoutIpChange(network);
+ WifiConfigurationTestUtil.assertConfigurationEqualForConfigManagerAddOrUpdate(
+ network, mWifiConfigManager.getConfiguredNetworkWithPassword(network.networkId));
+ }
+
+ /**
+ * Verifies the modification of a single network using
+ * {@link WifiConfigManagerNew#addOrUpdateNetwork(WifiConfiguration, int)} by passing in nulls
+ * in all the publicly exposed fields.
+ */
+ @Test
+ public void testUpdateSingleNetworkWithNullValues() {
+ WifiConfiguration network = WifiConfigurationTestUtil.createEapNetwork();
+ verifyAddNetworkToWifiConfigManager(network);
+
+ // Save a copy of the original network for comparison.
+ WifiConfiguration originalNetwork = new WifiConfiguration(network);
+
+ // Now set all the public fields to null and try updating the network.
+ network.allowedAuthAlgorithms = null;
+ network.allowedProtocols = null;
+ network.allowedKeyManagement = null;
+ network.allowedPairwiseCiphers = null;
+ network.allowedGroupCiphers = null;
+ network.setIpConfiguration(null);
+ network.enterpriseConfig = null;
+
+ verifyUpdateNetworkToWifiConfigManagerWithoutIpChange(network);
+
+ // Copy over the updated debug params to the original network config before comparison.
+ originalNetwork.lastUpdateUid = network.lastUpdateUid;
+ originalNetwork.lastUpdateName = network.lastUpdateName;
+ originalNetwork.updateTime = network.updateTime;
+
+ // Now verify that there was no change to the network configurations.
+ WifiConfigurationTestUtil.assertConfigurationEqualForConfigManagerAddOrUpdate(
+ originalNetwork,
+ mWifiConfigManager.getConfiguredNetworkWithPassword(originalNetwork.networkId));
+ }
+
+ /**
+ * Verifies the matching of networks with different encryption types with the
+ * corresponding scan detail using
+ * {@link WifiConfigManagerNew#getSavedNetworkForScanDetailAndCache(ScanDetail)}.
+ * The test also verifies that the provided scan detail was cached,
+ */
+ @Test
+ public void testMatchScanDetailToNetworksAndCache() {
+ // Create networks of different types and ensure that they're all matched using
+ // the corresponding ScanDetail correctly.
+ verifyAddSingleNetworkAndMatchScanDetailToNetworkAndCache(
+ WifiConfigurationTestUtil.createOpenNetwork());
+ verifyAddSingleNetworkAndMatchScanDetailToNetworkAndCache(
+ WifiConfigurationTestUtil.createWepNetwork());
+ verifyAddSingleNetworkAndMatchScanDetailToNetworkAndCache(
+ WifiConfigurationTestUtil.createPskNetwork());
+ verifyAddSingleNetworkAndMatchScanDetailToNetworkAndCache(
+ WifiConfigurationTestUtil.createEapNetwork());
+ }
+
+ /**
+ * Verifies that scan details with wrong SSID/authentication types are not matched using
+ * {@link WifiConfigManagerNew#getSavedNetworkForScanDetailAndCache(ScanDetail)}
+ * to the added networks.
+ */
+ @Test
+ public void testNoMatchScanDetailToNetwork() {
+ // First create networks of different types.
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ WifiConfiguration wepNetwork = WifiConfigurationTestUtil.createWepNetwork();
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ WifiConfiguration eapNetwork = WifiConfigurationTestUtil.createEapNetwork();
+
+ // Now add them to WifiConfigManager.
+ verifyAddNetworkToWifiConfigManager(openNetwork);
+ verifyAddNetworkToWifiConfigManager(wepNetwork);
+ verifyAddNetworkToWifiConfigManager(pskNetwork);
+ verifyAddNetworkToWifiConfigManager(eapNetwork);
+
+ // Now create dummy scan detail corresponding to the networks.
+ ScanDetail openNetworkScanDetail = createScanDetailForNetwork(openNetwork);
+ ScanDetail wepNetworkScanDetail = createScanDetailForNetwork(wepNetwork);
+ ScanDetail pskNetworkScanDetail = createScanDetailForNetwork(pskNetwork);
+ ScanDetail eapNetworkScanDetail = createScanDetailForNetwork(eapNetwork);
+
+ // Now mix and match parameters from different scan details.
+ openNetworkScanDetail.getScanResult().SSID =
+ wepNetworkScanDetail.getScanResult().SSID;
+ wepNetworkScanDetail.getScanResult().capabilities =
+ pskNetworkScanDetail.getScanResult().capabilities;
+ pskNetworkScanDetail.getScanResult().capabilities =
+ eapNetworkScanDetail.getScanResult().capabilities;
+ eapNetworkScanDetail.getScanResult().capabilities =
+ 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));
+
+ // All the cache's should be empty as well.
+ assertNull(mWifiConfigManager.getScanDetailCacheForNetwork(openNetwork.networkId));
+ assertNull(mWifiConfigManager.getScanDetailCacheForNetwork(wepNetwork.networkId));
+ assertNull(mWifiConfigManager.getScanDetailCacheForNetwork(pskNetwork.networkId));
+ assertNull(mWifiConfigManager.getScanDetailCacheForNetwork(eapNetwork.networkId));
+ }
+
+ /**
+ * Verifies that scan detail cache is trimmed down when the size of the cache for a network
+ * exceeds {@link WifiConfigManagerNew#SCAN_CACHE_ENTRIES_MAX_SIZE}.
+ */
+ @Test
+ public void testScanDetailCacheTrimForNetwork() {
+ // Add a single network.
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ ScanDetailCache scanDetailCache;
+ String testBssidPrefix = "00:a5:b8:c9:45:";
+
+ // Modify |BSSID| field in the scan result and add copies of scan detail
+ // |SCAN_CACHE_ENTRIES_MAX_SIZE| times.
+ int scanDetailNum = 1;
+ for (; scanDetailNum <= WifiConfigManagerNew.SCAN_CACHE_ENTRIES_MAX_SIZE; scanDetailNum++) {
+ // Create dummy scan detail caches with different BSSID for the network.
+ ScanDetail scanDetail =
+ createScanDetailForNetwork(
+ openNetwork, String.format("%s%02x", testBssidPrefix, scanDetailNum));
+ assertNotNull(
+ mWifiConfigManager.getSavedNetworkForScanDetailAndCache(scanDetail));
+
+ // The size of scan detail cache should keep growing until it hits
+ // |SCAN_CACHE_ENTRIES_MAX_SIZE|.
+ scanDetailCache =
+ mWifiConfigManager.getScanDetailCacheForNetwork(openNetwork.networkId);
+ assertEquals(scanDetailNum, scanDetailCache.size());
+ }
+
+ // Now add the |SCAN_CACHE_ENTRIES_MAX_SIZE + 1| entry. This should trigger the trim.
+ ScanDetail scanDetail =
+ createScanDetailForNetwork(
+ openNetwork, String.format("%s%02x", testBssidPrefix, scanDetailNum));
+ assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(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
+ // was added after the trim.
+ scanDetailCache = mWifiConfigManager.getScanDetailCacheForNetwork(openNetwork.networkId);
+ assertEquals(WifiConfigManagerNew.SCAN_CACHE_ENTRIES_TRIM_SIZE + 1, scanDetailCache.size());
+ }
+
+ /**
+ * This method sets defaults in the provided WifiConfiguration object if not set
+ * so that it can be used for comparison with the configuration retrieved from
+ * WifiConfigManager.
+ */
+ private void setDefaults(WifiConfiguration configuration) {
+ if (configuration.allowedAuthAlgorithms.isEmpty()) {
+ configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
+ configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
+ }
+ if (configuration.allowedProtocols.isEmpty()) {
+ configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
+ configuration.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
+ }
+ if (configuration.allowedKeyManagement.isEmpty()) {
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+ }
+ if (configuration.allowedPairwiseCiphers.isEmpty()) {
+ configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+ configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
+ }
+ if (configuration.allowedGroupCiphers.isEmpty()) {
+ configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
+ configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
+ configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
+ configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
+ }
+ if (configuration.getIpAssignment() == IpConfiguration.IpAssignment.UNASSIGNED) {
+ configuration.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
+ }
+ if (configuration.getProxySettings() == IpConfiguration.ProxySettings.UNASSIGNED) {
+ configuration.setProxySettings(IpConfiguration.ProxySettings.NONE);
+ }
+ }
+
+ /**
+ * Modifies the provided configuration with creator uid, package name
+ * and time.
+ */
+ private void setCreationDebugParams(WifiConfiguration configuration) {
+ configuration.creatorUid = configuration.lastUpdateUid = TEST_CREATOR_UID;
+ configuration.creatorName = configuration.lastUpdateName = TEST_CREATOR_NAME;
+ configuration.creationTime = configuration.updateTime =
+ WifiConfigManagerNew.createDebugTimeStampString(
+ TEST_WALLCLOCK_CREATION_TIME_MILLIS);
+ }
+
+ /**
+ * Modifies the provided configuration with update uid, package name
+ * and time.
+ */
+ private void setUpdateDebugParams(WifiConfiguration configuration) {
+ configuration.lastUpdateUid = TEST_UPDATE_UID;
+ configuration.lastUpdateName = TEST_UPDATE_NAME;
+ configuration.updateTime =
+ WifiConfigManagerNew.createDebugTimeStampString(TEST_WALLCLOCK_UPDATE_TIME_MILLIS);
+ }
+
+ private void assertNotEquals(Object expected, Object actual) {
+ if (actual != null) {
+ assertFalse(actual.equals(expected));
+ } else {
+ assertNotNull(expected);
+ }
+ }
+
+ /**
+ * Modifies the provided WifiConfiguration with the specified bssid value. Also, asserts that
+ * the existing |BSSID| field is not the same value as the one being set
+ */
+ private void assertAndSetNetworkBSSID(WifiConfiguration configuration, String bssid) {
+ assertNotEquals(bssid, configuration.BSSID);
+ configuration.BSSID = bssid;
+ }
+
+ /**
+ * Modifies the provided WifiConfiguration with the specified |IpConfiguration| object. Also,
+ * asserts that the existing |mIpConfiguration| field is not the same value as the one being set
+ */
+ private void assertAndSetNetworkIpConfiguration(
+ WifiConfiguration configuration, IpConfiguration ipConfiguration) {
+ assertNotEquals(ipConfiguration, configuration.getIpConfiguration());
+ configuration.setIpConfiguration(ipConfiguration);
+ }
+
+ /**
+ * Modifies the provided WifiConfiguration with the specified |wepKeys| value and
+ * |wepTxKeyIndex|.
+ */
+ private void assertAndSetNetworkWepKeysAndTxIndex(
+ WifiConfiguration configuration, String[] wepKeys, int wepTxKeyIdx) {
+ assertNotEquals(wepKeys, configuration.wepKeys);
+ assertNotEquals(wepTxKeyIdx, configuration.wepTxKeyIndex);
+ configuration.wepKeys = Arrays.copyOf(wepKeys, wepKeys.length);
+ configuration.wepTxKeyIndex = wepTxKeyIdx;
+ }
+
+ /**
+ * Modifies the provided WifiConfiguration with the specified |preSharedKey| value.
+ */
+ private void assertAndSetNetworkPreSharedKey(
+ WifiConfiguration configuration, String preSharedKey) {
+ assertNotEquals(preSharedKey, configuration.preSharedKey);
+ configuration.preSharedKey = preSharedKey;
+ }
+
+ /**
+ * Modifies the provided WifiConfiguration with the specified enteprise |password| value.
+ */
+ private void assertAndSetNetworkEnterprisePassword(
+ WifiConfiguration configuration, String password) {
+ assertNotEquals(password, configuration.enterpriseConfig.getPassword());
+ configuration.enterpriseConfig.setPassword(password);
+ }
+
+ /**
+ * Returns whether the provided network was in the store data or not.
+ */
+ private boolean isNetworkInConfigStoreData(WifiConfiguration configuration) {
+ try {
+ ArgumentCaptor<WifiConfigStoreData> storeDataCaptor =
+ ArgumentCaptor.forClass(WifiConfigStoreData.class);
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore)
+ .write(anyBoolean(), storeDataCaptor.capture());
+
+ WifiConfigStoreData storeData = storeDataCaptor.getValue();
+
+ boolean foundNetworkInStoreData = false;
+ for (WifiConfiguration retrievedConfig : storeData.configurations) {
+ if (retrievedConfig.configKey().equals(configuration.configKey())) {
+ foundNetworkInStoreData = true;
+ }
+ }
+ return foundNetworkInStoreData;
+ } catch (Exception e) {
+ fail("Exception encountered during write " + e);
+ }
+ return false;
+ }
+
+ /**
+ * Verifies that the provided network was not present in the last config store write.
+ */
+ private void verifyNetworkNotInConfigStoreData(WifiConfiguration configuration) {
+ assertFalse(isNetworkInConfigStoreData(configuration));
+ }
+
+ /**
+ * Verifies that the provided network was present in the last config store write.
+ */
+ private void verifyNetworkInConfigStoreData(WifiConfiguration configuration) {
+ assertTrue(isNetworkInConfigStoreData(configuration));
+ }
+
+ private void assertPasswordsMaskedInWifiConfiguration(WifiConfiguration configuration) {
+ if (!TextUtils.isEmpty(configuration.preSharedKey)) {
+ assertEquals(WifiConfigManagerNew.PASSWORD_MASK, configuration.preSharedKey);
+ }
+ if (configuration.wepKeys != null) {
+ for (int i = 0; i < configuration.wepKeys.length; i++) {
+ if (!TextUtils.isEmpty(configuration.wepKeys[i])) {
+ assertEquals(WifiConfigManagerNew.PASSWORD_MASK, configuration.wepKeys[i]);
+ }
+ }
+ }
+ if (!TextUtils.isEmpty(configuration.enterpriseConfig.getPassword())) {
+ assertEquals(
+ WifiConfigManagerNew.PASSWORD_MASK,
+ configuration.enterpriseConfig.getPassword());
+ }
+ }
+
+ /**
+ * Verifies that the network was present in the network change broadcast and returns the
+ * change reason.
+ */
+ private int verifyNetworkInBroadcastAndReturnReason(WifiConfiguration configuration) {
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ ArgumentCaptor<UserHandle> userHandleCaptor = ArgumentCaptor.forClass(UserHandle.class);
+ mContextConfigStoreMockOrder.verify(mContext)
+ .sendBroadcastAsUser(intentCaptor.capture(), userHandleCaptor.capture());
+
+ assertEquals(userHandleCaptor.getValue(), UserHandle.ALL);
+ Intent intent = intentCaptor.getValue();
+
+ int changeReason = intent.getIntExtra(WifiManager.EXTRA_CHANGE_REASON, -1);
+ WifiConfiguration retrievedConfig =
+ (WifiConfiguration) intent.getExtra(WifiManager.EXTRA_WIFI_CONFIGURATION);
+ assertEquals(retrievedConfig.configKey(), configuration.configKey());
+
+ // Verify that all the passwords are masked in the broadcast configuration.
+ assertPasswordsMaskedInWifiConfiguration(retrievedConfig);
+
+ return changeReason;
+ }
+
+ /**
+ * Verifies that we sent out an add broadcast with the provided network.
+ */
+ private void verifyNetworkAddBroadcast(WifiConfiguration configuration) {
+ assertEquals(
+ verifyNetworkInBroadcastAndReturnReason(configuration),
+ WifiManager.CHANGE_REASON_ADDED);
+ }
+
+ /**
+ * Verifies that we sent out an update broadcast with the provided network.
+ */
+ private void verifyNetworkUpdateBroadcast(WifiConfiguration configuration) {
+ assertEquals(
+ verifyNetworkInBroadcastAndReturnReason(configuration),
+ WifiManager.CHANGE_REASON_CONFIG_CHANGE);
+ }
+
+ /**
+ * Verifies that we sent out a remove broadcast with the provided network.
+ */
+ private void verifyNetworkRemoveBroadcast(WifiConfiguration configuration) {
+ assertEquals(
+ verifyNetworkInBroadcastAndReturnReason(configuration),
+ WifiManager.CHANGE_REASON_REMOVED);
+ }
+
+ /**
+ * Adds the provided configuration to WifiConfigManager and modifies the provided configuration
+ * with creator/update uid, package name and time. This also sets defaults for fields not
+ * populated.
+ * These fields are populated internally by WifiConfigManager and hence we need
+ * to modify the configuration before we compare the added network with the retrieved network.
+ */
+ private NetworkUpdateResult addNetworkToWifiConfigManager(WifiConfiguration configuration) {
+ when(mClock.getWallClockMillis()).thenReturn(TEST_WALLCLOCK_CREATION_TIME_MILLIS);
+ NetworkUpdateResult result =
+ mWifiConfigManager.addOrUpdateNetwork(configuration, TEST_CREATOR_UID);
+ setDefaults(configuration);
+ setCreationDebugParams(configuration);
+ configuration.networkId = result.getNetworkId();
+ return result;
+ }
+
+ /**
+ * Add network to WifiConfigManager and ensure that it was successful.
+ */
+ private NetworkUpdateResult verifyAddNetworkToWifiConfigManager(
+ WifiConfiguration configuration) {
+ NetworkUpdateResult result = addNetworkToWifiConfigManager(configuration);
+ assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
+ assertTrue(result.isNewNetwork());
+ assertTrue(result.hasIpChanged());
+ assertTrue(result.hasProxyChanged());
+
+ verifyNetworkAddBroadcast(configuration);
+ // Verify that the config store write was triggered with this new configuration.
+ verifyNetworkInConfigStoreData(configuration);
+ return result;
+ }
+
+ /**
+ * Add ephemeral network to WifiConfigManager and ensure that it was successful.
+ */
+ private NetworkUpdateResult verifyAddEphemeralNetworkToWifiConfigManager(
+ WifiConfiguration configuration) {
+ NetworkUpdateResult result = addNetworkToWifiConfigManager(configuration);
+ assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
+ assertTrue(result.isNewNetwork());
+ assertTrue(result.hasIpChanged());
+ assertTrue(result.hasProxyChanged());
+
+ verifyNetworkAddBroadcast(configuration);
+ // Ephemeral networks should not be persisted.
+ verifyNetworkNotInConfigStoreData(configuration);
+ return result;
+ }
+
+ /**
+ * Updates the provided configuration to WifiConfigManager and modifies the provided
+ * configuration with update uid, package name and time.
+ * These fields are populated internally by WifiConfigManager and hence we need
+ * to modify the configuration before we compare the added network with the retrieved network.
+ */
+ private NetworkUpdateResult updateNetworkToWifiConfigManager(WifiConfiguration configuration) {
+ when(mClock.getWallClockMillis()).thenReturn(TEST_WALLCLOCK_UPDATE_TIME_MILLIS);
+ NetworkUpdateResult result =
+ mWifiConfigManager.addOrUpdateNetwork(configuration, TEST_UPDATE_UID);
+ setUpdateDebugParams(configuration);
+ return result;
+ }
+
+ /**
+ * Update network to WifiConfigManager config change and ensure that it was successful.
+ */
+ private NetworkUpdateResult verifyUpdateNetworkToWifiConfigManager(
+ WifiConfiguration configuration) {
+ NetworkUpdateResult result = updateNetworkToWifiConfigManager(configuration);
+ assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
+ assertFalse(result.isNewNetwork());
+
+ verifyNetworkUpdateBroadcast(configuration);
+ // Verify that the config store write was triggered with this new configuration.
+ verifyNetworkInConfigStoreData(configuration);
+ return result;
+ }
+
+ /**
+ * Update network to WifiConfigManager without IP config change and ensure that it was
+ * successful.
+ */
+ private NetworkUpdateResult verifyUpdateNetworkToWifiConfigManagerWithoutIpChange(
+ WifiConfiguration configuration) {
+ NetworkUpdateResult result = verifyUpdateNetworkToWifiConfigManager(configuration);
+ assertFalse(result.hasIpChanged());
+ assertFalse(result.hasProxyChanged());
+ return result;
+ }
+
+ /**
+ * Update network to WifiConfigManager with IP config change and ensure that it was
+ * successful.
+ */
+ private NetworkUpdateResult verifyUpdateNetworkToWifiConfigManagerWithIpChange(
+ WifiConfiguration configuration) {
+ NetworkUpdateResult result = verifyUpdateNetworkToWifiConfigManager(configuration);
+ assertTrue(result.hasIpChanged());
+ assertTrue(result.hasProxyChanged());
+ return result;
+ }
+
+ /**
+ * Removes network from WifiConfigManager and ensure that it was successful.
+ */
+ private void verifyRemoveNetworkFromWifiConfigManager(
+ WifiConfiguration configuration) {
+ assertTrue(mWifiConfigManager.removeNetwork(configuration.networkId));
+
+ verifyNetworkRemoveBroadcast(configuration);
+ // Verify if the config store write was triggered without this new configuration.
+ verifyNetworkNotInConfigStoreData(configuration);
+ }
+
+ /**
+ * Verifies the provided network's public status and ensures that the network change broadcast
+ * has been sent out.
+ */
+ private void verifyUpdateNetworkStatus(WifiConfiguration configuration, int status) {
+ assertEquals(status, configuration.status);
+ verifyNetworkUpdateBroadcast(configuration);
+ }
+
+ /**
+ * Verifies the network's selection status update.
+ *
+ * For temporarily disabled reasons, the method ensures that the status has changed only if
+ * disable reason counter has exceeded the threshold.
+ *
+ * For permanently disabled/enabled reasons, the method ensures that the public status has
+ * changed and the network change broadcast has been sent out.
+ */
+ private void verifyUpdateNetworkSelectionStatus(
+ int networkId, int reason, int temporaryDisableReasonCounter) {
+ when(mClock.getElapsedSinceBootMillis())
+ .thenReturn(TEST_ELAPSED_UPDATE_NETWORK_SELECTION_TIME_MILLIS);
+
+ // Fetch the current status of the network before we try to update the status.
+ WifiConfiguration retrievedNetwork = mWifiConfigManager.getConfiguredNetwork(networkId);
+ NetworkSelectionStatus currentStatus = retrievedNetwork.getNetworkSelectionStatus();
+ int currentDisableReason = currentStatus.getNetworkSelectionDisableReason();
+
+ // First set the status to the provided reason.
+ assertTrue(mWifiConfigManager.updateNetworkSelectionStatus(networkId, reason));
+
+ // Now fetch the network configuration and verify the new status of the network.
+ retrievedNetwork = mWifiConfigManager.getConfiguredNetwork(networkId);
+
+ NetworkSelectionStatus retrievedStatus = retrievedNetwork.getNetworkSelectionStatus();
+ int retrievedDisableReason = retrievedStatus.getNetworkSelectionDisableReason();
+ long retrievedDisableTime = retrievedStatus.getDisableTime();
+ int retrievedDisableReasonCounter = retrievedStatus.getDisableReasonCounter(reason);
+ int disableReasonThreshold =
+ WifiConfigManagerNew.NETWORK_SELECTION_DISABLE_THRESHOLD[reason];
+
+ if (reason == NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
+ assertEquals(reason, retrievedDisableReason);
+ assertTrue(retrievedStatus.isNetworkEnabled());
+ assertEquals(
+ NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP,
+ retrievedDisableTime);
+ verifyUpdateNetworkStatus(retrievedNetwork, WifiConfiguration.Status.ENABLED);
+ } else if (reason < NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) {
+ // For temporarily disabled networks, we need to ensure that the current status remains
+ // until the threshold is crossed.
+ assertEquals(temporaryDisableReasonCounter, retrievedDisableReasonCounter);
+ if (retrievedDisableReasonCounter < disableReasonThreshold) {
+ assertEquals(currentDisableReason, retrievedDisableReason);
+ assertEquals(
+ currentStatus.getNetworkSelectionStatus(),
+ retrievedStatus.getNetworkSelectionStatus());
+ } else {
+ assertEquals(reason, retrievedDisableReason);
+ assertTrue(retrievedStatus.isNetworkTemporaryDisabled());
+ assertEquals(
+ TEST_ELAPSED_UPDATE_NETWORK_SELECTION_TIME_MILLIS, retrievedDisableTime);
+ }
+ } else if (reason < NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX) {
+ assertEquals(reason, retrievedDisableReason);
+ assertTrue(retrievedStatus.isNetworkPermanentlyDisabled());
+ assertEquals(
+ NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP,
+ retrievedDisableTime);
+ verifyUpdateNetworkStatus(retrievedNetwork, WifiConfiguration.Status.DISABLED);
+ }
+ }
+
+ /**
+ * Creates a scan detail corresponding to the provided network and BSSID value.
+ */
+ private ScanDetail createScanDetailForNetwork(WifiConfiguration configuration, String bssid) {
+ 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, 0, 0, SystemClock.uptimeMillis(), System.currentTimeMillis());
+ }
+
+ /**
+ * Creates a scan detail corresponding to the provided network and fixed BSSID value.
+ */
+ private ScanDetail createScanDetailForNetwork(WifiConfiguration configuration) {
+ return createScanDetailForNetwork(configuration, TEST_BSSID);
+ }
+
+ /**
+ * 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 WifiConfigManagerNew#getSavedNetworkForScanDetailAndCache(ScanDetail)} and also
+ * verifies that the provided scan detail was cached,
+ */
+ private void verifyAddSingleNetworkAndMatchScanDetailToNetworkAndCache(
+ WifiConfiguration network) {
+ // First add the provided network.
+ verifyAddNetworkToWifiConfigManager(network);
+
+ // Now create a dummy scan detail corresponding to the network.
+ ScanDetail scanDetail = createScanDetailForNetwork(network);
+ ScanResult scanResult = scanDetail.getScanResult();
+
+ WifiConfiguration retrievedNetwork =
+ mWifiConfigManager.getSavedNetworkForScanDetailAndCache(scanDetail);
+
+ WifiConfigurationTestUtil.assertConfigurationEqualForConfigManagerAddOrUpdate(
+ network, retrievedNetwork);
+
+ // Now retrieve the scan detail cache and ensure that the new scan detail is in cache.
+ ScanDetailCache retrievedScanDetailCache =
+ mWifiConfigManager.getScanDetailCacheForNetwork(network.networkId);
+ assertEquals(1, retrievedScanDetailCache.size());
+ ScanResult retrievedScanResult = retrievedScanDetailCache.get(scanResult.BSSID);
+
+ ScanTestUtil.assertScanResultEquals(scanResult, retrievedScanResult);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
index a585000..fd1d66d 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
import android.content.Context;
import android.content.pm.UserInfo;
import android.net.wifi.FakeKeys;
@@ -45,7 +46,6 @@
import android.util.SparseArray;
import com.android.server.net.DelayedDiskWrite;
-import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments;
import com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager;
import com.android.server.wifi.hotspot2.pps.Credential;
import com.android.server.wifi.hotspot2.pps.HomeSP;
@@ -420,23 +420,15 @@
assertEquals(WifiConfiguration.Status.ENABLED, config2.status);
}
} else {
- // If the network configuration is visible to the current user, verify that it
- // was enabled and all other network configurations visible to the user were
- // disabled.
+ // If the network configuration is visible to the current user, verify that
+ // a connection attempt was made to it. This does not modify the status of
+ // other networks.
assertTrue(success);
verify(mWifiNative).selectNetwork(config.networkId);
verify(mWifiNative, never()).selectNetwork(intThat(not(config.networkId)));
verify(mWifiNative, never()).enableNetwork(config.networkId);
verify(mWifiNative, never()).enableNetwork(intThat(not(config.networkId)));
- for (WifiConfiguration config2 : mConfiguredNetworks.valuesForAllUsers()) {
- if (WifiConfigurationUtil.isVisibleToAnyProfile(config2,
- USER_PROFILES.get(userId))
- && config2.networkId != config.networkId) {
- assertEquals(WifiConfiguration.Status.DISABLED, config2.status);
- } else {
- assertEquals(WifiConfiguration.Status.ENABLED, config2.status);
- }
- }
+ assertEquals(WifiConfiguration.Status.ENABLED, config.status);
}
}
}
@@ -471,12 +463,13 @@
// configuration.
final Map<String, String> metadata = new HashMap<String, String>();
if (CONFIGS.get(network).FQDN != null) {
- metadata.put(WifiConfigStore.ID_STRING_KEY_FQDN, CONFIGS.get(network).FQDN);
+ metadata.put(WifiSupplicantControl.ID_STRING_KEY_FQDN, CONFIGS.get(network).FQDN);
}
- metadata.put(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY, CONFIGS.get(network).configKey());
- metadata.put(WifiConfigStore.ID_STRING_KEY_CREATOR_UID,
+ metadata.put(
+ WifiSupplicantControl.ID_STRING_KEY_CONFIG_KEY, CONFIGS.get(network).configKey());
+ metadata.put(WifiSupplicantControl.ID_STRING_KEY_CREATOR_UID,
Integer.toString(CONFIGS.get(network).creatorUid));
- verify(mWifiNative).setNetworkExtra(network, WifiConfigStore.ID_STRING_VAR_NAME,
+ verify(mWifiNative).setNetworkExtra(network, WifiSupplicantControl.ID_STRING_VAR_NAME,
metadata);
// Verify that an attempt to read back the requirePMF variable was made.
@@ -576,27 +569,27 @@
.thenReturn(encodeConfigSSID(CONFIGS.get(i)));
}
// Legacy regular network configuration: No "id_str".
- when(mWifiNative.getNetworkExtra(0, WifiConfigStore.ID_STRING_VAR_NAME))
+ when(mWifiNative.getNetworkExtra(0, WifiSupplicantControl.ID_STRING_VAR_NAME))
.thenReturn(null);
// Legacy Hotspot 2.0 network configuration: Quoted FQDN in "id_str".
- when(mWifiNative.getNetworkExtra(1, WifiConfigStore.ID_STRING_VAR_NAME))
+ when(mWifiNative.getNetworkExtra(1, WifiSupplicantControl.ID_STRING_VAR_NAME))
.thenReturn(null);
- when(mWifiNative.getNetworkVariable(1, WifiConfigStore.ID_STRING_VAR_NAME))
+ when(mWifiNative.getNetworkVariable(1, WifiSupplicantControl.ID_STRING_VAR_NAME))
.thenReturn('"' + CONFIGS.get(1).FQDN + '"');
// Up-to-date Hotspot 2.0 network configuration: Metadata in "id_str".
Map<String, String> metadata = new HashMap<String, String>();
- metadata.put(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY, CONFIGS.get(2).configKey());
- metadata.put(WifiConfigStore.ID_STRING_KEY_CREATOR_UID,
+ metadata.put(WifiSupplicantControl.ID_STRING_KEY_CONFIG_KEY, CONFIGS.get(2).configKey());
+ metadata.put(WifiSupplicantControl.ID_STRING_KEY_CREATOR_UID,
Integer.toString(CONFIGS.get(2).creatorUid));
- metadata.put(WifiConfigStore.ID_STRING_KEY_FQDN, CONFIGS.get(2).FQDN);
- when(mWifiNative.getNetworkExtra(2, WifiConfigStore.ID_STRING_VAR_NAME))
+ metadata.put(WifiSupplicantControl.ID_STRING_KEY_FQDN, CONFIGS.get(2).FQDN);
+ when(mWifiNative.getNetworkExtra(2, WifiSupplicantControl.ID_STRING_VAR_NAME))
.thenReturn(metadata);
// Up-to-date regular network configuration: Metadata in "id_str".
metadata = new HashMap<String, String>();
- metadata.put(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY, CONFIGS.get(3).configKey());
- metadata.put(WifiConfigStore.ID_STRING_KEY_CREATOR_UID,
+ metadata.put(WifiSupplicantControl.ID_STRING_KEY_CONFIG_KEY, CONFIGS.get(3).configKey());
+ metadata.put(WifiSupplicantControl.ID_STRING_KEY_CREATOR_UID,
Integer.toString(CONFIGS.get(3).creatorUid));
- when(mWifiNative.getNetworkExtra(3, WifiConfigStore.ID_STRING_VAR_NAME))
+ when(mWifiNative.getNetworkExtra(3, WifiSupplicantControl.ID_STRING_VAR_NAME))
.thenReturn(metadata);
// Set up networkHistory.txt file.
@@ -661,10 +654,10 @@
when(mWifiNative.getNetworkVariable(anyInt(), eq(WifiConfiguration.ssidVarName)))
.thenReturn(encodeConfigSSID(config));
final Map<String, String> metadata = new HashMap<String, String>();
- metadata.put(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY, config.configKey());
- metadata.put(WifiConfigStore.ID_STRING_KEY_CREATOR_UID,
+ metadata.put(WifiSupplicantControl.ID_STRING_KEY_CONFIG_KEY, config.configKey());
+ metadata.put(WifiSupplicantControl.ID_STRING_KEY_CREATOR_UID,
Integer.toString(config.creatorUid));
- when(mWifiNative.getNetworkExtra(anyInt(), eq(WifiConfigStore.ID_STRING_VAR_NAME)))
+ when(mWifiNative.getNetworkExtra(anyInt(), eq(WifiSupplicantControl.ID_STRING_VAR_NAME)))
.thenReturn(metadata);
// Load network configurations.
@@ -741,16 +734,20 @@
for (WifiConfiguration config : newConfigs) {
if (oldUserOnlyConfigs.contains(config)) {
verify(mWifiNative).disableNetwork(config.networkId);
- assertEquals(WifiConfiguration.Status.DISABLED, config.status);
+ assertNetworkStatus(
+ config,
+ WifiConfiguration.NetworkSelectionStatus.DISABLED_DUE_TO_USER_SWITCH);
} else {
verify(mWifiNative, never()).disableNetwork(config.networkId);
if (neitherUserConfigs.contains(config)) {
- assertEquals(WifiConfiguration.Status.DISABLED, config.status);
+ assertNetworkStatus(
+ config,
+ WifiConfiguration.NetworkSelectionStatus.DISABLED_DUE_TO_USER_SWITCH);
} else {
- // Only enabled in networkSelection.
- assertTrue(config.getNetworkSelectionStatus().isNetworkEnabled());
+ assertNetworkStatus(
+ config,
+ WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
}
-
}
}
}
@@ -1463,8 +1460,8 @@
updateRequirePMFConfig.requirePMF = true;
// Set up mock to allow the new value to be read back into the config
- // TODO: please see b/28088226 - this test is implemented as if WifiConfigStore correctly
- // read back the boolean value. When fixed, uncomment the following line and the
+ // TODO: please see b/28088226 - this test is implemented as if WifiSupplicantControl
+ // correctly read back the boolean value. When fixed, uncomment the following line and the
// checkHasEverConnectedFalse below.
//when(mWifiNative.getNetworkVariable(BASE_HAS_EVER_CONNECTED_CONFIG.networkId,
// WifiConfiguration.pmfVarName)).thenReturn("2");
@@ -1591,7 +1588,6 @@
eapConfigSame.enterpriseConfig));
}
-
private void checkHasEverConnectedTrue(int networkId) {
WifiConfiguration checkConfig = mWifiConfigManager.getWifiConfiguration(networkId);
assertTrue("hasEverConnected expected to be true.",
@@ -1627,5 +1623,80 @@
return buf.toString();
}
+ /**
+ * Test whether enableNetwork with the disableOthers flag set to false enables the
+ * input network, but does not attempt a connection.
+ */
+ @Test
+ public void testEnableNetworkWithoutDisableOthers() throws Exception {
+ addNetworks();
+ for (Map.Entry<Integer, List<WifiConfiguration>> entry : VISIBLE_CONFIGS.entrySet()) {
+ switchUser(entry.getKey());
+ // Iterate through all the configs for the current user and invoke |enableNetwork|
+ // on the corresponding config retrieved from WifiConfigManager.
+ for (WifiConfiguration config : entry.getValue()) {
+ WifiConfiguration retrievedConfig =
+ mWifiConfigManager.getWifiConfiguration(config.networkId);
+ assertTrue(mWifiConfigManager.enableNetwork(retrievedConfig, false, 0));
+ assertNetworkStatus(retrievedConfig,
+ WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
+ verify(mWifiNative, never()).selectNetwork(anyInt());
+ }
+ }
+ }
+
+ /**
+ * Test whether enableNetwork without the disableOthers flag set to true enables the input
+ * network and attempts a connection to it immediately. It also checks if all the other
+ * networks are disabled.
+ */
+ @Test
+ public void testEnableNetworkWithDisableOthers() throws Exception {
+ addNetworks();
+
+ for (Map.Entry<Integer, List<WifiConfiguration>> entry : VISIBLE_CONFIGS.entrySet()) {
+ switchUser(entry.getKey());
+ // Iterate through all the configs for the current user and invoke |enableNetwork|
+ // on the corresponding config retrieved from WifiConfigManager.
+ for (WifiConfiguration config : entry.getValue()) {
+ reset(mWifiNative);
+ when(mWifiNative.selectNetwork(anyInt())).thenReturn(true);
+ WifiConfiguration retrievedConfig =
+ mWifiConfigManager.getWifiConfiguration(config.networkId);
+ assertTrue(mWifiConfigManager.enableNetwork(retrievedConfig, true, 0));
+ assertNetworkStatus(retrievedConfig,
+ WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
+ assertAllNetworksDisabledExcept(retrievedConfig.networkId,
+ WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER);
+ verify(mWifiNative).selectNetwork(retrievedConfig.networkId);
+ verify(mWifiNative, never()).selectNetwork(intThat(not(retrievedConfig.networkId)));
+ }
+ }
+ }
+
+ private void assertNetworkStatus(WifiConfiguration config, int disableReason) {
+ final WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus();
+ assertEquals(disableReason, status.getNetworkSelectionDisableReason());
+ if (disableReason
+ == WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
+ assertEquals(WifiConfiguration.Status.ENABLED, config.status);
+ assertTrue(config.getNetworkSelectionStatus().isNetworkEnabled());
+ } else if (disableReason
+ < WifiConfiguration.NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) {
+ assertEquals(WifiConfiguration.Status.ENABLED, config.status);
+ assertTrue(config.getNetworkSelectionStatus().isNetworkTemporaryDisabled());
+ } else {
+ assertEquals(WifiConfiguration.Status.DISABLED, config.status);
+ assertTrue(config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled());
+ }
+ }
+
+ private void assertAllNetworksDisabledExcept(int netId, int disableReason) {
+ for (WifiConfiguration config : mWifiConfigManager.getSavedNetworks()) {
+ if (config.networkId != netId) {
+ assertNetworkStatus(config, disableReason);
+ }
+ }
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreDataTest.java
new file mode 100644
index 0000000..7bed17a
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreDataTest.java
@@ -0,0 +1,419 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.WifiConfigStoreData}.
+ */
+@SmallTest
+public class WifiConfigStoreDataTest {
+
+ private static final String TEST_SSID = "WifiConfigStoreDataSSID_";
+ private static final String TEST_CONNECT_CHOICE = "XmlUtilConnectChoice";
+ private static final long TEST_CONNECT_CHOICE_TIMESTAMP = 0x4566;
+ private static final Set<String> TEST_DELETED_EPHEMERAL_LIST = new HashSet<String>() {
+ {
+ add("\"" + TEST_SSID + "1\"");
+ add("\"" + TEST_SSID + "2\"");
+ }
+ };
+ private static final String SINGLE_OPEN_NETWORK_LIST_XML_STRING_FORMAT =
+ "<NetworkList>\n"
+ + "<Network>\n"
+ + "<WifiConfiguration>\n"
+ + "<string name=\"ConfigKey\">%s</string>\n"
+ + "<string name=\"SSID\">%s</string>\n"
+ + "<null name=\"BSSID\" />\n"
+ + "<null name=\"PreSharedKey\" />\n"
+ + "<null name=\"WEPKeys\" />\n"
+ + "<int name=\"WEPTxKeyIndex\" value=\"0\" />\n"
+ + "<boolean name=\"HiddenSSID\" value=\"false\" />\n"
+ + "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">01</byte-array>\n"
+ + "<byte-array name=\"AllowedProtocols\" num=\"0\"></byte-array>\n"
+ + "<byte-array name=\"AllowedAuthAlgos\" num=\"0\"></byte-array>\n"
+ + "<boolean name=\"Shared\" value=\"%s\" />\n"
+ + "<null name=\"FQDN\" />\n"
+ + "<null name=\"ProviderFriendlyName\" />\n"
+ + "<null name=\"LinkedNetworksList\" />\n"
+ + "<null name=\"DefaultGwMacAddress\" />\n"
+ + "<boolean name=\"ValidatedInternetAccess\" value=\"false\" />\n"
+ + "<boolean name=\"NoInternetAccessExpected\" value=\"false\" />\n"
+ + "<int name=\"UserApproved\" value=\"0\" />\n"
+ + "<boolean name=\"MeteredHint\" value=\"false\" />\n"
+ + "<boolean name=\"UseExternalScores\" value=\"false\" />\n"
+ + "<int name=\"NumAssociation\" value=\"0\" />\n"
+ + "<int name=\"CreatorUid\" value=\"%d\" />\n"
+ + "<null name=\"CreatorName\" />\n"
+ + "<null name=\"CreationTime\" />\n"
+ + "<int name=\"LastUpdateUid\" value=\"-1\" />\n"
+ + "<null name=\"LastUpdateName\" />\n"
+ + "<int name=\"LastConnectUid\" value=\"0\" />\n"
+ + "</WifiConfiguration>\n"
+ + "<NetworkStatus>\n"
+ + "<int name=\"SelectionStatus\" value=\"0\" />\n"
+ + "<int name=\"DisableReason\" value=\"0\" />\n"
+ + "<null name=\"ConnectChoice\" />\n"
+ + "<long name=\"ConnectChoiceTimeStamp\" value=\"-1\" />\n"
+ + "<boolean name=\"HasEverConnected\" value=\"false\" />\n"
+ + "</NetworkStatus>\n"
+ + "<IpConfiguration>\n"
+ + "<string name=\"IpAssignment\">DHCP</string>\n"
+ + "<string name=\"ProxySettings\">NONE</string>\n"
+ + "</IpConfiguration>\n"
+ + "</Network>\n"
+ + "</NetworkList>\n";
+ private static final String SINGLE_OPEN_NETWORK_SHARED_DATA_XML_STRING_FORMAT =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<WifiConfigStoreData>\n"
+ + "<int name=\"Version\" value=\"1\" />\n"
+ + SINGLE_OPEN_NETWORK_LIST_XML_STRING_FORMAT
+ + "</WifiConfigStoreData>\n";
+ private static final String SINGLE_OPEN_NETWORK_USER_DATA_XML_STRING_FORMAT =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<WifiConfigStoreData>\n"
+ + "<int name=\"Version\" value=\"1\" />\n"
+ + SINGLE_OPEN_NETWORK_LIST_XML_STRING_FORMAT
+ + "<DeletedEphemeralSSIDList>\n"
+ + "<set name=\"SSIDList\" />\n"
+ + "</DeletedEphemeralSSIDList>\n"
+ + "</WifiConfigStoreData>\n";
+
+ /**
+ * Asserts that the 2 config store data are equal.
+ */
+ public static void assertConfigStoreDataEqual(
+ WifiConfigStoreData expected, WifiConfigStoreData actual) {
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigStore(
+ expected.configurations, actual.configurations);
+ assertEquals(expected.deletedEphemeralSSIDs, actual.deletedEphemeralSSIDs);
+ }
+
+ /**
+ * Verify that multiple shared networks with different credential types and IpConfiguration
+ * types are serialized and deserialized correctly.
+ */
+ @Test
+ public void testMultipleNetworkAllShared()
+ throws XmlPullParserException, IOException {
+ List<WifiConfiguration> configurations = createNetworks(true);
+ serializeDeserializeConfigStoreData(configurations);
+ }
+
+ /**
+ * Verify that multiple user networks with different credential types and IpConfiguration
+ * types are serialized and deserialized correctly.
+ */
+ @Test
+ public void testMultipleNetworksAllUser()
+ throws XmlPullParserException, IOException {
+ List<WifiConfiguration> configurations = createNetworks(false);
+ serializeDeserializeConfigStoreData(configurations);
+ }
+
+ /**
+ * Verify that multiple networks with different credential types and IpConfiguration
+ * types are serialized and deserialized correctly when both user & shared networks are present.
+ */
+ @Test
+ public void testMultipleNetworksSharedAndUserNetworks()
+ throws XmlPullParserException, IOException {
+ List<WifiConfiguration> configurations = createNetworks();
+ // Let's split the list of networks into 2 and make all the networks in the first list
+ // shared and the second list all user networks.
+ int listSize = configurations.size();
+ List<WifiConfiguration> sharedConfigurations = configurations.subList(0, listSize / 2);
+ List<WifiConfiguration> userConfigurations = configurations.subList(listSize / 2, listSize);
+ for (WifiConfiguration config : sharedConfigurations) {
+ config.shared = true;
+ }
+ for (WifiConfiguration config : userConfigurations) {
+ config.shared = false;
+ }
+ serializeDeserializeConfigStoreData(configurations);
+ }
+
+ /**
+ * Verify that multiple shared networks with different credential types and IpConfiguration
+ * types are serialized and deserialized correctly when the shared data bytes are null in
+ * |parseRawData| method.
+ */
+ @Test
+ public void testMultipleNetworksSharedDataNullInParseRawData()
+ throws XmlPullParserException, IOException {
+ List<WifiConfiguration> configurations = createNetworks(false);
+ serializeDeserializeConfigStoreData(configurations, true, false);
+ }
+
+ /**
+ * Verify that multiple shared networks with different credential types and IpConfiguration
+ * types are serialized and deserialized correctly when the user data bytes are null in
+ * |parseRawData| method.
+ */
+ @Test
+ public void testMultipleNetworksUserDataNullInParseRawData()
+ throws XmlPullParserException, IOException {
+ List<WifiConfiguration> configurations = createNetworks(true);
+ serializeDeserializeConfigStoreData(configurations, false, true);
+ }
+
+ /**
+ * Verify that the manually populated xml string for is deserialized/serialized correctly.
+ * This generates a store data corresponding to the XML string and verifies that the string
+ * is indeed parsed correctly to the store data.
+ */
+ @Test
+ public void testManualConfigStoreDataParse() {
+ WifiConfiguration sharedNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ sharedNetwork.shared = true;
+ sharedNetwork.setIpConfiguration(WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy());
+ WifiConfiguration userNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ userNetwork.setIpConfiguration(WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy());
+ userNetwork.shared = false;
+
+ // Create the store data for comparison.
+ List<WifiConfiguration> networks = new ArrayList<>();
+ networks.add(sharedNetwork);
+ networks.add(userNetwork);
+ WifiConfigStoreData storeData =
+ new WifiConfigStoreData(networks, new HashSet<String>());
+
+ String sharedStoreXmlString =
+ String.format(SINGLE_OPEN_NETWORK_SHARED_DATA_XML_STRING_FORMAT,
+ sharedNetwork.configKey().replaceAll("\"", """),
+ sharedNetwork.SSID.replaceAll("\"", """),
+ sharedNetwork.shared, sharedNetwork.creatorUid);
+ String userStoreXmlString =
+ String.format(SINGLE_OPEN_NETWORK_USER_DATA_XML_STRING_FORMAT,
+ userNetwork.configKey().replaceAll("\"", """),
+ userNetwork.SSID.replaceAll("\"", """),
+ userNetwork.shared, userNetwork.creatorUid);
+ byte[] rawSharedData = sharedStoreXmlString.getBytes();
+ byte[] rawUserData = userStoreXmlString.getBytes();
+ WifiConfigStoreData retrievedStoreData = null;
+ try {
+ retrievedStoreData = WifiConfigStoreData.parseRawData(rawSharedData, rawUserData);
+ } catch (Exception e) {
+ // Assert if an exception was raised.
+ fail("Error in parsing the xml data: " + e
+ + ". Shared data: " + sharedStoreXmlString
+ + ", User data: " + userStoreXmlString);
+ }
+ // Compare the retrieved config store data with the original.
+ assertConfigStoreDataEqual(storeData, retrievedStoreData);
+
+ // Now convert the store data to XML bytes and compare the output with the expected string.
+ byte[] retrievedSharedStoreXmlBytes = null;
+ byte[] retrievedUserStoreXmlBytes = null;
+ try {
+ retrievedSharedStoreXmlBytes = retrievedStoreData.createSharedRawData();
+ retrievedUserStoreXmlBytes = retrievedStoreData.createUserRawData();
+ } catch (Exception e) {
+ // Assert if an exception was raised.
+ fail("Error in writing the xml data: " + e);
+ }
+ String retrievedSharedStoreXmlString =
+ new String(retrievedSharedStoreXmlBytes, StandardCharsets.UTF_8);
+ String retrievedUserStoreXmlString =
+ new String(retrievedUserStoreXmlBytes, StandardCharsets.UTF_8);
+ assertEquals("Retrieved: " + retrievedSharedStoreXmlString
+ + ", Expected: " + sharedStoreXmlString,
+ sharedStoreXmlString, retrievedSharedStoreXmlString);
+ assertEquals("Retrieved: " + retrievedUserStoreXmlString
+ + ", Expected: " + userStoreXmlString,
+ userStoreXmlString, retrievedUserStoreXmlString);
+ }
+
+ /**
+ * Verify that XML with corrupted version provided to WifiConfigStoreData is ignored correctly.
+ */
+ @Test
+ public void testCorruptVersionConfigStoreData() {
+ String storeDataAsString =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<WifiConfigStoreData>\n"
+ + "<int name=\"Version\" value=\"200\" />\n"
+ + "</WifiConfigStoreData>\n";
+ byte[] rawData = storeDataAsString.getBytes();
+ try {
+ WifiConfigStoreData storeData = WifiConfigStoreData.parseRawData(rawData, rawData);
+ } catch (Exception e) {
+ return;
+ }
+ // Assert if there was no exception was raised.
+ fail();
+ }
+
+ /**
+ * Verify that XML with no network list provided to WifiConfigStoreData is ignored correctly.
+ */
+ @Test
+ public void testCorruptNetworkListConfigStoreData() {
+ String storeDataAsString =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<WifiConfigStoreData>\n"
+ + "<int name=\"Version\" value=\"1\" />\n"
+ + "</WifiConfigStoreData>\n";
+ byte[] rawData = storeDataAsString.getBytes();
+ try {
+ WifiConfigStoreData storeData = WifiConfigStoreData.parseRawData(rawData, rawData);
+ } catch (Exception e) {
+ return;
+ }
+ // Assert if there was no exception was raised.
+ fail();
+ }
+
+ /**
+ * Verify that any corrupted data provided to WifiConfigStoreData is ignored correctly.
+ */
+ @Test
+ public void testRandomCorruptConfigStoreData() {
+ Random random = new Random();
+ byte[] rawData = new byte[100];
+ random.nextBytes(rawData);
+ try {
+ WifiConfigStoreData storeData = WifiConfigStoreData.parseRawData(rawData, rawData);
+ } catch (Exception e) {
+ return;
+ }
+ // Assert if there was no exception was raised.
+ fail();
+ }
+
+ /**
+ * Helper method to add 4 networks with different credential types, IpConfiguration
+ * types for all tests in the class.
+ *
+ * @return
+ */
+ private List<WifiConfiguration> createNetworks() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+
+ WifiConfiguration wepNetwork = WifiConfigurationTestUtil.createWepNetwork();
+ wepNetwork.setIpConfiguration(WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy());
+ wepNetwork.getNetworkSelectionStatus().setNetworkSelectionStatus(
+ NetworkSelectionStatus.NETWORK_SELECTION_ENABLED);
+ configurations.add(wepNetwork);
+
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ pskNetwork.setIpConfiguration(WifiConfigurationTestUtil.createStaticIpConfigurationWithPacProxy());
+ pskNetwork.getNetworkSelectionStatus().setNetworkSelectionStatus(
+ NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED);
+ pskNetwork.getNetworkSelectionStatus().setNetworkSelectionDisableReason(
+ NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION);
+ configurations.add(pskNetwork);
+
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ openNetwork.setIpConfiguration(WifiConfigurationTestUtil.createStaticIpConfigurationWithStaticProxy());
+ openNetwork.getNetworkSelectionStatus().setNetworkSelectionStatus(
+ NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
+ openNetwork.getNetworkSelectionStatus().setNetworkSelectionDisableReason(
+ NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER);
+ configurations.add(openNetwork);
+
+ WifiConfiguration eapNetwork = WifiConfigurationTestUtil.createEapNetwork();
+ eapNetwork.setIpConfiguration(WifiConfigurationTestUtil.createPartialStaticIpConfigurationWithPacProxy());
+ eapNetwork.getNetworkSelectionStatus().setConnectChoice(TEST_CONNECT_CHOICE);
+ eapNetwork.getNetworkSelectionStatus().setConnectChoiceTimestamp(
+ TEST_CONNECT_CHOICE_TIMESTAMP);
+ eapNetwork.getNetworkSelectionStatus().setHasEverConnected(true);
+ configurations.add(eapNetwork);
+
+ return configurations;
+ }
+
+ private List<WifiConfiguration> createNetworks(boolean shared) {
+ List<WifiConfiguration> configurations = createNetworks();
+ for (WifiConfiguration config : configurations) {
+ config.shared = shared;
+ }
+ return configurations;
+ }
+
+ /**
+ * Helper method to serialize/deserialize store data.
+ */
+ private void serializeDeserializeConfigStoreData(List<WifiConfiguration> configurations)
+ throws XmlPullParserException, IOException {
+ serializeDeserializeConfigStoreData(configurations, false, false);
+ }
+
+ /**
+ * Helper method to ensure the the provided config store data is serialized/deserialized
+ * correctly.
+ * This method serialize the provided config store data instance to raw bytes in XML format
+ * and then deserialzes the raw bytes back to a config store data instance. It then
+ * compares that the original config store data matches with the deserialzed instance.
+ *
+ * @param configurations list of configurations to be added in the store data instance.
+ * @param setSharedDataNull whether to set the shared data to null to simulate the non-existence
+ * of the shared store file.
+ * @param setUserDataNull whether to set the user data to null to simulate the non-existence
+ * of the user store file.
+ */
+ private void serializeDeserializeConfigStoreData(
+ List<WifiConfiguration> configurations, boolean setSharedDataNull,
+ boolean setUserDataNull)
+ throws XmlPullParserException, IOException {
+ // Will not work if both the flags are set because then we need to ignore the configuration
+ // list as well.
+ assertFalse(setSharedDataNull & setUserDataNull);
+
+ Set<String> deletedEphemeralList;
+ if (setUserDataNull) {
+ deletedEphemeralList = new HashSet<>();
+ } else {
+ deletedEphemeralList = TEST_DELETED_EPHEMERAL_LIST;
+ }
+
+ // Serialize the data.
+ WifiConfigStoreData storeData =
+ new WifiConfigStoreData(configurations, deletedEphemeralList);
+
+ byte[] sharedDataBytes = null;
+ byte[] userDataBytes = null;
+ if (!setSharedDataNull) {
+ sharedDataBytes = storeData.createSharedRawData();
+ }
+ if (!setUserDataNull) {
+ userDataBytes = storeData.createUserRawData();
+ }
+
+ // Deserialize the data.
+ WifiConfigStoreData retrievedStoreData =
+ WifiConfigStoreData.parseRawData(sharedDataBytes, userDataBytes);
+ assertConfigStoreDataEqual(storeData, retrievedStoreData);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreNewTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreNewTest.java
new file mode 100644
index 0000000..462a229
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreNewTest.java
@@ -0,0 +1,248 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi;
+
+import static com.android.server.wifi.WifiConfigStoreDataTest.assertConfigStoreDataEqual;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.app.test.TestAlarmManager;
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+import android.os.test.TestLooper;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.wifi.WifiConfigStoreNew.StoreFile;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.WifiConfigStoreNew}.
+ */
+@SmallTest
+public class WifiConfigStoreNewTest {
+ // Test mocks
+ @Mock private Context mContext;
+ private TestAlarmManager mAlarmManager;
+ private TestLooper mLooper;
+ @Mock private Clock mClock;
+ private MockStoreFile mSharedStore;
+ private MockStoreFile mUserStore;
+
+ /**
+ * Test instance of WifiConfigStore.
+ */
+ private WifiConfigStoreNew mWifiConfigStore;
+
+ /**
+ * Setup mocks before the test starts.
+ */
+ private void setupMocks() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mAlarmManager = new TestAlarmManager();
+ mLooper = new TestLooper();
+ when(mContext.getSystemService(Context.ALARM_SERVICE))
+ .thenReturn(mAlarmManager.getAlarmManager());
+ mUserStore = new MockStoreFile();
+ mSharedStore = new MockStoreFile();
+ }
+
+ /**
+ * Setup the test environment.
+ */
+ @Before
+ public void setUp() throws Exception {
+ setupMocks();
+
+ mWifiConfigStore =
+ new WifiConfigStoreNew(
+ mContext, mLooper.getLooper(), mClock, mSharedStore, mUserStore);
+
+ // Enable verbose logging before tests.
+ mWifiConfigStore.enableVerboseLogging(true);
+ }
+
+ /**
+ * Called after each test
+ */
+ @After
+ public void cleanup() {
+ validateMockitoUsage();
+ }
+
+ /**
+ * Tests the write API with the force flag set to true.
+ * Expected behavior: This should trigger an immediate write to the store files and no alarms
+ * should be started.
+ */
+ @Test
+ public void testForceWrite() throws Exception {
+ mWifiConfigStore.write(true, getEmptyStoreData());
+
+ assertFalse(mAlarmManager.isPending(WifiConfigStoreNew.BUFFERED_WRITE_ALARM_TAG));
+ assertTrue(mSharedStore.isStoreWritten());
+ assertTrue(mUserStore.isStoreWritten());
+ }
+
+ /**
+ * Tests the write API with the force flag set to false.
+ * Expected behavior: This should set an alarm to write to the store files.
+ */
+ @Test
+ public void testBufferedWrite() throws Exception {
+ mWifiConfigStore.write(false, getEmptyStoreData());
+
+ assertTrue(mAlarmManager.isPending(WifiConfigStoreNew.BUFFERED_WRITE_ALARM_TAG));
+ assertFalse(mSharedStore.isStoreWritten());
+ assertFalse(mUserStore.isStoreWritten());
+
+ // Now send the alarm and ensure that the writes happen.
+ mAlarmManager.dispatch(WifiConfigStoreNew.BUFFERED_WRITE_ALARM_TAG);
+ mLooper.dispatchAll();
+ assertTrue(mSharedStore.isStoreWritten());
+ assertTrue(mUserStore.isStoreWritten());
+ }
+
+ /**
+ * Tests the force write after a buffered write.
+ * Expected behaviour: The force write should override the previous buffered write and stop the
+ * buffer write alarms.
+ */
+ @Test
+ public void testForceWriteAfterBufferedWrite() throws Exception {
+ WifiConfigStoreData bufferedStoreData = createSingleOpenNetworkStoreData();
+ mWifiConfigStore.write(false, bufferedStoreData);
+
+ assertTrue(mAlarmManager.isPending(WifiConfigStoreNew.BUFFERED_WRITE_ALARM_TAG));
+ assertFalse(mSharedStore.isStoreWritten());
+ assertFalse(mUserStore.isStoreWritten());
+
+ // Now send a force write and ensure that the writes have been performed and alarms have
+ // been stopped.
+ WifiConfigStoreData forcedStoreData = createSinglePskNetworkStoreData();
+ mWifiConfigStore.write(true, forcedStoreData);
+
+ assertFalse(mAlarmManager.isPending(WifiConfigStoreNew.BUFFERED_WRITE_ALARM_TAG));
+ assertTrue(mSharedStore.isStoreWritten());
+ assertTrue(mUserStore.isStoreWritten());
+
+ // Now deserialize the data and ensure that the configuration retrieved matches the force
+ // write data.
+ WifiConfigStoreData retrievedStoreData =
+ WifiConfigStoreData.parseRawData(
+ mSharedStore.getStoreBytes(), mUserStore.getStoreBytes());
+
+ assertConfigStoreDataEqual(forcedStoreData, retrievedStoreData);
+ }
+
+ /**
+ * Tests the read API behaviour when there is no file on the device.
+ * Expected behaviour: The read should return an empty store data instance when the file not
+ * found exception is raised.
+ */
+ @Test
+ public void testReadWithNoStoreFile() throws Exception {
+ // Reading the mock store without a write should simulate the file not found case because
+ // |readRawData| would return null.
+ WifiConfigStoreData readData = mWifiConfigStore.read();
+ assertConfigStoreDataEqual(getEmptyStoreData(), readData);
+ }
+
+ /**
+ * Tests the read API behaviour after a write to the store file.
+ * Expected behaviour: The read should return the same data that was last written.
+ */
+ @Test
+ public void testReadAfterWrite() throws Exception {
+ WifiConfigStoreData writeData = createSingleOpenNetworkStoreData();
+ mWifiConfigStore.write(true, writeData);
+ WifiConfigStoreData readData = mWifiConfigStore.read();
+
+ assertConfigStoreDataEqual(writeData, readData);
+ }
+
+ /**
+ * Returns an empty store data object.
+ */
+ private WifiConfigStoreData getEmptyStoreData() {
+ return new WifiConfigStoreData(new ArrayList<WifiConfiguration>(), new HashSet<String>());
+ }
+
+ /**
+ * Returns an store data object with a single open network.
+ */
+ private WifiConfigStoreData createSingleOpenNetworkStoreData() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ configurations.add(WifiConfigurationTestUtil.createOpenNetwork());
+ return new WifiConfigStoreData(configurations, new HashSet<String>());
+ }
+
+ /**
+ * Returns an store data object with a single psk network.
+ */
+ private WifiConfigStoreData createSinglePskNetworkStoreData() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ configurations.add(WifiConfigurationTestUtil.createPskNetwork());
+ return new WifiConfigStoreData(configurations, new HashSet<String>());
+ }
+
+ /**
+ * Mock Store File to redirect all file writes from WifiConfigStoreNew to local buffers.
+ * This can be used to examine the data output by WifiConfigStoreNew.
+ */
+ private class MockStoreFile extends StoreFile {
+ private byte[] mStoreBytes;
+ private boolean mStoreWritten;
+
+ public MockStoreFile() {
+ super(new File("MockStoreFile"));
+ }
+
+ @Override
+ public byte[] readRawData() {
+ return mStoreBytes;
+ }
+
+ @Override
+ public void storeRawDataToWrite(byte[] data) {
+ mStoreBytes = data;
+ mStoreWritten = false;
+ }
+
+ @Override
+ public void writeBufferedRawData() {
+ mStoreWritten = true;
+ }
+
+ public byte[] getStoreBytes() {
+ return mStoreBytes;
+ }
+
+ public boolean isStoreWritten() {
+ return mStoreWritten;
+ }
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java
index 7117c2a..39bfd16 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java
@@ -16,8 +16,22 @@
package com.android.server.wifi;
+import static org.junit.Assert.*;
+
+import android.net.IpConfiguration;
+import android.net.LinkAddress;
+import android.net.NetworkUtils;
+import android.net.ProxyInfo;
+import android.net.StaticIpConfiguration;
+import android.net.wifi.FakeKeys;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
import android.net.wifi.WifiEnterpriseConfig;
+import android.text.TextUtils;
+
+import java.net.InetAddress;
+import java.security.cert.X509Certificate;
+import java.util.List;
/**
* Helper for creating and populating WifiConfigurations in unit tests.
@@ -33,6 +47,45 @@
public static final int SECURITY_EAP = 1 << 2;
/**
+ * These values are used to describe ip configuration parameters for a network.
+ */
+ public static final int STATIC_IP_ASSIGNMENT = 0;
+ public static final int DHCP_IP_ASSIGNMENT = 1;
+ public static final int STATIC_PROXY_SETTING = 0;
+ public static final int PAC_PROXY_SETTING = 1;
+ public static final int NONE_PROXY_SETTING = 2;
+
+ /**
+ * These are constants used to generate predefined WifiConfiguration objects.
+ */
+ public static final int TEST_NETWORK_ID = -1;
+ public static final int TEST_UID = 1;
+ public static final String TEST_SSID = "WifiConfigurationTestUtilSSID_";
+ public static final String TEST_PSK = "WifiConfigurationTestUtilPsk";
+ public static final String[] TEST_WEP_KEYS =
+ {"WifiConfigurationTestUtilWep1", "WifiConfigurationTestUtilWep2",
+ "WifiConfigurationTestUtilWep3", "WifiConfigurationTestUtilWep3"};
+ public static final int TEST_WEP_TX_KEY_INDEX = 1;
+ public static final String TEST_FQDN = "WifiConfigurationTestUtilFQDN";
+ public static final String TEST_PROVIDER_FRIENDLY_NAME =
+ "WifiConfigurationTestUtilFriendlyName";
+ public static final String TEST_STATIC_IP_LINK_ADDRESS = "192.168.48.2";
+ public static final int TEST_STATIC_IP_LINK_PREFIX_LENGTH = 8;
+ public static final String TEST_STATIC_IP_GATEWAY_ADDRESS = "192.168.48.1";
+ public static final String[] TEST_STATIC_IP_DNS_SERVER_ADDRESSES =
+ new String[]{"192.168.48.1", "192.168.48.10"};
+ public static final String TEST_STATIC_PROXY_HOST = "192.168.48.1";
+ public static final int TEST_STATIC_PROXY_PORT = 8000;
+ public static final String TEST_STATIC_PROXY_EXCLUSION_LIST = "";
+ public static final String TEST_PAC_PROXY_LOCATION = "http://";
+ public static final String TEST_CA_CERT_ALIAS = "WifiConfigurationTestUtilCaCertAlias";
+
+ /**
+ * Index used to assign unique SSIDs for the generation of predefined WifiConfiguration objects.
+ */
+ private static int sNetworkIndex = 0;
+
+ /**
* Construct a {@link android.net.wifi.WifiConfiguration}.
* @param networkId the configuration's networkId
* @param uid the configuration's creator uid
@@ -78,10 +131,10 @@
WifiConfiguration config = generateWifiConfig(networkId, uid, ssid, shared, enabled, fqdn,
providerFriendlyName);
- if (security == SECURITY_NONE) {
+ if ((security == SECURITY_NONE) || ((security & SECURITY_WEP) != 0)) {
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
} else {
- if (((security & SECURITY_WEP) != 0) || ((security & SECURITY_PSK) != 0)) {
+ if ((security & SECURITY_PSK) != 0) {
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
}
@@ -92,4 +145,416 @@
}
return config;
}
+
+ /**
+ * Construct a {@link android.net.IpConfiguration }.
+ * @param ipAssignmentType One of {@link #STATIC_IP_ASSIGNMENT} or {@link #DHCP_IP_ASSIGNMENT}.
+ * @param proxySettingType One of {@link #STATIC_PROXY_SETTING} or {@link #PAC_PROXY_SETTING} or
+ * {@link #NONE_PROXY_SETTING}.
+ * @param linkAddress static ip address string.
+ * @param linkPrefixLength static ip address prefix length.
+ * @param gatewayAddress static gateway address.
+ * @param dnsServerAddresses list of dns servers for static ip configuration.
+ * @param proxyHost Static proxy server address.
+ * @param proxyPort Static proxy server port.
+ * @param proxyExclusionList Static proxy exclusion list.
+ * @param pacProxyPath Pac proxy server path.
+ * @return the constructed {@link android.net.IpConfiguration}
+ */
+ public static IpConfiguration generateIpConfig(
+ int ipAssignmentType, int proxySettingType, String linkAddress, int linkPrefixLength,
+ String gatewayAddress, String[] dnsServerAddresses, String proxyHost,
+ int proxyPort, String proxyExclusionList, String pacProxyPath) {
+ StaticIpConfiguration staticIpConfiguration = null;
+ ProxyInfo proxyInfo = null;
+ IpConfiguration.IpAssignment ipAssignment = IpConfiguration.IpAssignment.UNASSIGNED;
+ IpConfiguration.ProxySettings proxySettings = IpConfiguration.ProxySettings.UNASSIGNED;
+
+ if (ipAssignmentType == STATIC_IP_ASSIGNMENT) {
+ staticIpConfiguration = new StaticIpConfiguration();
+ if (!TextUtils.isEmpty(linkAddress)) {
+ LinkAddress linkAddr =
+ new LinkAddress(
+ NetworkUtils.numericToInetAddress(linkAddress), linkPrefixLength);
+ staticIpConfiguration.ipAddress = linkAddr;
+ }
+
+ if (!TextUtils.isEmpty(gatewayAddress)) {
+ InetAddress gatewayAddr =
+ NetworkUtils.numericToInetAddress(gatewayAddress);
+ staticIpConfiguration.gateway = gatewayAddr;
+ }
+ if (dnsServerAddresses != null) {
+ for (String dnsServerAddress : dnsServerAddresses) {
+ if (!TextUtils.isEmpty(dnsServerAddress)) {
+ staticIpConfiguration.dnsServers.add(
+ NetworkUtils.numericToInetAddress(dnsServerAddress));
+ }
+
+ }
+ }
+ ipAssignment = IpConfiguration.IpAssignment.STATIC;
+ } else if (ipAssignmentType == DHCP_IP_ASSIGNMENT) {
+ ipAssignment = IpConfiguration.IpAssignment.DHCP;
+ }
+
+ if (proxySettingType == STATIC_PROXY_SETTING) {
+ proxyInfo = new ProxyInfo(proxyHost, proxyPort, proxyExclusionList);
+ proxySettings = IpConfiguration.ProxySettings.STATIC;
+ } else if (proxySettingType == PAC_PROXY_SETTING) {
+ proxyInfo = new ProxyInfo(pacProxyPath);
+ proxySettings = IpConfiguration.ProxySettings.PAC;
+ } else if (proxySettingType == NONE_PROXY_SETTING) {
+ proxySettings = IpConfiguration.ProxySettings.NONE;
+ }
+ return new IpConfiguration(ipAssignment, proxySettings, staticIpConfiguration, proxyInfo);
+ }
+
+ /**
+ * Create a new SSID for the the network being created.
+ */
+ private static String createNewSSID() {
+ return "\"" + TEST_SSID + sNetworkIndex++ + "\"";
+ }
+
+ /**
+ * Helper methods to generate predefined WifiConfiguration objects of the required type. These
+ * use a static index to avoid duplicate configurations.
+ */
+ public static WifiConfiguration createOpenNetwork() {
+ return generateWifiConfig(TEST_NETWORK_ID, TEST_UID, createNewSSID(), true, true, null,
+ null, SECURITY_NONE);
+ }
+
+ public static WifiConfiguration createOpenHiddenNetwork() {
+ WifiConfiguration configuration = createOpenNetwork();
+ configuration.hiddenSSID = true;
+ return configuration;
+ }
+
+ public static WifiConfiguration createPskNetwork() {
+ WifiConfiguration configuration =
+ generateWifiConfig(TEST_NETWORK_ID, TEST_UID, createNewSSID(), true, true, null,
+ null, SECURITY_PSK);
+ configuration.preSharedKey = TEST_PSK;
+ return configuration;
+ }
+
+ public static WifiConfiguration createPskHiddenNetwork() {
+ WifiConfiguration configuration = createPskNetwork();
+ configuration.hiddenSSID = true;
+ return configuration;
+ }
+
+ public static WifiConfiguration createWepNetwork() {
+ WifiConfiguration configuration =
+ generateWifiConfig(TEST_NETWORK_ID, TEST_UID, createNewSSID(), true, true, null,
+ null, SECURITY_WEP);
+ configuration.wepKeys = TEST_WEP_KEYS;
+ configuration.wepTxKeyIndex = TEST_WEP_TX_KEY_INDEX;
+ return configuration;
+ }
+
+ public static WifiConfiguration createWepHiddenNetwork() {
+ WifiConfiguration configuration = createWepNetwork();
+ configuration.hiddenSSID = true;
+ return configuration;
+ }
+
+
+ public static WifiConfiguration createWepNetworkWithSingleKey() {
+ WifiConfiguration configuration =
+ generateWifiConfig(TEST_NETWORK_ID, TEST_UID, createNewSSID(), true, true, null,
+ null, SECURITY_WEP);
+ configuration.wepKeys[0] = TEST_WEP_KEYS[0];
+ configuration.wepTxKeyIndex = 0;
+ return configuration;
+ }
+
+
+ public static WifiConfiguration createEapNetwork() {
+ WifiConfiguration configuration =
+ generateWifiConfig(TEST_NETWORK_ID, TEST_UID, createNewSSID(), true, true,
+ TEST_FQDN, TEST_PROVIDER_FRIENDLY_NAME, SECURITY_EAP);
+ return configuration;
+ }
+
+ public static IpConfiguration createStaticIpConfigurationWithPacProxy() {
+ return generateIpConfig(
+ STATIC_IP_ASSIGNMENT, PAC_PROXY_SETTING,
+ TEST_STATIC_IP_LINK_ADDRESS, TEST_STATIC_IP_LINK_PREFIX_LENGTH,
+ TEST_STATIC_IP_GATEWAY_ADDRESS, TEST_STATIC_IP_DNS_SERVER_ADDRESSES,
+ TEST_STATIC_PROXY_HOST, TEST_STATIC_PROXY_PORT, TEST_STATIC_PROXY_EXCLUSION_LIST,
+ TEST_PAC_PROXY_LOCATION);
+ }
+
+ public static IpConfiguration createStaticIpConfigurationWithStaticProxy() {
+ return generateIpConfig(
+ STATIC_IP_ASSIGNMENT, STATIC_PROXY_SETTING,
+ TEST_STATIC_IP_LINK_ADDRESS, TEST_STATIC_IP_LINK_PREFIX_LENGTH,
+ TEST_STATIC_IP_GATEWAY_ADDRESS, TEST_STATIC_IP_DNS_SERVER_ADDRESSES,
+ TEST_STATIC_PROXY_HOST, TEST_STATIC_PROXY_PORT, TEST_STATIC_PROXY_EXCLUSION_LIST,
+ TEST_PAC_PROXY_LOCATION);
+ }
+
+ public static IpConfiguration createPartialStaticIpConfigurationWithPacProxy() {
+ return generateIpConfig(
+ STATIC_IP_ASSIGNMENT, PAC_PROXY_SETTING,
+ TEST_STATIC_IP_LINK_ADDRESS, TEST_STATIC_IP_LINK_PREFIX_LENGTH,
+ null, null,
+ TEST_STATIC_PROXY_HOST, TEST_STATIC_PROXY_PORT, TEST_STATIC_PROXY_EXCLUSION_LIST,
+ TEST_PAC_PROXY_LOCATION);
+ }
+
+ public static IpConfiguration createDHCPIpConfigurationWithPacProxy() {
+ return generateIpConfig(
+ DHCP_IP_ASSIGNMENT, PAC_PROXY_SETTING,
+ TEST_STATIC_IP_LINK_ADDRESS, TEST_STATIC_IP_LINK_PREFIX_LENGTH,
+ TEST_STATIC_IP_GATEWAY_ADDRESS, TEST_STATIC_IP_DNS_SERVER_ADDRESSES,
+ TEST_STATIC_PROXY_HOST, TEST_STATIC_PROXY_PORT, TEST_STATIC_PROXY_EXCLUSION_LIST,
+ TEST_PAC_PROXY_LOCATION);
+ }
+
+ public static IpConfiguration createDHCPIpConfigurationWithStaticProxy() {
+ return generateIpConfig(
+ DHCP_IP_ASSIGNMENT, STATIC_PROXY_SETTING,
+ TEST_STATIC_IP_LINK_ADDRESS, TEST_STATIC_IP_LINK_PREFIX_LENGTH,
+ TEST_STATIC_IP_GATEWAY_ADDRESS, TEST_STATIC_IP_DNS_SERVER_ADDRESSES,
+ TEST_STATIC_PROXY_HOST, TEST_STATIC_PROXY_PORT, TEST_STATIC_PROXY_EXCLUSION_LIST,
+ TEST_PAC_PROXY_LOCATION);
+ }
+
+ public static IpConfiguration createDHCPIpConfigurationWithNoProxy() {
+ return generateIpConfig(
+ DHCP_IP_ASSIGNMENT, NONE_PROXY_SETTING,
+ TEST_STATIC_IP_LINK_ADDRESS, TEST_STATIC_IP_LINK_PREFIX_LENGTH,
+ TEST_STATIC_IP_GATEWAY_ADDRESS, TEST_STATIC_IP_DNS_SERVER_ADDRESSES,
+ TEST_STATIC_PROXY_HOST, TEST_STATIC_PROXY_PORT, TEST_STATIC_PROXY_EXCLUSION_LIST,
+ TEST_PAC_PROXY_LOCATION);
+ }
+
+ // TODO: These enterprise configurations may need more parameters set.
+ public static WifiEnterpriseConfig createPEAPWifiEnterpriseConfigWithGTCPhase2() {
+ WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+ config.setEapMethod(WifiEnterpriseConfig.Eap.PEAP);
+ config.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC);
+ config.setCaCertificateAliases(new String[] {TEST_CA_CERT_ALIAS + "PEAP"});
+ config.setCaCertificates(new X509Certificate[] {FakeKeys.CA_CERT0, FakeKeys.CA_CERT1});
+ return config;
+ }
+
+ public static WifiEnterpriseConfig createTLSWifiEnterpriseConfigWithNonePhase2() {
+ WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+ config.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ config.setPhase2Method(WifiEnterpriseConfig.Phase2.NONE);
+ config.setCaCertificateAliases(new String[] {TEST_CA_CERT_ALIAS + "TLS"});
+ config.setCaCertificates(new X509Certificate[] {FakeKeys.CA_CERT0, FakeKeys.CA_CERT1});
+ return config;
+ }
+
+ /**
+ * Asserts that the 2 WifiConfigurations are equal in the elements saved for both backup/restore
+ * and config store.
+ */
+ private static void assertCommonConfigurationElementsEqual(
+ WifiConfiguration expected, WifiConfiguration actual) {
+ assertEquals(expected.SSID, actual.SSID);
+ assertEquals(expected.BSSID, actual.BSSID);
+ assertEquals(expected.preSharedKey, actual.preSharedKey);
+ assertEquals(expected.wepKeys, actual.wepKeys);
+ assertEquals(expected.wepTxKeyIndex, actual.wepTxKeyIndex);
+ assertEquals(expected.hiddenSSID, actual.hiddenSSID);
+ assertEquals(expected.allowedKeyManagement, actual.allowedKeyManagement);
+ assertEquals(expected.allowedProtocols, actual.allowedProtocols);
+ assertEquals(expected.allowedAuthAlgorithms, actual.allowedAuthAlgorithms);
+ assertEquals(expected.shared, actual.shared);
+ assertEquals(expected.getIpConfiguration(), actual.getIpConfiguration());
+ }
+
+ /**
+ * Asserts that the 2 WifiConfigurations are equal. This only compares the elements saved
+ * fpr backup/restore.
+ */
+ public static void assertConfigurationEqualForBackup(
+ WifiConfiguration expected, WifiConfiguration actual) {
+ assertCommonConfigurationElementsEqual(expected, actual);
+ }
+
+ /**
+ * Asserts that the 2 WifiConfigurations are equal. This compares all the elements saved for
+ * config store.
+ */
+ public static void assertConfigurationEqualForConfigStore(
+ WifiConfiguration expected, WifiConfiguration actual) {
+ assertCommonConfigurationElementsEqual(expected, actual);
+ assertEquals(expected.FQDN, actual.FQDN);
+ assertEquals(expected.providerFriendlyName, actual.providerFriendlyName);
+ assertEquals(expected.linkedConfigurations, actual.linkedConfigurations);
+ assertEquals(expected.defaultGwMacAddress, actual.defaultGwMacAddress);
+ assertEquals(expected.validatedInternetAccess, actual.validatedInternetAccess);
+ assertEquals(expected.noInternetAccessExpected, actual.noInternetAccessExpected);
+ assertEquals(expected.userApproved, actual.userApproved);
+ assertEquals(expected.meteredHint, actual.meteredHint);
+ assertEquals(expected.useExternalScores, actual.useExternalScores);
+ assertEquals(expected.numAssociation, actual.numAssociation);
+ assertEquals(expected.creatorUid, actual.creatorUid);
+ assertEquals(expected.creatorName, actual.creatorName);
+ assertEquals(expected.creationTime, actual.creationTime);
+ assertEquals(expected.lastUpdateUid, actual.lastUpdateUid);
+ assertEquals(expected.lastUpdateName, actual.lastUpdateName);
+ assertEquals(expected.lastConnectUid, actual.lastConnectUid);
+ assertEquals(expected.updateTime, actual.updateTime);
+ assertNetworkSelectionStatusEqualForConfigStore(
+ expected.getNetworkSelectionStatus(), actual.getNetworkSelectionStatus());
+ assertWifiEnterpriseConfigEqualForConfigStore(
+ expected.enterpriseConfig, actual.enterpriseConfig);
+ }
+
+ /**
+ * Asserts that the 2 WifiConfigurations are equal. This compares all the elements that are
+ * saved into internal database by WifiConfigurationManager for network additions/updates.
+ */
+ public static void assertConfigurationEqualForConfigManagerAddOrUpdate(
+ WifiConfiguration expected, WifiConfiguration actual) {
+ assertCommonConfigurationElementsEqual(expected, actual);
+ assertEquals(expected.FQDN, actual.FQDN);
+ assertEquals(expected.providerFriendlyName, actual.providerFriendlyName);
+ assertEquals(expected.noInternetAccessExpected, actual.noInternetAccessExpected);
+ assertEquals(expected.meteredHint, actual.meteredHint);
+ assertEquals(expected.useExternalScores, actual.useExternalScores);
+ assertEquals(expected.ephemeral, actual.ephemeral);
+ assertEquals(expected.creatorUid, actual.creatorUid);
+ assertEquals(expected.creatorName, actual.creatorName);
+ assertEquals(expected.creationTime, actual.creationTime);
+ assertEquals(expected.lastUpdateUid, actual.lastUpdateUid);
+ assertEquals(expected.lastUpdateName, actual.lastUpdateName);
+ assertEquals(expected.updateTime, actual.updateTime);
+ assertNetworkSelectionStatusEqualForConfigStore(
+ expected.getNetworkSelectionStatus(), actual.getNetworkSelectionStatus());
+ assertWifiEnterpriseConfigEqualForConfigStore(
+ expected.enterpriseConfig, actual.enterpriseConfig);
+ }
+
+ /**
+ * Assert that the 2 NetworkSelectionStatus's are equal. This compares all the elements saved
+ * for config store.
+ */
+ public static void assertNetworkSelectionStatusEqualForConfigStore(
+ NetworkSelectionStatus expected, NetworkSelectionStatus actual) {
+ if (expected.isNetworkTemporaryDisabled()) {
+ // Temporarily disabled networks are enabled when persisted.
+ assertEquals(
+ NetworkSelectionStatus.NETWORK_SELECTION_ENABLED,
+ actual.getNetworkSelectionStatus());
+ } else {
+ assertEquals(expected.getNetworkSelectionStatus(), actual.getNetworkSelectionStatus());
+ }
+ assertEquals(
+ expected.getNetworkSelectionDisableReason(),
+ actual.getNetworkSelectionDisableReason());
+ assertEquals(expected.getConnectChoice(), actual.getConnectChoice());
+ assertEquals(expected.getConnectChoiceTimestamp(), actual.getConnectChoiceTimestamp());
+ assertEquals(expected.getHasEverConnected(), actual.getHasEverConnected());
+ }
+
+ /**
+ * Assert that the 2 WifiEnterpriseConfig's are equal. This compares all the elements saved
+ * for config store.
+ */
+ public static void assertWifiEnterpriseConfigEqualForConfigStore(
+ WifiEnterpriseConfig expected, WifiEnterpriseConfig actual) {
+ assertEquals(expected.getFieldValue(WifiEnterpriseConfig.IDENTITY_KEY, ""),
+ actual.getFieldValue(WifiEnterpriseConfig.IDENTITY_KEY, ""));
+ assertEquals(expected.getFieldValue(WifiEnterpriseConfig.ANON_IDENTITY_KEY, ""),
+ actual.getFieldValue(WifiEnterpriseConfig.ANON_IDENTITY_KEY, ""));
+ assertEquals(expected.getFieldValue(WifiEnterpriseConfig.PASSWORD_KEY, ""),
+ actual.getFieldValue(WifiEnterpriseConfig.PASSWORD_KEY, ""));
+ assertEquals(expected.getFieldValue(WifiEnterpriseConfig.CLIENT_CERT_KEY, ""),
+ actual.getFieldValue(WifiEnterpriseConfig.CLIENT_CERT_KEY, ""));
+ assertEquals(expected.getFieldValue(WifiEnterpriseConfig.CA_CERT_KEY, ""),
+ actual.getFieldValue(WifiEnterpriseConfig.CA_CERT_KEY, ""));
+ assertEquals(expected.getFieldValue(WifiEnterpriseConfig.SUBJECT_MATCH_KEY, ""),
+ actual.getFieldValue(WifiEnterpriseConfig.SUBJECT_MATCH_KEY, ""));
+ assertEquals(expected.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY, ""),
+ actual.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY, ""));
+ assertEquals(expected.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, ""),
+ actual.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, ""));
+ assertEquals(expected.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, ""),
+ actual.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, ""));
+ assertEquals(expected.getFieldValue(WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY, ""),
+ actual.getFieldValue(WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY, ""));
+ assertEquals(expected.getFieldValue(WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY, ""),
+ actual.getFieldValue(WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY, ""));
+ assertEquals(expected.getFieldValue(WifiEnterpriseConfig.CA_PATH_KEY, ""),
+ actual.getFieldValue(WifiEnterpriseConfig.CA_PATH_KEY, ""));
+ assertEquals(expected.getEapMethod(), actual.getEapMethod());
+ assertEquals(expected.getPhase2Method(), actual.getPhase2Method());
+ }
+
+ /**
+ * Asserts that the 2 lists of WifiConfigurations are equal. This compares all the elements
+ * saved for backup/restore.
+ */
+ public static void assertConfigurationsEqualForBackup(
+ List<WifiConfiguration> expected, List<WifiConfiguration> actual) {
+ assertEquals(expected.size(), actual.size());
+ for (WifiConfiguration expectedConfiguration : expected) {
+ String expectedConfigKey = expectedConfiguration.configKey();
+ boolean didCompare = false;
+ for (WifiConfiguration actualConfiguration : actual) {
+ String actualConfigKey = actualConfiguration.configKey();
+ if (actualConfigKey.equals(expectedConfigKey)) {
+ assertConfigurationEqualForBackup(
+ expectedConfiguration, actualConfiguration);
+ didCompare = true;
+ }
+ }
+ assertTrue(didCompare);
+ }
+ }
+
+ /**
+ * Asserts that the 2 lists of WifiConfigurations are equal. This compares all the elements
+ * that are saved into internal database by WifiConfigurationManager for network
+ * additions/updates.
+ */
+ public static void assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ List<WifiConfiguration> expected, List<WifiConfiguration> actual) {
+ assertEquals(expected.size(), actual.size());
+ for (WifiConfiguration expectedConfiguration : expected) {
+ String expectedConfigKey = expectedConfiguration.configKey();
+ boolean didCompare = false;
+ for (WifiConfiguration actualConfiguration : actual) {
+ String actualConfigKey = actualConfiguration.configKey();
+ if (actualConfigKey.equals(expectedConfigKey)) {
+ assertConfigurationEqualForConfigManagerAddOrUpdate(
+ expectedConfiguration, actualConfiguration);
+ didCompare = true;
+ }
+ }
+ assertTrue(didCompare);
+ }
+ }
+
+ /**
+ * Asserts that the 2 lists of WifiConfigurations are equal. This compares all the elements
+ * saved for config store.
+ */
+ public static void assertConfigurationsEqualForConfigStore(
+ List<WifiConfiguration> expected, List<WifiConfiguration> actual) {
+ assertEquals(expected.size(), actual.size());
+ for (WifiConfiguration expectedConfiguration : expected) {
+ String expectedConfigKey = expectedConfiguration.configKey();
+ boolean didCompare = false;
+ for (WifiConfiguration actualConfiguration : actual) {
+ String actualConfigKey = actualConfiguration.configKey();
+ if (actualConfigKey.equals(expectedConfigKey)) {
+ assertConfigurationEqualForConfigStore(
+ expectedConfiguration, actualConfiguration);
+ didCompare = true;
+ }
+ }
+ assertTrue(didCompare);
+ }
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
index 022997d..fbf1255 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
@@ -22,6 +22,8 @@
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
+import android.app.test.TestAlarmManager;
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
import android.content.Context;
import android.content.res.Resources;
import android.net.wifi.ScanResult;
@@ -38,10 +40,10 @@
import android.net.wifi.WifiSsid;
import android.os.SystemClock;
import android.os.WorkSource;
+import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.R;
-import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments;
import org.junit.After;
import org.junit.Before;
@@ -66,7 +68,7 @@
public void setUp() throws Exception {
mWifiInjector = mockWifiInjector();
mResource = mockResource();
- mAlarmManager = new MockAlarmManager();
+ mAlarmManager = new TestAlarmManager();
mContext = mockContext();
mWifiStateMachine = mockWifiStateMachine();
mWifiConfigManager = mockWifiConfigManager();
@@ -77,7 +79,7 @@
mWifiScanner, mWifiConfigManager, mWifiInfo, mWifiQNS, mWifiInjector,
mLooper.getLooper());
mWifiConnectivityManager.setWifiEnabled(true);
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime());
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime());
}
/**
@@ -90,8 +92,8 @@
private Resources mResource;
private Context mContext;
- private MockAlarmManager mAlarmManager;
- private MockLooper mLooper = new MockLooper();
+ private TestAlarmManager mAlarmManager;
+ private TestLooper mLooper = new TestLooper();
private WifiConnectivityManager mWifiConnectivityManager;
private WifiQualifiedNetworkSelector mWifiQNS;
private WifiStateMachine mWifiStateMachine;
@@ -203,8 +205,8 @@
candidateScanResult.BSSID = CANDIDATE_BSSID;
candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult);
- when(qns.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyObject(),
- anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(candidate);
+ when(qns.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyBoolean(),
+ anyBoolean(), anyBoolean(), anyBoolean(), anyObject())).thenReturn(candidate);
return qns;
}
@@ -372,7 +374,7 @@
long currentTimeStamp = 0;
for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
currentTimeStamp += connectionAttemptIntervals;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Set WiFi to disconnected state to trigger PNO scan
mWifiConnectivityManager.handleConnectionStateChanged(
WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
@@ -380,7 +382,7 @@
}
// Now trigger another connection attempt before the rate interval, this should be
// skipped because we've crossed rate limit.
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Set WiFi to disconnected state to trigger PNO scan
mWifiConnectivityManager.handleConnectionStateChanged(
WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
@@ -411,7 +413,7 @@
long currentTimeStamp = 0;
for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
currentTimeStamp += connectionAttemptIntervals;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Set WiFi to disconnected state to trigger PNO scan
mWifiConnectivityManager.handleConnectionStateChanged(
WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
@@ -419,7 +421,7 @@
}
// Now trigger another connection attempt after the rate interval, this should not be
// skipped because we should've evicted the older attempt.
- when(mClock.elapsedRealtime()).thenReturn(
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(
currentTimeStamp + connectionAttemptIntervals * 2);
// Set WiFi to disconnected state to trigger PNO scan
mWifiConnectivityManager.handleConnectionStateChanged(
@@ -451,7 +453,7 @@
long currentTimeStamp = 0;
for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
currentTimeStamp += connectionAttemptIntervals;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Set WiFi to disconnected state to trigger PNO scan
mWifiConnectivityManager.handleConnectionStateChanged(
WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
@@ -462,7 +464,7 @@
for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
currentTimeStamp += connectionAttemptIntervals;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Set WiFi to disconnected state to trigger PNO scan
mWifiConnectivityManager.handleConnectionStateChanged(
WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
@@ -483,8 +485,8 @@
*/
@Test
public void PnoRetryForLowRssiNetwork() {
- when(mWifiQNS.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyObject(),
- anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(null);
+ when(mWifiQNS.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyBoolean(),
+ anyBoolean(), anyBoolean(), anyBoolean(), anyObject())).thenReturn(null);
// Set screen to off
mWifiConnectivityManager.handleScreenStateChanged(false);
@@ -538,8 +540,8 @@
@Test
public void watchdogBitePnoGoodIncrementsMetrics() {
// Qns returns no candidate after watchdog single scan.
- when(mWifiQNS.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyObject(),
- anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(null);
+ when(mWifiQNS.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyBoolean(),
+ anyBoolean(), anyBoolean(), anyBoolean(), anyObject())).thenReturn(null);
// Set screen to off
mWifiConnectivityManager.handleScreenStateChanged(false);
@@ -566,7 +568,7 @@
@Test
public void checkPeriodicScanIntervalWhenDisconnected() {
long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Set screen to ON
mWifiConnectivityManager.handleScreenStateChanged(true);
@@ -574,7 +576,7 @@
// Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered
// by screen state change can settle
currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Set WiFi to disconnected state to trigger periodic scan
mWifiConnectivityManager.handleConnectionStateChanged(
@@ -587,7 +589,7 @@
assertEquals(firstIntervalMs, WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS);
currentTimeStamp += firstIntervalMs;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Now fire the first periodic scan alarm timer
mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
@@ -602,7 +604,7 @@
assertEquals(firstIntervalMs * 2, secondIntervalMs);
currentTimeStamp += secondIntervalMs;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Make sure we eventually stay at the maximum scan interval.
long intervalMs = 0;
@@ -613,7 +615,7 @@
.getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
- currentTimeStamp;
currentTimeStamp += intervalMs;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
}
assertEquals(intervalMs, WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS);
@@ -629,7 +631,7 @@
@Test
public void checkPeriodicScanIntervalWhenConnected() {
long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Set screen to ON
mWifiConnectivityManager.handleScreenStateChanged(true);
@@ -637,7 +639,7 @@
// Wait for MAX_PERIODIC_SCAN_INTERVAL_MS so that any impact triggered
// by screen state change can settle
currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Set WiFi to connected state to trigger periodic scan
mWifiConnectivityManager.handleConnectionStateChanged(
@@ -650,7 +652,7 @@
assertEquals(firstIntervalMs, WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS);
currentTimeStamp += firstIntervalMs;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Now fire the first periodic scan alarm timer
mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
@@ -665,7 +667,7 @@
assertEquals(firstIntervalMs * 2, secondIntervalMs);
currentTimeStamp += secondIntervalMs;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Make sure we eventually stay at the maximum scan interval.
long intervalMs = 0;
@@ -676,7 +678,7 @@
.getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
- currentTimeStamp;
currentTimeStamp += intervalMs;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
}
assertEquals(intervalMs, WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS);
@@ -692,7 +694,7 @@
@Test
public void checkMinimumPeriodicScanIntervalWhenScreenOn() {
long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Set screen to ON
mWifiConnectivityManager.handleScreenStateChanged(true);
@@ -701,7 +703,7 @@
// by screen state change can settle
currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS;
long firstScanTimeStamp = currentTimeStamp;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Set WiFi to connected state to trigger the periodic scan
mWifiConnectivityManager.handleConnectionStateChanged(
@@ -709,7 +711,7 @@
// Set the second scan attempt time stamp.
currentTimeStamp += 2000;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Set WiFi to disconnected state to trigger another periodic scan
mWifiConnectivityManager.handleConnectionStateChanged(
@@ -737,7 +739,7 @@
@Test
public void checkMinimumPeriodicScanIntervalNotEnforced() {
long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Set screen to ON
mWifiConnectivityManager.handleScreenStateChanged(true);
@@ -746,7 +748,7 @@
// by screen state change can settle
currentTimeStamp += WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS;
long firstScanTimeStamp = currentTimeStamp;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Set WiFi to connected state to trigger the periodic scan
mWifiConnectivityManager.handleConnectionStateChanged(
@@ -754,7 +756,7 @@
// Set the second scan attempt time stamp
currentTimeStamp += 2000;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Force a connectivity scan
mWifiConnectivityManager.forceConnectivityScan();
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java
index 8f1b23e..b10266e 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java
@@ -33,6 +33,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.os.WorkSource;
+import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
@@ -78,7 +79,7 @@
when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true);
}
- MockLooper mLooper;
+ TestLooper mLooper;
@Mock Context mContext;
@Mock WifiServiceImpl mService;
@Mock FrameworkFacade mFacade;
@@ -92,7 +93,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mLooper = new MockLooper();
+ mLooper = new TestLooper();
initializeSettingsStore();
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiInjectorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiInjectorTest.java
new file mode 100644
index 0000000..a0a1832
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiInjectorTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi;
+
+import static org.mockito.Mockito.*;
+
+import android.content.Context;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Unit tests for {@link WifiInjector}. */
+@SmallTest
+public class WifiInjectorTest {
+
+ @Mock private Context mContext;
+ private WifiInjector mInjector;
+
+ /**
+ * Method to initialize mocks for tests.
+ */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ /**
+ * Test that attempting to get the instance of the WifiInjector throws an IllegalStateException
+ * if it is not initialized.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testGetInstanceWithUninitializedWifiInjector() {
+ WifiInjector.getInstance();
+ }
+
+ /**
+ * Test that attempting to call the WifiInjector a second time throws an exception.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testShouldNotBeAbleToCreateMoreThanOneWifiInjector() {
+ try {
+ WifiInjector willThrowNullPointerException = new WifiInjector(mContext);
+ } catch (NullPointerException e) {
+ }
+ WifiInjector shouldThrowIllegalStateException = new WifiInjector(mContext);
+ }
+
+ /**
+ * Test that a WifiInjector cannot be created with a null Context.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testShouldNotCreateWifiInjectorWithNullContext() {
+ new WifiInjector(null);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java b/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java
index 237fc66..a8b1614 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java
@@ -55,8 +55,7 @@
@Before
public void setUp() throws Exception {
initMocks(this);
- mLastResortWatchdog = new WifiLastResortWatchdog(mWifiMetrics);
- mLastResortWatchdog.setWifiController(mWifiController);
+ mLastResortWatchdog = new WifiLastResortWatchdog(mWifiController, mWifiMetrics);
}
private List<Pair<ScanDetail, WifiConfiguration>> createFilteredQnsCandidates(String[] ssids,
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiLoggerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiLoggerTest.java
index d915ff3..b70e1dd 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiLoggerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiLoggerTest.java
@@ -16,6 +16,7 @@
package com.android.server.wifi;
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
import android.content.Context;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.R;
@@ -33,8 +34,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments;
-
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
index 011682b..d011c54 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
@@ -50,7 +50,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mDeserializedWifiMetrics = null;
- when(mClock.elapsedRealtime()).thenReturn((long) 0);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 0);
mWifiMetrics = new WifiMetrics(mClock);
}
@@ -89,7 +89,7 @@
PrintWriter writer = new PrintWriter(stream);
String[] args = new String[0];
- when(mClock.elapsedRealtime()).thenReturn(TEST_RECORD_DURATION_MILLIS);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(TEST_RECORD_DURATION_MILLIS);
//Test proto dump, by passing in proto arg option
args = new String[]{WifiMetrics.PROTO_DUMP_ARG};
mWifiMetrics.dump(null, writer, args);
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMulticastLockManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMulticastLockManagerTest.java
new file mode 100644
index 0000000..02150d7
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiMulticastLockManagerTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi;
+
+import static org.mockito.Mockito.*;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.app.IBatteryStats;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.WifiConfigStoreData}.
+ */
+@SmallTest
+public class WifiMulticastLockManagerTest {
+ @Mock WifiMulticastLockManager.FilterController mHandler;
+ @Mock IBatteryStats mBatteryStats;
+ WifiMulticastLockManager mManager;
+
+ /**
+ * Initialize |WifiMulticastLockManager| instance before each test.
+ */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mManager = new WifiMulticastLockManager(mHandler, mBatteryStats);
+ }
+
+ /**
+ * Test behavior when no locks are held.
+ */
+ @Test
+ public void noLocks() {
+ assertFalse(mManager.isMulticastEnabled());
+ mManager.initializeFiltering();
+ verify(mHandler, times(1)).startFilteringMulticastPackets();
+ }
+
+ /**
+ * Test behavior when one lock is aquired then released.
+ */
+ @Test
+ public void oneLock() throws RemoteException {
+ IBinder binder = mock(IBinder.class);
+ mManager.acquireLock(binder, "Test");
+ assertTrue(mManager.isMulticastEnabled());
+ verify(mHandler).stopFilteringMulticastPackets();
+ mManager.initializeFiltering();
+ verify(mHandler, times(0)).startFilteringMulticastPackets();
+ verify(mBatteryStats).noteWifiMulticastEnabled(anyInt());
+ verify(mBatteryStats, times(0)).noteWifiMulticastDisabled(anyInt());
+
+ mManager.releaseLock();
+ verify(mBatteryStats).noteWifiMulticastDisabled(anyInt());
+ assertFalse(mManager.isMulticastEnabled());
+ }
+}
+
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNotificationControllerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNotificationControllerTest.java
index 41e4e46..c3fcc96 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNotificationControllerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNotificationControllerTest.java
@@ -34,6 +34,7 @@
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.UserHandle;
+import android.os.test.TestLooper;
import android.provider.Settings;
import android.test.suitebuilder.annotation.SmallTest;
@@ -79,7 +80,7 @@
when(mFrameworkFacade.getIntegerSetting(mContext,
Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1)).thenReturn(1);
- MockLooper mock_looper = new MockLooper();
+ TestLooper mock_looper = new TestLooper();
mWifiNotificationController = new WifiNotificationController(
mContext, mock_looper.getLooper(), mWifiStateMachine, mFrameworkFacade,
mock(Notification.Builder.class));
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiQualifiedNetworkSelectorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiQualifiedNetworkSelectorTest.java
index 64fee84..79dfffa 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiQualifiedNetworkSelectorTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiQualifiedNetworkSelectorTest.java
@@ -32,6 +32,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
import android.content.Context;
import android.content.res.Resources;
import android.net.NetworkScoreManager;
@@ -39,13 +40,13 @@
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
import android.net.wifi.WifiSsid;
import android.os.SystemClock;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.LocalLog;
import com.android.internal.R;
-import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments;
import org.junit.After;
import org.junit.Before;
@@ -76,9 +77,9 @@
mWifiQualifiedNetworkSelector = new WifiQualifiedNetworkSelector(mWifiConfigManager,
mContext, mWifiInfo, mClock);
mWifiQualifiedNetworkSelector.enableVerboseLogging(1);
- mWifiQualifiedNetworkSelector.setUserPreferredBand(1);
+ mWifiQualifiedNetworkSelector.setUserPreferredBand(WifiManager.WIFI_FREQUENCY_BAND_AUTO);
mWifiQualifiedNetworkSelector.setWifiNetworkScoreCache(mScoreCache);
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime());
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime());
}
@After
@@ -102,7 +103,7 @@
private List<ScanDetail> getScanDetails(String[] ssids, String[] bssids, int[] frequencies,
String[] caps, int[] levels) {
List<ScanDetail> scanDetailList = new ArrayList<ScanDetail>();
- long timeStamp = mClock.elapsedRealtime();
+ long timeStamp = mClock.getElapsedSinceBootMillis();
for (int index = 0; index < ssids.length; index++) {
ScanDetail scanDetail = new ScanDetail(WifiSsid.createFromAsciiEncoded(ssids[index]),
bssids[index], caps[index], levels[index], frequencies[index], timeStamp, 0);
@@ -342,7 +343,7 @@
ScanResult chosenScanResult = scanDetails.get(scanDetails.size() - 1).getScanResult();
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
+ false, false, false, true, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -379,7 +380,7 @@
ScanResult chosenScanResult = scanDetails.get(scanDetails.size() - 1).getScanResult();
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
+ false, false, false, true, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -415,7 +416,7 @@
ScanResult chosenScanResult = scanDetails.get(scanDetails.size() - 1).getScanResult();
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
+ false, false, false, true, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -451,8 +452,9 @@
scanResultLinkConfiguration(savedConfigs, scanDetails);
ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
+ mWifiQualifiedNetworkSelector.setUserPreferredBand(WifiManager.WIFI_FREQUENCY_BAND_AUTO);
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
+ false, false, false, true, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -489,7 +491,7 @@
ScanResult chosenScanResult = scanDetails.get(scanDetails.size() - 1).getScanResult();
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
+ false, false, false, true, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -525,7 +527,7 @@
scanResultLinkConfiguration(savedConfigs, scanDetails);
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
+ false, false, false, true, false, scanDetails);
assertEquals("choose the wrong SSID", null, candidate);
}
@@ -561,7 +563,7 @@
ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
+ false, false, false, true, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -597,7 +599,7 @@
ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
+ false, false, false, true, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -632,7 +634,7 @@
ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
+ false, false, false, true, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -717,7 +719,7 @@
.thenReturn(configs[2]);
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
+ false, false, false, true, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -798,7 +800,7 @@
mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssids[1], false);
mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssids[1], false);
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
+ false, false, false, true, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -840,7 +842,7 @@
//re-enable it
mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssids[1], true);
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
+ false, false, false, true, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -884,10 +886,10 @@
}
//re-enable it
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime()
- + WifiQualifiedNetworkSelector.BSSID_BLACKLIST_EXPIRE_TIME);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
+ + WifiQualifiedNetworkSelector.BSSID_BLACKLIST_EXPIRE_TIME_MS);
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
+ false, false, false, true, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -926,7 +928,7 @@
savedConfigs[1].getNetworkSelectionStatus().setNetworkSelectionStatus(
WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED);
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
+ false, false, false, true, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -962,7 +964,7 @@
scanResultLinkConfiguration(savedConfigs, scanDetails);
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, true, false, true, false);
+ false, true, false, true, false, scanDetails);
assertEquals("choose the wrong network", null, candidate);
}
@@ -994,11 +996,11 @@
when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
scanResultLinkConfiguration(savedConfigs, scanDetails);
//first QNS
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
+ mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false,
+ false, true, false, scanDetails);
//immediately second QNS
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
+ false, false, false, true, false, scanDetails);
ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
verifySelectedResult(chosenScanResult, candidate);
@@ -1032,11 +1034,11 @@
when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
scanResultLinkConfiguration(savedConfigs, scanDetails);
//first QNS
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
+ mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false,
+ false, true, false, scanDetails);
//immediately second QNS
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
+ false, false, true, false, false, scanDetails);
ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
assertEquals("choose the wrong BSSID", null, candidate);
}
@@ -1068,11 +1070,11 @@
when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
scanResultLinkConfiguration(savedConfigs, scanDetails);
//first QNS
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
+ mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false,
+ false, true, false, scanDetails);
//immediately second QNS
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(true,
- false, scanDetails, false, true, false, false);
+ false, false, true, false, false, scanDetails);
ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
verifySelectedResult(chosenScanResult, candidate);
@@ -1105,7 +1107,7 @@
scanResultLinkConfiguration(savedConfigs, scanDetails);
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
+ false, false, true, false, false, scanDetails);
assertEquals("choose the wrong BSSID", null, candidate);
assertEquals("Should receive zero filteredScanDetails", 0,
mWifiQualifiedNetworkSelector.getFilteredScanDetails().size());
@@ -1140,20 +1142,20 @@
scanResultLinkConfiguration(savedConfigs, scanDetails);
//first time, connect to test2 due to 5GHz bonus
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
+ mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false,
+ false, true, false, scanDetails);
when(mWifiInfo.getNetworkId()).thenReturn(1);
when(mWifiInfo.getBSSID()).thenReturn(bssids[1]);
when(mWifiInfo.is24GHz()).thenReturn(false);
when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
levels[0] = -50; // if there is QNS, test1 will be chosen
scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
scanResultLinkConfiguration(savedConfigs, scanDetails);
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
+ false, false, true, false, false, scanDetails);
assertEquals("choose the wrong BSSID", null, candidate);
}
@@ -1184,16 +1186,16 @@
scanResultLinkConfiguration(savedConfigs, scanDetails);
//first connect to test2 due to 5GHz bonus
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
+ mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false,
+ false, true, false, scanDetails);
when(mWifiInfo.getNetworkId()).thenReturn(1);
when(mWifiInfo.getBSSID()).thenReturn(bssids[1]);
when(mWifiInfo.is24GHz()).thenReturn(false);
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, true, true, false, false);
+ false, true, true, false, false, scanDetails);
assertEquals("choose the wrong BSSID", null, candidate);
}
@@ -1226,15 +1228,15 @@
when(mWifiInfo.getBSSID()).thenReturn(bssids[0]);
when(mWifiInfo.is24GHz()).thenReturn(true);
//connect to config2 first
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
+ mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false,
+ false, true, false, scanDetails);
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
+ false, false, true, false, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -1266,14 +1268,14 @@
scanResultLinkConfiguration(savedConfigs, scanDetails);
//first connect to test2 because of RSSI
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
+ mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false,
+ false, true, false, scanDetails);
when(mWifiInfo.getNetworkId()).thenReturn(1);
when(mWifiInfo.getBSSID()).thenReturn(bssids[1]);
when(mWifiInfo.is24GHz()).thenReturn(false);
when(mWifiInfo.is5GHz()).thenReturn(true);
when(mWifiConfigManager.isOpenNetwork(savedConfigs[1])).thenReturn(true);
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
levels[0] = -60;
scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
@@ -1281,7 +1283,7 @@
ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
+ false, false, true, false, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -1314,8 +1316,8 @@
scanResultLinkConfiguration(savedConfigs, scanDetails);
//first connect to test2 since test1's RSSI is negligible
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
+ mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false,
+ false, true, false, scanDetails);
when(mWifiInfo.getNetworkId()).thenReturn(1);
when(mWifiInfo.getBSSID()).thenReturn(bssids[1]);
when(mWifiInfo.is24GHz()).thenReturn(false);
@@ -1324,11 +1326,11 @@
scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
scanResultLinkConfiguration(savedConfigs, scanDetails);
when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
+ false, false, true, false, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -1359,8 +1361,8 @@
when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
scanResultLinkConfiguration(savedConfigs, scanDetails);
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
+ mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false,
+ false, true, false, scanDetails);
when(mWifiInfo.getNetworkId()).thenReturn(1);
when(mWifiInfo.getBSSID()).thenReturn(bssids[1]);
when(mWifiInfo.getRssi()).thenReturn(levels[1]);
@@ -1368,14 +1370,14 @@
when(mWifiInfo.is5GHz()).thenReturn(true);
when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
levels[0] = -60;
scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
scanResultLinkConfiguration(savedConfigs, scanDetails);
ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
+ false, false, true, false, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -1405,8 +1407,8 @@
when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
scanResultLinkConfiguration(savedConfigs, scanDetails);
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
+ mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false,
+ false, true, false, scanDetails);
when(mWifiInfo.getNetworkId()).thenReturn(1);
when(mWifiInfo.getBSSID()).thenReturn(bssids[1]);
@@ -1417,12 +1419,12 @@
when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
levels[0] = -60;
scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
scanResultLinkConfiguration(savedConfigs, scanDetails);
ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
+ false, false, true, false, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -1454,8 +1456,8 @@
when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
scanResultLinkConfiguration(savedConfigs, scanDetails);
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
+ mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false,
+ false, true, false, scanDetails);
when(mWifiInfo.getNetworkId()).thenReturn(1);
when(mWifiInfo.getBSSID()).thenReturn(bssids[1]);
when(mWifiInfo.is24GHz()).thenReturn(true);
@@ -1464,12 +1466,12 @@
levels[0] = -80 + WifiQualifiedNetworkSelector.SAME_BSSID_AWARD / 4
+ WifiQualifiedNetworkSelector.SAME_NETWORK_AWARD / 4 - 1;
scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
scanResultLinkConfiguration(savedConfigs, scanDetails);
ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
+ false, false, true, false, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -1499,8 +1501,8 @@
final List<WifiConfiguration> savedNetwork = Arrays.asList(savedConfigs);
when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
scanResultLinkConfiguration(savedConfigs, scanDetails);
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
+ mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false,
+ false, true, false, scanDetails);
when(mWifiInfo.getNetworkId()).thenReturn(1);
when(mWifiInfo.getBSSID()).thenReturn(bssids[1]);
@@ -1510,12 +1512,12 @@
levels[0] = -80 + WifiQualifiedNetworkSelector.SAME_BSSID_AWARD / 4
+ WifiQualifiedNetworkSelector.SAME_NETWORK_AWARD / 4 + 1;
scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
scanResultLinkConfiguration(savedConfigs, scanDetails);
ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
+ false, false, true, false, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -1551,8 +1553,8 @@
final List<WifiConfiguration> savedNetwork = Arrays.asList(savedConfigs);
when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
scanResultLinkConfiguration(savedConfigs, scanDetails);
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
+ mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false,
+ false, true, false, scanDetails);
when(mWifiInfo.getNetworkId()).thenReturn(1);
when(mWifiInfo.getBSSID()).thenReturn(bssids[1]);
@@ -1561,12 +1563,12 @@
when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
levels[0] = -80 + WifiQualifiedNetworkSelector.SAME_NETWORK_AWARD / 4 - 1;
scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
scanResultLinkConfiguration(savedConfigs, scanDetails);
ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
+ false, false, true, false, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -1602,8 +1604,8 @@
final List<WifiConfiguration> savedNetwork = Arrays.asList(savedConfigs);
when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
scanResultLinkConfiguration(savedConfigs, scanDetails);
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
+ mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, false,
+ false, true, false, scanDetails);
when(mWifiInfo.getNetworkId()).thenReturn(1);
when(mWifiInfo.getBSSID()).thenReturn(bssids[1]);
@@ -1612,12 +1614,12 @@
when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
levels[0] = -80 + WifiQualifiedNetworkSelector.SAME_BSSID_AWARD / 4 + 1;
scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
scanResultLinkConfiguration(savedConfigs, scanDetails);
ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
+ false, false, true, false, false, scanDetails);
verifySelectedResult(chosenScanResult, candidate);
}
@@ -1671,11 +1673,11 @@
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(
false /* forceSelectNetwork */,
true /* isUntrustedConnectionsAllowed */,
- scanDetails,
false, /* isLinkDebouncing */
false, /* isConnected */
true, /* isDisconnected */
- false /* isSupplicantTransient */);
+ false, /* isSupplicantTransient */
+ scanDetails);
verify(selectionStatus).setCandidate(untrustedScanResult);
assertSame(unTrustedNetworkCandidate, candidate);
assertEquals(meteredHints[1], candidate.meteredHint);
@@ -1744,11 +1746,11 @@
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(
false /* forceSelectNetwork */,
true /* isUntrustedConnectionsAllowed */,
- scanDetails,
false, /* isLinkDebouncing */
false, /* isConnected */
true, /* isDisconnected */
- false /* isSupplicantTransient */);
+ false, /* isSupplicantTransient */
+ scanDetails);
verifySelectedResult(chosenScanResult, candidate);
//Verify two scanDetails returned in the filteredScanDetails
@@ -1823,11 +1825,11 @@
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(
false /* forceSelectNetwork */,
false /* isUntrustedConnectionsAllowed */,
- scanDetails,
false, /* isLinkDebouncing */
false, /* isConnected */
true, /* isDisconnected */
- false /* isSupplicantTransient */);
+ false, /* isSupplicantTransient */
+ scanDetails);
verifySelectedResult(chosenScanResult, candidate);
//Verify two scanDetails returned in the filteredScanDetails
@@ -1893,11 +1895,11 @@
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(
false /* forceSelectNetwork */,
true /* isUntrustedConnectionsAllowed */,
- scanDetails,
false, /* isLinkDebouncing */
false, /* isConnected */
true, /* isDisconnected */
- false /* isSupplicantTransient */);
+ false, /* isSupplicantTransient */
+ scanDetails);
verify(selectionStatus).setCandidate(untrustedScanResult);
assertSame(candidate, unTrustedNetworkCandidate);
assertEquals(meteredHints[0], candidate.meteredHint);
@@ -1938,11 +1940,11 @@
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(
false /* forceSelectNetwork */,
false /* isUntrustedConnectionsAllowed */,
- scanDetails,
false, /* isLinkDebouncing */
false, /* isConnected */
true, /* isDisconnected */
- false /* isSupplicantTransient */);
+ false, /* isSupplicantTransient */
+ scanDetails);
verifySelectedResult(scanDetails.get(0).getScanResult(), candidate);
assertSame(candidate, savedConfigs[0]);
}
@@ -1984,11 +1986,11 @@
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(
false /* forceSelectNetwork */,
false /* isUntrustedConnectionsAllowed */,
- scanDetails,
false, /* isLinkDebouncing */
false, /* isConnected */
true, /* isDisconnected */
- false /* isSupplicantTransient */);
+ false, /* isSupplicantTransient */
+ scanDetails);
verifySelectedResult(scanDetails.get(0).getScanResult(), candidate);
assertSame(candidate, savedConfigs[0]);
}
@@ -2035,11 +2037,11 @@
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(
false /* forceSelectNetwork */,
true /* isUntrustedConnectionsAllowed */,
- scanDetails,
false, /* isLinkDebouncing */
false, /* isConnected */
true, /* isDisconnected */
- false /* isSupplicantTransient */);
+ false, /* isSupplicantTransient */
+ scanDetails);
verifySelectedResult(scanDetails.get(0).getScanResult(), candidate);
assertSame(candidate, savedConfigs[0]);
}
@@ -2093,11 +2095,11 @@
WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(
false /* forceSelectNetwork */,
true /* isUntrustedConnectionsAllowed */,
- scanDetails,
false, /* isLinkDebouncing */
false, /* isConnected */
true, /* isDisconnected */
- false /* isSupplicantTransient */);
+ false, /* isSupplicantTransient */
+ scanDetails);
verify(selectionStatus).setCandidate(untrustedScanResult);
assertSame(unTrustedNetworkCandidate, candidate);
}
@@ -2233,4 +2235,119 @@
assertEquals(WifiQualifiedNetworkSelector.ExternalScoreEvaluator
.BestCandidateType.NONE, evaluator.getBestCandidateType());
}
+
+ /**
+ * Case #46 5GHz due to user band preference is set to 5GHz only
+ *
+ * In this test. we simulate following scenario
+ * WifiStateMachine is under disconnected state
+ * User band preference is set to 5GHz only
+ * Two networks test1, test2 are secured network
+ * Both network are enabled
+ * test1 is @ 2GHz with RSSI -50
+ * test2 is @ 5Ghz with RSSI -75
+ *
+ * Expected behavior: test2 is chosen due to user band preference
+ */
+ @Test
+ public void chooseNetworkDisconnectUserPrefer5GTest() {
+ String[] ssids = DEFAULT_SSIDS;
+ String[] bssids = DEFAULT_BSSIDS;
+ int[] frequencies = {2437, 5180};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {-50, -75};
+ int[] security = {SECURITY_PSK, SECURITY_PSK};
+
+ List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
+ WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security);
+ prepareConfigStore(savedConfigs);
+
+ final List<WifiConfiguration> savedNetwork = Arrays.asList(savedConfigs);
+ when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
+ scanResultLinkConfiguration(savedConfigs, scanDetails);
+ ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
+
+ mWifiQualifiedNetworkSelector.setUserPreferredBand(WifiManager.WIFI_FREQUENCY_BAND_5GHZ);
+ WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
+ false, false, false, true, false, scanDetails);
+
+ verifySelectedResult(chosenScanResult, candidate);
+ }
+
+ /**
+ * Case #47 2GHz due to user band preference is set to 2GHz only
+ *
+ * In this test. we simulate following scenario
+ * WifiStateMachine is under disconnected state
+ * User band preference is set to 2.4GHz only
+ * Two networks test1, test2 are secured network
+ * Both network are enabled
+ * test1 is @ 2GHz with RSSI -75
+ * test2 is @ 5Ghz with RSSI -50
+ *
+ * Expected behavior: test1 is chosen due to user band preference
+ */
+ @Test
+ public void chooseNetworkDisconnectUserPrefer2GTest() {
+ String[] ssids = DEFAULT_SSIDS;
+ String[] bssids = DEFAULT_BSSIDS;
+ int[] frequencies = {2437, 5180};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {-75, -50};
+ int[] security = {SECURITY_PSK, SECURITY_PSK};
+
+ List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
+ WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security);
+ prepareConfigStore(savedConfigs);
+
+ final List<WifiConfiguration> savedNetwork = Arrays.asList(savedConfigs);
+ when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
+ scanResultLinkConfiguration(savedConfigs, scanDetails);
+ ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
+
+ mWifiQualifiedNetworkSelector.setUserPreferredBand(WifiManager.WIFI_FREQUENCY_BAND_2GHZ);
+ WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
+ false, false, false, true, false, scanDetails);
+
+ verifySelectedResult(chosenScanResult, candidate);
+ }
+
+ /**
+ * Case #48 5GHz due to user band preference is set to AUTO
+ *
+ * In this test. we simulate following scenario
+ * WifiStateMachine is under disconnected state
+ * User band preference is set to AUTO
+ * Two networks test1, test2 are secured network
+ * Both network are enabled
+ * test1 is @ 2GHz with RSSI -50
+ * test2 is @ 5Ghz with RSSI -50
+ *
+ * Expected behavior: test2 is chosen due to 5G bonus and user band preference
+ * is set to AUTO.
+ */
+ @Test
+ public void chooseNetworkDisconnectUserPreferAutoBandTest() {
+ String[] ssids = DEFAULT_SSIDS;
+ String[] bssids = DEFAULT_BSSIDS;
+ int[] frequencies = {2437, 5180};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {-50, -50};
+ int[] security = {SECURITY_PSK, SECURITY_PSK};
+
+ List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
+ WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security);
+ prepareConfigStore(savedConfigs);
+
+ final List<WifiConfiguration> savedNetwork = Arrays.asList(savedConfigs);
+ when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
+ scanResultLinkConfiguration(savedConfigs, scanDetails);
+ ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
+
+ mWifiQualifiedNetworkSelector.setUserPreferredBand(WifiManager.WIFI_FREQUENCY_BAND_AUTO);
+ WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
+ false, false, false, true, false, scanDetails);
+
+ verifySelectedResult(chosenScanResult, candidate);
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
index e0f94ad..be19084 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
@@ -22,6 +22,8 @@
import static org.mockito.Mockito.*;
import android.app.ActivityManager;
+import android.app.test.TestAlarmManager;
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -53,6 +55,7 @@
import android.os.PowerManager;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.test.TestLooper;
import android.provider.Settings;
import android.security.KeyStore;
import android.telephony.TelephonyManager;
@@ -65,7 +68,6 @@
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.IState;
import com.android.internal.util.StateMachine;
-import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments;
import com.android.server.wifi.hotspot2.NetworkDetail;
import com.android.server.wifi.hotspot2.Utils;
import com.android.server.wifi.p2p.WifiP2pServiceImpl;
@@ -84,6 +86,7 @@
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -165,20 +168,16 @@
WifiP2pServiceImpl p2pm = (WifiP2pServiceImpl) p2pBinder.queryLocalInterface(
IWifiP2pManager.class.getCanonicalName());
- final Object sync = new Object();
- synchronized (sync) {
- mP2pThread = new HandlerThread("WifiP2pMockThread") {
- @Override
- protected void onLooperPrepared() {
- synchronized (sync) {
- sync.notifyAll();
- }
- }
- };
+ final CountDownLatch untilDone = new CountDownLatch(1);
+ mP2pThread = new HandlerThread("WifiP2pMockThread") {
+ @Override
+ protected void onLooperPrepared() {
+ untilDone.countDown();
+ }
+ };
- mP2pThread.start();
- sync.wait();
- }
+ mP2pThread.start();
+ untilDone.await();
Handler handler = new Handler(mP2pThread.getLooper());
when(p2pm.getP2pStateMachineMessenger()).thenReturn(new Messenger(handler));
@@ -229,7 +228,7 @@
when(context.getSystemService(Context.POWER_SERVICE)).thenReturn(
new PowerManager(context, mock(IPowerManager.class), new Handler()));
- mAlarmManager = new MockAlarmManager();
+ mAlarmManager = new TestAlarmManager();
when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn(
mAlarmManager.getAlarmManager());
@@ -307,10 +306,10 @@
HandlerThread mP2pThread;
HandlerThread mSyncThread;
AsyncChannel mWsmAsyncChannel;
- MockAlarmManager mAlarmManager;
+ TestAlarmManager mAlarmManager;
MockWifiMonitor mWifiMonitor;
TestIpManager mTestIpManager;
- MockLooper mLooper;
+ TestLooper mLooper;
WifiConfigManager mWifiConfigManager;
@Mock WifiNative mWifiNative;
@@ -334,7 +333,7 @@
Log.d(TAG, "Setting up ...");
// Ensure looper exists
- mLooper = new MockLooper();
+ mLooper = new TestLooper();
MockitoAnnotations.initMocks(this);
@@ -349,6 +348,7 @@
when(mWifiInjector.getPropertyService()).thenReturn(mPropertyService);
when(mWifiInjector.getBuildProperties()).thenReturn(mBuildProperties);
when(mWifiInjector.getKeyStore()).thenReturn(mock(KeyStore.class));
+ when(mWifiInjector.getWifiBackupRestore()).thenReturn(mock(WifiBackupRestore.class));
FrameworkFacade factory = getFrameworkFacade();
Context context = getContext();
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiSupplicantControlTest.java
similarity index 93%
rename from tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
rename to tests/wifitests/src/com/android/server/wifi/WifiSupplicantControlTest.java
index 3993fe5..ceb4b42 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiSupplicantControlTest.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package com.android.server.wifi;
@@ -35,10 +35,10 @@
import java.util.Map;
/**
- * Unit tests for {@link com.android.server.wifi.WifiConfigStore}.
+ * Unit tests for {@link com.android.server.wifi.WifiSupplicantControl}.
*/
@SmallTest
-public class WifiConfigStoreTest {
+public class WifiSupplicantControlTest {
private static final String KEY_SSID = "ssid";
private static final String KEY_PSK = "psk";
private static final String KEY_KEY_MGMT = "key_mgmt";
@@ -97,7 +97,7 @@
NETWORK_3_VARS.put(CONFIG_KEY, "\"testwpa2psk\"WPA_PSK");
}
- private static final ArrayList<HashMap<String, String>> NETWORK_VARS = new ArrayList<HashMap<String, String>>();
+ private static final ArrayList<HashMap<String, String>> NETWORK_VARS = new ArrayList<>();
static {
NETWORK_VARS.add(NETWORK_0_VARS);
NETWORK_VARS.add(NETWORK_1_VARS);
@@ -105,7 +105,8 @@
NETWORK_VARS.add(NETWORK_3_VARS);
}
- // Taken from wpa_supplicant.conf actual test device Some fields modified for privacy.
+ // Taken from wpa_supplicant.conf of an actual test device. Some fields modified for privacy
+ // reasons.
private static final String TEST_WPA_SUPPLICANT_CONF = ""
+ "ctrl_interface=/data/misc/wifi/sockets\n"
+ "disable_scan_offload=1\n"
@@ -157,16 +158,15 @@
@Mock private WifiNative mWifiNative;
@Mock private Context mContext;
- private MockKeyStore mMockKeyStore;
- private WifiConfigStore mWifiConfigStore;
+ private WifiSupplicantControl mWifiSupplicantControl;
+ /**
+ * Initialize |WifiSupplicantControl| instance before each test.
+ */
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
-
- mMockKeyStore = new MockKeyStore();
- mWifiConfigStore = new WifiConfigStore(mContext, mWifiNative, mMockKeyStore.createMock(),
- null, false, true);
+ mWifiSupplicantControl = new WifiSupplicantControl(mContext, mWifiNative, null);
}
/**
@@ -226,7 +226,7 @@
BufferedReader reader = null;
try {
reader = new BufferedReader(new StringReader(TEST_WPA_SUPPLICANT_CONF));
- result = mWifiConfigStore.readNetworkVariablesFromReader(reader, key);
+ result = mWifiSupplicantControl.readNetworkVariablesFromReader(reader, key);
} catch (IOException e) {
fail("Error reading test supplicant conf string");
} finally {
diff --git a/tests/wifitests/src/com/android/server/wifi/nan/TlvBufferUtilsTest.java b/tests/wifitests/src/com/android/server/wifi/nan/TlvBufferUtilsTest.java
index 5c83185..9141841 100644
--- a/tests/wifitests/src/com/android/server/wifi/nan/TlvBufferUtilsTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/nan/TlvBufferUtilsTest.java
@@ -24,7 +24,6 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
-import org.junit.rules.ExpectedException;
/**
* Unit test harness for WifiNanManager class.
@@ -34,9 +33,6 @@
@Rule
public ErrorCollector collector = new ErrorCollector();
- @Rule
- public ExpectedException thrown = ExpectedException.none();
-
/*
* TlvBufferUtils Tests
*/
@@ -64,10 +60,20 @@
utilAreArraysEqual(tlv01.getArray(), tlv01.getActualLength(), new byte[] {
1, 2, 3, 0, 1, 2 }, 6),
equalTo(true));
+
+ collector.checkThat("tlv11-valid",
+ TlvBufferUtils.isValid(tlv11.getArray(), tlv11.getActualLength(), 1, 1),
+ equalTo(true));
+ collector.checkThat("tlv01-valid",
+ TlvBufferUtils.isValid(tlv01.getArray(), tlv01.getActualLength(), 0, 1),
+ equalTo(true));
}
@Test
public void testTlvIterate() {
+ final String ascii = "ABC";
+ final String nonAscii = "何かもっと複雑な";
+
TlvBufferUtils.TlvConstructor tlv22 = new TlvBufferUtils.TlvConstructor(2, 2);
tlv22.allocate(18);
tlv22.putInt(0, 2);
@@ -102,9 +108,10 @@
}
TlvBufferUtils.TlvConstructor tlv02 = new TlvBufferUtils.TlvConstructor(0, 2);
- tlv02.allocate(15);
+ tlv02.allocate(100);
tlv02.putByte(0, (byte) 2);
- tlv02.putString(0, "ABC");
+ tlv02.putString(0, ascii);
+ tlv02.putString(0, nonAscii);
TlvBufferUtils.TlvIterable tlv02It = new TlvBufferUtils.TlvIterable(0, 2, tlv02.getArray(),
tlv02.getActualLength());
@@ -115,101 +122,119 @@
collector.checkThat("tlv02-correct-iteration-DATA", (int) tlv.getByte(),
equalTo(2));
} else if (count == 1) {
- collector.checkThat("tlv02-correct-iteration-mLength", tlv.mLength, equalTo(3));
- collector.checkThat("tlv02-correct-iteration-DATA", tlv.getString().equals("ABC"),
+ collector.checkThat("tlv02-correct-iteration-mLength", tlv.mLength,
+ equalTo(ascii.length()));
+ collector.checkThat("tlv02-correct-iteration-DATA", tlv.getString().equals(ascii),
equalTo(true));
+ } else if (count == 2) {
+ collector.checkThat("tlv02-correct-iteration-mLength", tlv.mLength,
+ equalTo(nonAscii.getBytes().length));
+ collector.checkThat("tlv02-correct-iteration-DATA",
+ tlv.getString().equals(nonAscii), equalTo(true));
} else {
collector.checkThat("Invalid number of iterations in loop - tlv02", true,
equalTo(false));
}
++count;
}
- if (count != 2) {
- collector.checkThat("Invalid number of iterations outside loop - tlv02", true,
- equalTo(false));
- }
+ collector.checkThat("Invalid number of iterations outside loop - tlv02", count,
+ equalTo(3));
+
+ collector.checkThat("tlv22-valid",
+ TlvBufferUtils.isValid(tlv22.getArray(), tlv22.getActualLength(), 2, 2),
+ equalTo(true));
+ collector.checkThat("tlv02-valid",
+ TlvBufferUtils.isValid(tlv02.getArray(), tlv02.getActualLength(), 0, 2),
+ equalTo(true));
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testTlvInvalidSizeT1L0() {
- thrown.expect(IllegalArgumentException.class);
TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(1, 0);
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testTlvInvalidSizeTm3L2() {
- thrown.expect(IllegalArgumentException.class);
TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(-3, 2);
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testTlvInvalidSizeT1Lm2() {
- thrown.expect(IllegalArgumentException.class);
TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(1, -2);
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testTlvInvalidSizeT1L3() {
- thrown.expect(IllegalArgumentException.class);
TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(1, 3);
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testTlvInvalidSizeT3L1() {
- thrown.expect(IllegalArgumentException.class);
TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(3, 1);
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testTlvItInvalidSizeT1L0() {
final byte[] dummy = {
0, 1, 2 };
final int dummyLength = 3;
- thrown.expect(IllegalArgumentException.class);
TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(1, 0, dummy,
dummyLength);
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testTlvItInvalidSizeTm3L2() {
final byte[] dummy = {
0, 1, 2 };
final int dummyLength = 3;
- thrown.expect(IllegalArgumentException.class);
TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(-3, 2, dummy,
dummyLength);
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testTlvItInvalidSizeT1Lm2() {
final byte[] dummy = {
0, 1, 2 };
final int dummyLength = 3;
- thrown.expect(IllegalArgumentException.class);
TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(1, -2, dummy,
dummyLength);
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testTlvItInvalidSizeT1L3() {
final byte[] dummy = {
0, 1, 2 };
final int dummyLength = 3;
- thrown.expect(IllegalArgumentException.class);
TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(1, 3, dummy,
dummyLength);
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testTlvItInvalidSizeT3L1() {
final byte[] dummy = {
0, 1, 2 };
final int dummyLength = 3;
- thrown.expect(IllegalArgumentException.class);
TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(3, 1, dummy,
dummyLength);
}
+ /**
+ * Validate that a malformed byte array fails the TLV validity test.
+ */
+ @Test
+ public void testTlvInvalidByteArray() {
+ TlvBufferUtils.TlvConstructor tlv01 = new TlvBufferUtils.TlvConstructor(0, 1);
+ tlv01.allocate(15);
+ tlv01.putByte(0, (byte) 2);
+ tlv01.putByteArray(2, new byte[]{0, 1, 2});
+
+ byte[] array = tlv01.getArray();
+ array[0] = 10;
+
+ collector.checkThat("tlv01-invalid",
+ TlvBufferUtils.isValid(array, tlv01.getActualLength(), 0, 1), equalTo(false));
+ }
+
/*
* Utilities
*/
diff --git a/tests/wifitests/src/com/android/server/wifi/nan/WifiNanDataPathStateManagerTest.java b/tests/wifitests/src/com/android/server/wifi/nan/WifiNanDataPathStateManagerTest.java
new file mode 100644
index 0000000..e4f714a
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/nan/WifiNanDataPathStateManagerTest.java
@@ -0,0 +1,735 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi.nan;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyShort;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.test.TestAlarmManager;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
+import android.net.NetworkFactory;
+import android.net.NetworkInfo;
+import android.net.NetworkMisc;
+import android.net.NetworkRequest;
+import android.net.wifi.RttManager;
+import android.net.wifi.nan.ConfigRequest;
+import android.net.wifi.nan.IWifiNanEventCallback;
+import android.net.wifi.nan.IWifiNanManager;
+import android.net.wifi.nan.IWifiNanSessionCallback;
+import android.net.wifi.nan.PublishConfig;
+import android.net.wifi.nan.SubscribeConfig;
+import android.net.wifi.nan.WifiNanEventCallback;
+import android.net.wifi.nan.WifiNanManager;
+import android.net.wifi.nan.WifiNanPublishSession;
+import android.net.wifi.nan.WifiNanSessionCallback;
+import android.net.wifi.nan.WifiNanSubscribeSession;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.test.TestLooper;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
+
+import com.android.internal.util.AsyncChannel;
+
+import libcore.util.HexEncoding;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ErrorCollector;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+
+/**
+ * Unit test harness for WifiNanDataPathStateManager class.
+ */
+@SmallTest
+public class WifiNanDataPathStateManagerTest {
+ private static final String sNanInterfacePrefix = "nan";
+
+ private TestLooper mMockLooper;
+ private WifiNanStateManager mDut;
+ @Mock private WifiNanNative mMockNative;
+ @Mock private Context mMockContext;
+ @Mock private IWifiNanManager mMockNanService;
+ @Mock private WifiNanPublishSession mMockPublishSession;
+ @Mock private WifiNanSubscribeSession mMockSubscribeSession;
+ @Mock private RttManager.RttListener mMockRttListener;
+ @Mock private ConnectivityManager mMockCm;
+ @Mock private INetworkManagementService mMockNwMgt;
+ @Mock private WifiNanDataPathStateManager.NetworkInterfaceWrapper mMockNetworkInterface;
+ @Mock private IWifiNanEventCallback mMockCallback;
+ @Mock IWifiNanSessionCallback mMockSessionCallback;
+ TestAlarmManager mAlarmManager;
+
+ @Rule
+ public ErrorCollector collector = new ErrorCollector();
+
+ /**
+ * Initialize mocks.
+ */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mAlarmManager = new TestAlarmManager();
+ when(mMockContext.getSystemService(Context.ALARM_SERVICE))
+ .thenReturn(mAlarmManager.getAlarmManager());
+
+ when(mMockContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mMockCm);
+
+ mMockLooper = new TestLooper();
+
+ mDut = installNewNanStateManager();
+ mDut.start(mMockContext, mMockLooper.getLooper());
+ mDut.startLate();
+
+ when(mMockNative.getCapabilities(anyShort())).thenReturn(true);
+ when(mMockNative.enableAndConfigure(anyShort(), any(ConfigRequest.class), anyBoolean()))
+ .thenReturn(true);
+ when(mMockNative.disable(anyShort())).thenReturn(true);
+ when(mMockNative.publish(anyShort(), anyInt(), any(PublishConfig.class))).thenReturn(true);
+ when(mMockNative.subscribe(anyShort(), anyInt(), any(SubscribeConfig.class)))
+ .thenReturn(true);
+ when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(byte[].class),
+ any(byte[].class), anyInt(), anyInt())).thenReturn(true);
+ when(mMockNative.stopPublish(anyShort(), anyInt())).thenReturn(true);
+ when(mMockNative.stopSubscribe(anyShort(), anyInt())).thenReturn(true);
+ when(mMockNative.createNanNetworkInterface(anyShort(), anyString())).thenReturn(true);
+ when(mMockNative.deleteNanNetworkInterface(anyShort(), anyString())).thenReturn(true);
+ when(mMockNative.initiateDataPath(anyShort(), anyInt(), anyInt(), anyInt(),
+ any(byte[].class), anyString(), any(byte[].class), anyInt())).thenReturn(true);
+ when(mMockNative.respondToDataPathRequest(anyShort(), anyBoolean(), anyInt(), anyString(),
+ any(byte[].class), anyInt())).thenReturn(true);
+ when(mMockNative.endDataPath(anyShort(), anyInt())).thenReturn(true);
+
+ when(mMockNetworkInterface.configureAgentProperties(
+ any(WifiNanDataPathStateManager.NanNetworkRequestInformation.class), anyString(),
+ anyInt(), any(NetworkInfo.class), any(NetworkCapabilities.class),
+ any(LinkProperties.class))).thenReturn(true);
+
+ installMockWifiNanNative(mMockNative);
+ installDataPathStateManagerMocks();
+ }
+
+ /**
+ * Validates that creating and deleting all interfaces works based on capabilities.
+ */
+ @Test
+ public void testCreateDeleteAllInterfaces() throws Exception {
+ final int numNdis = 3;
+ final int failCreateInterfaceIndex = 1;
+
+ WifiNanNative.Capabilities capabilities = new WifiNanNative.Capabilities();
+ capabilities.maxNdiInterfaces = numNdis;
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<String> interfaceName = ArgumentCaptor.forClass(String.class);
+ InOrder inOrder = inOrder(mMockNative);
+
+ // (1) get capabilities
+ mDut.getCapabilities();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), capabilities);
+ mMockLooper.dispatchAll();
+
+ // (2) create all interfaces
+ mDut.createAllDataPathInterfaces();
+ mMockLooper.dispatchAll();
+ for (int i = 0; i < numNdis; ++i) {
+ inOrder.verify(mMockNative).createNanNetworkInterface(transactionId.capture(),
+ interfaceName.capture());
+ collector.checkThat("interface created -- " + i, sNanInterfacePrefix + i,
+ equalTo(interfaceName.getValue()));
+ mDut.onCreateDataPathInterfaceResponse(transactionId.getValue(), true, 0);
+ mMockLooper.dispatchAll();
+ }
+
+ // (3) delete all interfaces [one unsuccessfully] - note that will not necessarily be
+ // done sequentially
+ boolean[] done = new boolean[numNdis];
+ Arrays.fill(done, false);
+ mDut.deleteAllDataPathInterfaces();
+ mMockLooper.dispatchAll();
+ for (int i = 0; i < numNdis; ++i) {
+ inOrder.verify(mMockNative).deleteNanNetworkInterface(transactionId.capture(),
+ interfaceName.capture());
+ int interfaceIndex = Integer.valueOf(
+ interfaceName.getValue().substring(sNanInterfacePrefix.length()));
+ done[interfaceIndex] = true;
+ if (interfaceIndex == failCreateInterfaceIndex) {
+ mDut.onDeleteDataPathInterfaceResponse(transactionId.getValue(), false, 0);
+ } else {
+ mDut.onDeleteDataPathInterfaceResponse(transactionId.getValue(), true, 0);
+ }
+ mMockLooper.dispatchAll();
+ }
+ for (int i = 0; i < numNdis; ++i) {
+ collector.checkThat("interface deleted -- " + i, done[i], equalTo(true));
+ }
+
+ // (4) create all interfaces (should get a delete for the one which couldn't delete earlier)
+ mDut.createAllDataPathInterfaces();
+ mMockLooper.dispatchAll();
+ for (int i = 0; i < numNdis; ++i) {
+ if (i == failCreateInterfaceIndex) {
+ inOrder.verify(mMockNative).deleteNanNetworkInterface(transactionId.capture(),
+ interfaceName.capture());
+ collector.checkThat("interface delete pre-create -- " + i, sNanInterfacePrefix + i,
+ equalTo(interfaceName.getValue()));
+ mDut.onDeleteDataPathInterfaceResponse(transactionId.getValue(), true, 0);
+ mMockLooper.dispatchAll();
+ }
+ inOrder.verify(mMockNative).createNanNetworkInterface(transactionId.capture(),
+ interfaceName.capture());
+ collector.checkThat("interface created -- " + i, sNanInterfacePrefix + i,
+ equalTo(interfaceName.getValue()));
+ mDut.onCreateDataPathInterfaceResponse(transactionId.getValue(), true, 0);
+ mMockLooper.dispatchAll();
+ }
+
+ verifyNoMoreInteractions(mMockNative);
+ }
+
+ /*
+ * Initiator tests
+ */
+
+ /**
+ * Validate the success flow of the Initiator: using session network specifier with a non-null
+ * token.
+ */
+ @Test
+ public void testDataPathInitiatorMacTokenSuccess() throws Exception {
+ testDataPathInitiatorUtility(false, true, true, true);
+ }
+
+ /**
+ * Validate the fail flow of the Initiator: using session network specifier with a null
+ * token.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testDataPathInitiatorMacNoTokenFail() throws Exception {
+ testDataPathInitiatorUtility(false, true, false, true);
+ }
+
+ /**
+ * Validate the fail flow of the Initiator: using session network specifier with a 0
+ * peer ID.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testDataPathInitiatorNoMacFail() throws Exception {
+ testDataPathInitiatorUtility(false, false, true, true);
+ }
+
+ /**
+ * Validate the success flow of the Initiator: using a direct network specifier with a non-null
+ * peer mac and non-null token.
+ */
+ @Test
+ public void testDataPathInitiatorDirectMacTokenSuccess() throws Exception {
+ testDataPathInitiatorUtility(true, true, true, true);
+ }
+
+ /**
+ * Validate the fail flow of the Initiator: using a direct network specifier with a non-null
+ * peer mac and null token.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testDataPathInitiatorDirectMacNoTokenFail() throws Exception {
+ testDataPathInitiatorUtility(true, true, false, true);
+ }
+
+ /**
+ * Validate the fail flow of the Initiator: using a direct network specifier with a null peer
+ * mac and non-null token.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testDataPathInitiatorDirectNoMacTokenFail() throws Exception {
+ testDataPathInitiatorUtility(true, false, true, true);
+ }
+
+ /**
+ * Validate the fail flow of the Initiator: using a direct network specifier with a null peer
+ * mac and null token.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testDataPathInitiatorDirectNoMacNoTokenFail() throws Exception {
+ testDataPathInitiatorUtility(true, false, false, true);
+ }
+
+ /**
+ * Validate the fail flow of the Initiator: use a session network specifier with a non-null
+ * token, but don't get a confirmation.
+ */
+ @Test
+ public void testDataPathInitiatorNoConfirmationTimeoutFail() throws Exception {
+ testDataPathInitiatorUtility(false, true, true, false);
+ }
+
+ /*
+ * Responder tests
+ */
+
+ /**
+ * Validate the success flow of the Responder: using session network specifier with a non-null
+ * token.
+ */
+ @Test
+ public void testDataPathResonderMacTokenSuccess() throws Exception {
+ testDataPathResponderUtility(false, true, true, true);
+ }
+
+ /**
+ * Validate the success flow of the Responder: using session network specifier with a null
+ * token.
+ */
+ @Test
+ public void testDataPathResonderMacNoTokenSuccess() throws Exception {
+ testDataPathResponderUtility(false, true, false, true);
+ }
+
+ /**
+ * Validate the success flow of the Responder: using session network specifier with a
+ * token and no peer ID (i.e. 0).
+ */
+ @Test
+ public void testDataPathResonderMacTokenNoPeerIdSuccess() throws Exception {
+ testDataPathResponderUtility(false, false, true, true);
+ }
+
+ /**
+ * Validate the success flow of the Responder: using session network specifier with a null
+ * token and no peer ID (i.e. 0).
+ */
+ @Test
+ public void testDataPathResonderMacTokenNoPeerIdNoTokenSuccess() throws Exception {
+ testDataPathResponderUtility(false, false, false, true);
+ }
+
+ /**
+ * Validate the success flow of the Responder: using a direct network specifier with a non-null
+ * peer mac and non-null token.
+ */
+ @Test
+ public void testDataPathResonderDirectMacTokenSuccess() throws Exception {
+ testDataPathResponderUtility(true, true, true, true);
+ }
+
+ /**
+ * Validate the success flow of the Responder: using a direct network specifier with a non-null
+ * peer mac and null token.
+ */
+ @Test
+ public void testDataPathResonderDirectMacNoTokenSuccess() throws Exception {
+ testDataPathResponderUtility(true, true, false, true);
+ }
+
+ /**
+ * Validate the success flow of the Responder: using a direct network specifier with a null peer
+ * mac and non-null token.
+ */
+ @Test
+ public void testDataPathResonderDirectNoMacTokenSuccess() throws Exception {
+ testDataPathResponderUtility(true, false, true, true);
+ }
+
+ /**
+ * Validate the success flow of the Responder: using a direct network specifier with a null peer
+ * mac and null token.
+ */
+ @Test
+ public void testDataPathResonderDirectNoMacNoTokenSuccess() throws Exception {
+ testDataPathResponderUtility(true, false, false, true);
+ }
+
+ /**
+ * Validate the fail flow of the Responder: use a session network specifier with a non-null
+ * token, but don't get a confirmation.
+ */
+ @Test
+ public void testDataPathResponderNoConfirmationTimeoutFail() throws Exception {
+ testDataPathInitiatorUtility(false, true, true, false);
+ }
+
+ /*
+ * Utilities
+ */
+
+ private void testDataPathInitiatorUtility(boolean useDirect, boolean provideMac,
+ boolean provideToken, boolean getConfirmation) throws Exception {
+ final int clientId = 123;
+ final int pubSubId = 11234;
+ final int peerId = 1341234;
+ final int ndpId = 2;
+ final String token = "some token";
+ final String peerToken = "let's go!";
+ final byte[] peerDiscoveryMac = HexEncoding.decode("000102030405".toCharArray(), false);
+ final byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false);
+
+ ArgumentCaptor<Messenger> messengerCaptor = ArgumentCaptor.forClass(Messenger.class);
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ InOrder inOrder = inOrder(mMockNative, mMockCm, mMockCallback, mMockSessionCallback);
+
+ // (0) initialize
+ Pair<Integer, Messenger> res = initDataPathEndPoint(clientId, pubSubId, peerId,
+ peerDiscoveryMac, inOrder);
+
+ // (1) request network
+ NetworkRequest nr;
+ if (useDirect) {
+ nr = getDirectNetworkRequest(clientId, WifiNanManager.WIFI_NAN_DATA_PATH_ROLE_INITIATOR,
+ provideMac ? peerDiscoveryMac : null, provideToken ? token : null);
+ } else {
+ nr = getSessionNetworkRequest(clientId, res.first, provideMac ? peerId : 0,
+ WifiNanManager.WIFI_NAN_DATA_PATH_ROLE_INITIATOR, provideToken ? token : null);
+ }
+
+ Message reqNetworkMsg = Message.obtain();
+ reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK;
+ reqNetworkMsg.obj = nr;
+ reqNetworkMsg.arg1 = 0;
+ res.second.send(reqNetworkMsg);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).initiateDataPath(transactionId.capture(),
+ eq(useDirect ? 0 : peerId), eq(WifiNanNative.CHANNEL_REQUEST_TYPE_REQUESTED),
+ eq(2437), eq(peerDiscoveryMac), eq("nan0"), eq(token.getBytes()),
+ eq(token.length()));
+ mDut.onInitiateDataPathResponseSuccess(transactionId.getValue(), ndpId);
+ mMockLooper.dispatchAll();
+
+ // (2) get confirmation OR timeout
+ if (getConfirmation) {
+ mDut.onDataPathConfirmNotification(ndpId, peerDataPathMac, true, 0,
+ peerToken.getBytes(),
+ peerToken.length());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockCm).registerNetworkAgent(messengerCaptor.capture(),
+ any(NetworkInfo.class), any(LinkProperties.class),
+ any(NetworkCapabilities.class),
+ anyInt(), any(NetworkMisc.class));
+ } else {
+ assertTrue(
+ mAlarmManager.dispatch(WifiNanStateManager.HAL_DATA_PATH_CONFIRM_TIMEOUT_TAG));
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).endDataPath(transactionId.capture(), eq(ndpId));
+ mDut.onEndDataPathResponse(transactionId.getValue(), true, 0);
+ mMockLooper.dispatchAll();
+ }
+
+ // (3) end data-path (unless didn't get confirmation)
+ if (getConfirmation) {
+ Message endNetworkReqMsg = Message.obtain();
+ endNetworkReqMsg.what = NetworkFactory.CMD_CANCEL_REQUEST;
+ endNetworkReqMsg.obj = nr;
+ res.second.send(endNetworkReqMsg);
+
+ Message endNetworkUsageMsg = Message.obtain();
+ endNetworkUsageMsg.what = AsyncChannel.CMD_CHANNEL_DISCONNECTED;
+ messengerCaptor.getValue().send(endNetworkUsageMsg);
+
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).endDataPath(transactionId.capture(), eq(ndpId));
+ mDut.onEndDataPathResponse(transactionId.getValue(), true, 0);
+ mMockLooper.dispatchAll();
+ }
+
+ verifyNoMoreInteractions(mMockNative, mMockCm);
+ }
+
+ private void testDataPathResponderUtility(boolean useDirect, boolean provideMac,
+ boolean provideToken, boolean getConfirmation) throws Exception {
+ final int clientId = 123;
+ final int pubSubId = 11234;
+ final int peerId = 1341234;
+ final int ndpId = 2;
+ final String token = "some token";
+ final String peerToken = "let's go!";
+ final byte[] peerDiscoveryMac = HexEncoding.decode("000102030405".toCharArray(), false);
+ final byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false);
+
+ ArgumentCaptor<Messenger> messengerCaptor = ArgumentCaptor.forClass(Messenger.class);
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ InOrder inOrder = inOrder(mMockNative, mMockCm, mMockCallback, mMockSessionCallback);
+
+ // (0) initialize
+ Pair<Integer, Messenger> res = initDataPathEndPoint(clientId, pubSubId, peerId,
+ peerDiscoveryMac, inOrder);
+
+ // (1) request network
+ NetworkRequest nr;
+ if (useDirect) {
+ nr = getDirectNetworkRequest(clientId, WifiNanManager.WIFI_NAN_DATA_PATH_ROLE_RESPONDER,
+ provideMac ? peerDiscoveryMac : null, provideToken ? token : null);
+ } else {
+ nr = getSessionNetworkRequest(clientId, res.first, provideMac ? peerId : 0,
+ WifiNanManager.WIFI_NAN_DATA_PATH_ROLE_RESPONDER, provideToken ? token : null);
+ }
+
+ Message reqNetworkMsg = Message.obtain();
+ reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK;
+ reqNetworkMsg.obj = nr;
+ reqNetworkMsg.arg1 = 0;
+ res.second.send(reqNetworkMsg);
+ mMockLooper.dispatchAll();
+
+ // (2) get request & respond
+ mDut.onDataPathRequestNotification(pubSubId, peerDiscoveryMac, ndpId, token.getBytes(),
+ token.length());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).respondToDataPathRequest(transactionId.capture(), eq(true),
+ eq(ndpId), eq("nan0"), eq(new byte[0]), eq(0));
+ mDut.onRespondToDataPathSetupRequestResponse(transactionId.getValue(), true, 0);
+ mMockLooper.dispatchAll();
+
+ // (3) get confirmation OR timeout
+ if (getConfirmation) {
+ mDut.onDataPathConfirmNotification(ndpId, peerDataPathMac, true, 0,
+ peerToken.getBytes(),
+ peerToken.length());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockCm).registerNetworkAgent(messengerCaptor.capture(),
+ any(NetworkInfo.class), any(LinkProperties.class),
+ any(NetworkCapabilities.class),
+ anyInt(), any(NetworkMisc.class));
+ } else {
+ assertTrue(
+ mAlarmManager.dispatch(WifiNanStateManager.HAL_DATA_PATH_CONFIRM_TIMEOUT_TAG));
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).endDataPath(transactionId.capture(), eq(ndpId));
+ mDut.onEndDataPathResponse(transactionId.getValue(), true, 0);
+ mMockLooper.dispatchAll();
+ }
+
+ // (4) end data-path (unless didn't get confirmation)
+ if (getConfirmation) {
+ Message endNetworkMsg = Message.obtain();
+ endNetworkMsg.what = NetworkFactory.CMD_CANCEL_REQUEST;
+ endNetworkMsg.obj = nr;
+ res.second.send(endNetworkMsg);
+
+ Message endNetworkUsageMsg = Message.obtain();
+ endNetworkUsageMsg.what = AsyncChannel.CMD_CHANNEL_DISCONNECTED;
+ messengerCaptor.getValue().send(endNetworkUsageMsg);
+
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).endDataPath(transactionId.capture(), eq(ndpId));
+ mDut.onEndDataPathResponse(transactionId.getValue(), true, 0);
+ mMockLooper.dispatchAll();
+ }
+
+ verifyNoMoreInteractions(mMockNative, mMockCm);
+ }
+
+
+ private static WifiNanStateManager installNewNanStateManager()
+ throws Exception {
+ Constructor<WifiNanStateManager> ctr = WifiNanStateManager.class.getDeclaredConstructor();
+ ctr.setAccessible(true);
+ WifiNanStateManager nanStateManager = ctr.newInstance();
+
+ Field field = WifiNanStateManager.class.getDeclaredField("sNanStateManagerSingleton");
+ field.setAccessible(true);
+ field.set(null, nanStateManager);
+
+ return WifiNanStateManager.getInstance();
+ }
+
+ private static void installMockWifiNanNative(WifiNanNative obj) throws Exception {
+ Field field = WifiNanNative.class.getDeclaredField("sWifiNanNativeSingleton");
+ field.setAccessible(true);
+ field.set(null, obj);
+ }
+
+ private void installDataPathStateManagerMocks() throws Exception {
+ Field field = WifiNanStateManager.class.getDeclaredField("mDataPathMgr");
+ field.setAccessible(true);
+ Object mDataPathMgr = field.get(mDut);
+
+ field = WifiNanDataPathStateManager.class.getDeclaredField("mNwService");
+ field.setAccessible(true);
+ field.set(mDataPathMgr, mMockNwMgt);
+
+ field = WifiNanDataPathStateManager.class.getDeclaredField("mNiWrapper");
+ field.setAccessible(true);
+ field.set(mDataPathMgr, mMockNetworkInterface);
+ }
+
+ private NetworkRequest getSessionNetworkRequest(int clientId, int sessionId, int peerId,
+ int role, String token) throws Exception {
+ final WifiNanManager mgr = new WifiNanManager(mMockNanService);
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final PublishConfig publishConfig = new PublishConfig.Builder().build();
+
+ ArgumentCaptor<IWifiNanEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiNanEventCallback.class);
+ ArgumentCaptor<IWifiNanSessionCallback> sessionProxyCallback = ArgumentCaptor
+ .forClass(IWifiNanSessionCallback.class);
+ ArgumentCaptor<WifiNanPublishSession> publishSession = ArgumentCaptor
+ .forClass(WifiNanPublishSession.class);
+
+ WifiNanEventCallback mockCallback = mock(WifiNanEventCallback.class);
+ WifiNanSessionCallback mockSessionCallback = mock(WifiNanSessionCallback.class);
+
+ when(mMockNanService.connect(any(IBinder.class), any(IWifiNanEventCallback.class),
+ any(ConfigRequest.class))).thenReturn(clientId);
+
+ mgr.connect(mMockLooper.getLooper(), mockCallback, configRequest);
+ verify(mMockNanService).connect(any(IBinder.class), clientProxyCallback.capture(),
+ eq(configRequest));
+ clientProxyCallback.getValue().onConnectSuccess();
+ mMockLooper.dispatchAll();
+ mgr.publish(publishConfig, mockSessionCallback);
+ verify(mMockNanService).publish(eq(clientId), eq(publishConfig),
+ sessionProxyCallback.capture());
+ sessionProxyCallback.getValue().onSessionStarted(sessionId);
+ mMockLooper.dispatchAll();
+ verify(mockSessionCallback).onPublishStarted(publishSession.capture());
+
+ String ns = publishSession.getValue().createNetworkSpecifier(role, peerId,
+ (token == null) ? null : token.getBytes(), (token == null) ? 0 : token.length());
+
+ NetworkCapabilities nc = new NetworkCapabilities();
+ nc.clearAll();
+ nc.addTransportType(NetworkCapabilities.TRANSPORT_WIFI_NAN);
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN).addCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+ nc.setNetworkSpecifier(ns);
+ nc.setLinkUpstreamBandwidthKbps(1);
+ nc.setLinkDownstreamBandwidthKbps(1);
+ nc.setSignalStrength(1);
+
+ return new NetworkRequest(nc, 0, 0, NetworkRequest.Type.NONE);
+ }
+
+ private NetworkRequest getDirectNetworkRequest(int clientId, int role, byte[] peer,
+ String token) throws Exception {
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final WifiNanManager mgr = new WifiNanManager(mMockNanService);
+
+ ArgumentCaptor<IWifiNanEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiNanEventCallback.class);
+
+ WifiNanEventCallback mockCallback = mock(WifiNanEventCallback.class);
+
+ when(mMockNanService.connect(any(IBinder.class), any(IWifiNanEventCallback.class),
+ any(ConfigRequest.class))).thenReturn(clientId);
+
+ mgr.connect(mMockLooper.getLooper(), mockCallback, configRequest);
+ verify(mMockNanService).connect(any(IBinder.class), clientProxyCallback.capture(),
+ eq(configRequest));
+ clientProxyCallback.getValue().onConnectSuccess();
+ mMockLooper.dispatchAll();
+
+ String ns = mgr.createNetworkSpecifier(role, peer,
+ (token == null) ? null : token.getBytes(), (token == null) ? 0 : token.length());
+ NetworkCapabilities nc = new NetworkCapabilities();
+ nc.clearAll();
+ nc.addTransportType(NetworkCapabilities.TRANSPORT_WIFI_NAN);
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN).addCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+ nc.setNetworkSpecifier(ns);
+ nc.setLinkUpstreamBandwidthKbps(1);
+ nc.setLinkDownstreamBandwidthKbps(1);
+ nc.setSignalStrength(1);
+
+ return new NetworkRequest(nc, 0, 0, NetworkRequest.Type.REQUEST);
+ }
+
+ private Pair<Integer, Messenger> initDataPathEndPoint(int clientId, int pubSubId, int peerId,
+ byte[] peerDiscoveryMac, InOrder inOrder) throws Exception {
+ final int uid = 1000;
+ final String someMsg = "some arbitrary message from peer";
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final PublishConfig publishConfig = new PublishConfig.Builder().build();
+
+ WifiNanNative.Capabilities capabilities = new WifiNanNative.Capabilities();
+ capabilities.maxNdiInterfaces = 1;
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Messenger> messengerCaptor = ArgumentCaptor.forClass(Messenger.class);
+ ArgumentCaptor<String> strCaptor = ArgumentCaptor.forClass(String.class);
+
+ // (0) start/registrations
+ inOrder.verify(mMockCm).registerNetworkFactory(messengerCaptor.capture(),
+ strCaptor.capture());
+ collector.checkThat("factory name", "WIFI_NAN_FACTORY", equalTo(strCaptor.getValue()));
+
+ // (1) get capabilities
+ mDut.getCapabilities();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), capabilities);
+ mMockLooper.dispatchAll();
+
+ // (2) enable usage (creates interfaces)
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).createNanNetworkInterface(transactionId.capture(),
+ strCaptor.capture());
+ collector.checkThat("interface created -- 0", sNanInterfacePrefix + 0,
+ equalTo(strCaptor.getValue()));
+ mDut.onCreateDataPathInterfaceResponse(transactionId.getValue(), true, 0);
+ mMockLooper.dispatchAll();
+
+ // (3) create client & session & rx message
+ mDut.connect(clientId, uid, mMockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockCallback).onConnectSuccess();
+ mDut.publish(clientId, publishConfig, mMockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, pubSubId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockSessionCallback).onSessionStarted(sessionId.capture());
+ mDut.onMessageReceivedNotification(pubSubId, peerId, peerDiscoveryMac, someMsg.getBytes(),
+ someMsg.length());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockSessionCallback).onMessageReceived(peerId, someMsg.getBytes(),
+ someMsg.length());
+
+ return new Pair<>(sessionId.getValue(), messengerCaptor.getValue());
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/nan/WifiNanHalMock.java b/tests/wifitests/src/com/android/server/wifi/nan/WifiNanHalMock.java
index 4f3ba4d..694187e 100644
--- a/tests/wifitests/src/com/android/server/wifi/nan/WifiNanHalMock.java
+++ b/tests/wifitests/src/com/android/server/wifi/nan/WifiNanHalMock.java
@@ -39,6 +39,10 @@
throw new IllegalStateException("Please mock this class!");
}
+ public void configHalMockNative(short transactionId, String jsonArgs) {
+ throw new IllegalStateException("Please mock this class!");
+ }
+
public void disableHalMockNative(short transactionId) {
throw new IllegalStateException("Please mock this class!");
}
@@ -63,6 +67,26 @@
throw new IllegalStateException("Please mock this class!");
}
+ public void createNanNetworkInterfaceMockNative(short transactionId, String jsonArgs) {
+ throw new IllegalStateException("Please mock this class!");
+ }
+
+ public void deleteNanNetworkInterfaceMockNative(short transactionId, String jsonArgs) {
+ throw new IllegalStateException("Please mock this class!");
+ }
+
+ public void initiateDataPathMockNative(short transactionId, String jsonArgs) {
+ throw new IllegalStateException("Please mock this class!");
+ }
+
+ public void respondToDataPathRequestMockNative(short transactionId, String jsonArgs) {
+ throw new IllegalStateException("Please mock this class!");
+ }
+
+ public void endDataPathMockNative(short transactionId, String jsonArgs) {
+ throw new IllegalStateException("Please mock this class!");
+ }
+
/*
* trigger callbacks - called by test harness with arguments passed by JSON
* string.
@@ -82,15 +106,23 @@
public static native void callDisabled(String jsonArgs);
+ public static native void callTransmitFollowup(String jsonArgs);
+
+ public static native void callDataPathRequest(String jsonArgs);
+
+ public static native void callDataPathConfirm(String jsonArgs);
+
+ public static native void callDataPathEnd(String jsonArgs);
+
/**
* initialize NAN mock
*/
private static native int initNanHalMock();
- public static void initNanHalMockLibrary() throws Exception {
- Field field = WifiNanNative.class.getDeclaredField("sNanNativeInit");
+ public static void initNanHalMockLibrary(WifiNanNative instance) throws Exception {
+ Field field = WifiNanNative.class.getDeclaredField("mNativeHandlersIsInitialized");
field.setAccessible(true);
- field.setBoolean(null, true);
+ field.setBoolean(instance, true);
initNanHalMock();
}
diff --git a/tests/wifitests/src/com/android/server/wifi/nan/WifiNanHalTest.java b/tests/wifitests/src/com/android/server/wifi/nan/WifiNanHalTest.java
index 536c924..2fef83c 100644
--- a/tests/wifitests/src/com/android/server/wifi/nan/WifiNanHalTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/nan/WifiNanHalTest.java
@@ -18,16 +18,16 @@
import static org.hamcrest.core.IsEqual.equalTo;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.net.wifi.nan.ConfigRequest;
-import android.net.wifi.nan.PublishData;
-import android.net.wifi.nan.PublishSettings;
-import android.net.wifi.nan.SubscribeData;
-import android.net.wifi.nan.SubscribeSettings;
+import android.net.wifi.nan.PublishConfig;
+import android.net.wifi.nan.SubscribeConfig;
import android.net.wifi.nan.TlvBufferUtils;
-import android.net.wifi.nan.WifiNanSessionListener;
+import android.net.wifi.nan.WifiNanEventCallback;
+import android.net.wifi.nan.WifiNanSessionCallback;
import android.os.Bundle;
import android.test.suitebuilder.annotation.SmallTest;
@@ -67,8 +67,10 @@
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
+ resetWifiNanNative();
+
HalMockUtils.initHalMockLibrary();
- WifiNanHalMock.initNanHalMockLibrary();
+ WifiNanHalMock.initNanHalMockLibrary(mDut);
WifiNanNative.initNanHandlersNative(WifiNative.class, WifiNative.sWlan0Index);
HalMockUtils.setHalMockObject(mNanHalMock);
installMockNanStateManager(mNanStateManager);
@@ -97,6 +99,14 @@
}
@Test
+ public void testConfigCall() throws JSONException {
+ final short transactionId = 31235;
+ final short masterPref = 157;
+
+ testConfig(transactionId, masterPref);
+ }
+
+ @Test
public void testDisable() {
final short transactionId = 5478;
@@ -113,6 +123,7 @@
final String ssi = "some much longer and more arbitrary data";
final int publishCount = 7;
final int publishTtl = 66;
+ final boolean enableTerminateNotification = true;
TlvBufferUtils.TlvConstructor tlvTx = new TlvBufferUtils.TlvConstructor(0, 1);
tlvTx.allocate(150).putByte(0, (byte) 10).putInt(0, 100).putString(0, "some string")
@@ -122,8 +133,8 @@
tlvRx.allocate(150).putByte(0, (byte) 66).putInt(0, 127).putString(0, "some other string")
.putZeroLengthElement(0).putByteArray(0, serviceName.getBytes());
- testPublish(transactionId, publishId, PublishSettings.PUBLISH_TYPE_UNSOLICITED, serviceName,
- ssi, tlvTx, tlvRx, publishCount, publishTtl);
+ testPublish(transactionId, publishId, PublishConfig.PUBLISH_TYPE_UNSOLICITED, serviceName,
+ ssi, tlvTx, tlvRx, publishCount, publishTtl, enableTerminateNotification);
}
@Test
@@ -134,6 +145,7 @@
final String ssi = "some much longer arbitrary data";
final int publishCount = 32;
final int publishTtl = 33;
+ final boolean enableTerminateNotification = false;
TlvBufferUtils.TlvConstructor tlvTx = new TlvBufferUtils.TlvConstructor(0, 1);
tlvTx.allocate(150).putByte(0, (byte) 10).putInt(0, 100).putString(0, "some string")
@@ -143,8 +155,8 @@
tlvRx.allocate(150).putByte(0, (byte) 66).putInt(0, 127).putString(0, "some other string")
.putZeroLengthElement(0).putByteArray(0, serviceName.getBytes());
- testPublish(transactionId, publishId, PublishSettings.PUBLISH_TYPE_SOLICITED, serviceName,
- ssi, tlvTx, tlvRx, publishCount, publishTtl);
+ testPublish(transactionId, publishId, PublishConfig.PUBLISH_TYPE_SOLICITED, serviceName,
+ ssi, tlvTx, tlvRx, publishCount, publishTtl, enableTerminateNotification);
}
@Test
@@ -169,6 +181,8 @@
final String ssi = "some much longer arbitrary data";
final int subscribeCount = 32;
final int subscribeTtl = 33;
+ final int matchStyle = SubscribeConfig.MATCH_STYLE_ALL;
+ final boolean enableTerminateNotification = true;
TlvBufferUtils.TlvConstructor tlvTx = new TlvBufferUtils.TlvConstructor(0, 1);
tlvTx.allocate(150).putByte(0, (byte) 10).putInt(0, 100).putString(0, "some string")
@@ -178,8 +192,9 @@
tlvRx.allocate(150).putByte(0, (byte) 66).putInt(0, 127).putString(0, "some other string")
.putZeroLengthElement(0).putByteArray(0, serviceName.getBytes());
- testSubscribe(transactionId, subscribeId, SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE,
- serviceName, ssi, tlvTx, tlvRx, subscribeCount, subscribeTtl);
+ testSubscribe(transactionId, subscribeId, SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE,
+ serviceName, ssi, tlvTx, tlvRx, subscribeCount, subscribeTtl, matchStyle,
+ enableTerminateNotification);
}
@Test
@@ -190,6 +205,8 @@
final String ssi = "some much longer arbitrary data";
final int subscribeCount = 32;
final int subscribeTtl = 33;
+ final int matchStyle = SubscribeConfig.MATCH_STYLE_FIRST_ONLY;
+ final boolean enableTerminateNotification = false;
TlvBufferUtils.TlvConstructor tlvTx = new TlvBufferUtils.TlvConstructor(0, 1);
tlvTx.allocate(150).putByte(0, (byte) 10).putInt(0, 100).putString(0, "some string")
@@ -199,8 +216,9 @@
tlvRx.allocate(150).putByte(0, (byte) 66).putInt(0, 127).putString(0, "some other string")
.putZeroLengthElement(0).putByteArray(0, serviceName.getBytes());
- testSubscribe(transactionId, subscribeId, SubscribeSettings.SUBSCRIBE_TYPE_ACTIVE,
- serviceName, ssi, tlvTx, tlvRx, subscribeCount, subscribeTtl);
+ testSubscribe(transactionId, subscribeId, SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE,
+ serviceName, ssi, tlvTx, tlvRx, subscribeCount, subscribeTtl, matchStyle,
+ enableTerminateNotification);
}
@Test
@@ -224,9 +242,10 @@
final int reqInstanceId = 11;
final byte[] peer = HexEncoding.decode("000102030405".toCharArray(), false);
final String msg = "Hello there - how are you doing?";
+ final int messageId = 10; /* garbage - not used by HAL */
mDut.sendMessage(transactionId, pubSubId, reqInstanceId, peer, msg.getBytes(),
- msg.length());
+ msg.length(), messageId);
verify(mNanHalMock).transmitFollowupHalMockNative(eq(transactionId), mArgs.capture());
@@ -246,7 +265,7 @@
}
@Test
- public void testNotifyCapabilities() throws JSONException {
+ public void testRespondWithCapabilities() throws JSONException {
final short transactionId = 23;
final int max_concurrent_nan_clusters = 1;
final int max_publishes = 2;
@@ -260,6 +279,7 @@
final int max_ndi_interfaces = 10;
final int max_ndp_sessions = 11;
final int max_app_info_len = 12;
+ final int max_queued_transmit_followup_msgs = 7;
ArgumentCaptor<WifiNanNative.Capabilities> capabilitiesCapture = ArgumentCaptor
.forClass(WifiNanNative.Capabilities.class);
@@ -283,11 +303,13 @@
args.putInt("body.nan_capabilities.max_ndi_interfaces", max_ndi_interfaces);
args.putInt("body.nan_capabilities.max_ndp_sessions", max_ndp_sessions);
args.putInt("body.nan_capabilities.max_app_info_len", max_app_info_len);
+ args.putInt("body.nan_capabilities.max_queued_transmit_followup_msgs",
+ max_queued_transmit_followup_msgs);
WifiNanHalMock.callNotifyResponse(transactionId,
HalMockUtils.convertBundleToJson(args).toString());
- verify(mNanStateManager).onCapabilitiesUpdate(eq(transactionId),
+ verify(mNanStateManager).onCapabilitiesUpdateResponse(eq(transactionId),
capabilitiesCapture.capture());
WifiNanNative.Capabilities capabilities = capabilitiesCapture.getValue();
collector.checkThat("max_concurrent_nan_clusters", capabilities.maxConcurrentNanClusters,
@@ -312,6 +334,9 @@
equalTo(max_ndp_sessions));
collector.checkThat("max_app_info_len", capabilities.maxAppInfoLen,
equalTo(max_app_info_len));
+ collector.checkThat("max_queued_transmit_followup_msgs",
+ capabilities.maxQueuedTransmitMessages, equalTo(max_queued_transmit_followup_msgs));
+ verifyNoMoreInteractions(mNanStateManager);
}
@Test
@@ -326,7 +351,8 @@
WifiNanHalMock.callNotifyResponse(transactionId,
HalMockUtils.convertBundleToJson(args).toString());
- verify(mNanStateManager).onConfigCompleted(transactionId);
+ verify(mNanStateManager).onConfigSuccessResponse(transactionId);
+ verifyNoMoreInteractions(mNanStateManager);
}
@Test
@@ -341,8 +367,9 @@
WifiNanHalMock.callNotifyResponse(transactionId,
HalMockUtils.convertBundleToJson(args).toString());
- verify(mNanStateManager).onConfigFailed(transactionId,
- WifiNanSessionListener.FAIL_REASON_INVALID_ARGS);
+ verify(mNanStateManager).onConfigFailedResponse(transactionId,
+ WifiNanEventCallback.REASON_INVALID_ARGS);
+ verifyNoMoreInteractions(mNanStateManager);
}
@Test
@@ -359,7 +386,8 @@
WifiNanHalMock.callNotifyResponse(transactionId,
HalMockUtils.convertBundleToJson(args).toString());
- verify(mNanStateManager).onPublishSuccess(transactionId, publishId);
+ verify(mNanStateManager).onSessionConfigSuccessResponse(transactionId, true, publishId);
+ verifyNoMoreInteractions(mNanStateManager);
}
@Test
@@ -376,14 +404,14 @@
WifiNanHalMock.callNotifyResponse(transactionId,
HalMockUtils.convertBundleToJson(args).toString());
- verify(mNanStateManager).onPublishFail(transactionId,
- WifiNanSessionListener.FAIL_REASON_NO_RESOURCES);
+ verify(mNanStateManager).onSessionConfigFailResponse(transactionId, true,
+ WifiNanSessionCallback.REASON_NO_RESOURCES);
+ verifyNoMoreInteractions(mNanStateManager);
}
@Test
public void testNotifyResponsePublishCancel() throws JSONException {
final short transactionId = 23;
- final int publishId = 127;
Bundle args = new Bundle();
args.putInt("status", WifiNanNative.NAN_STATUS_SUCCESS);
@@ -393,7 +421,7 @@
WifiNanHalMock.callNotifyResponse(transactionId,
HalMockUtils.convertBundleToJson(args).toString());
- verifyZeroInteractions(mNanStateManager);
+ verifyNoMoreInteractions(mNanStateManager);
}
@Test
@@ -410,7 +438,8 @@
WifiNanHalMock.callNotifyResponse(transactionId,
HalMockUtils.convertBundleToJson(args).toString());
- verify(mNanStateManager).onSubscribeSuccess(transactionId, subscribeId);
+ verify(mNanStateManager).onSessionConfigSuccessResponse(transactionId, false, subscribeId);
+ verifyNoMoreInteractions(mNanStateManager);
}
@Test
@@ -427,24 +456,24 @@
WifiNanHalMock.callNotifyResponse(transactionId,
HalMockUtils.convertBundleToJson(args).toString());
- verify(mNanStateManager).onSubscribeFail(transactionId,
- WifiNanSessionListener.FAIL_REASON_OTHER);
+ verify(mNanStateManager).onSessionConfigFailResponse(transactionId, false,
+ WifiNanSessionCallback.REASON_OTHER);
+ verifyNoMoreInteractions(mNanStateManager);
}
@Test
public void testNotifyResponseSubscribeCancel() throws JSONException {
final short transactionId = 23;
- final int subscribeId = 127;
Bundle args = new Bundle();
- args.putInt("status", WifiNanNative.NAN_STATUS_SUCCESS);
+ args.putInt("status", WifiNanNative.NAN_STATUS_DE_FAILURE);
args.putInt("value", 0);
args.putInt("response_type", WifiNanNative.NAN_RESPONSE_SUBSCRIBE_CANCEL);
WifiNanHalMock.callNotifyResponse(transactionId,
HalMockUtils.convertBundleToJson(args).toString());
- verifyZeroInteractions(mNanStateManager);
+ verifyNoMoreInteractions(mNanStateManager);
}
@Test
@@ -459,7 +488,8 @@
WifiNanHalMock.callNotifyResponse(transactionId,
HalMockUtils.convertBundleToJson(args).toString());
- verify(mNanStateManager).onMessageSendSuccess(transactionId);
+ verify(mNanStateManager).onMessageSendQueuedSuccessResponse(transactionId);
+ verifyNoMoreInteractions(mNanStateManager);
}
@Test
@@ -474,8 +504,130 @@
WifiNanHalMock.callNotifyResponse(transactionId,
HalMockUtils.convertBundleToJson(args).toString());
- verify(mNanStateManager).onMessageSendFail(transactionId,
- WifiNanSessionListener.FAIL_REASON_OTHER);
+ verify(mNanStateManager).onMessageSendQueuedFailResponse(transactionId,
+ WifiNanSessionCallback.REASON_OTHER);
+ verifyNoMoreInteractions(mNanStateManager);
+ }
+
+ @Test
+ public void testNotifyResponseCreateDataPath() throws JSONException {
+ final short transactionId = 48;
+ final int reason = WifiNanNative.NAN_STATUS_TIMEOUT;
+
+ Bundle args = new Bundle();
+ args.putInt("status", reason);
+ args.putInt("value", 0);
+ args.putInt("response_type", WifiNanNative.NAN_RESPONSE_DP_INTERFACE_CREATE);
+
+ WifiNanHalMock.callNotifyResponse(transactionId,
+ HalMockUtils.convertBundleToJson(args).toString());
+
+ verify(mNanStateManager).onCreateDataPathInterfaceResponse(transactionId, false, reason);
+ verifyNoMoreInteractions(mNanStateManager);
+ }
+
+ @Test
+ public void testNotifyResponseDeleteDataPath() throws JSONException {
+ final short transactionId = 49;
+ final int reason = WifiNanNative.NAN_STATUS_DE_FAILURE;
+
+ Bundle args = new Bundle();
+ args.putInt("status", reason);
+ args.putInt("value", 0);
+ args.putInt("response_type", WifiNanNative.NAN_RESPONSE_DP_INTERFACE_DELETE);
+
+ WifiNanHalMock.callNotifyResponse(transactionId,
+ HalMockUtils.convertBundleToJson(args).toString());
+
+ verify(mNanStateManager).onDeleteDataPathInterfaceResponse(transactionId, false, reason);
+ verifyNoMoreInteractions(mNanStateManager);
+ }
+
+ @Test
+ public void testNotifyResponseInitiateDataPathSuccess() throws JSONException {
+ final short transactionId = 49;
+ final int ndpId = 1234;
+
+ Bundle args = new Bundle();
+ args.putInt("status", WifiNanNative.NAN_STATUS_SUCCESS);
+ args.putInt("value", 0);
+ args.putInt("response_type", WifiNanNative.NAN_RESPONSE_DP_INITIATOR_RESPONSE);
+ args.putInt("body.data_request_response.ndp_instance_id", ndpId);
+
+ WifiNanHalMock.callNotifyResponse(transactionId,
+ HalMockUtils.convertBundleToJson(args).toString());
+
+ verify(mNanStateManager).onInitiateDataPathResponseSuccess(transactionId, ndpId);
+ verifyNoMoreInteractions(mNanStateManager);
+ }
+
+ @Test
+ public void testNotifyResponseInitiateDataPathFail() throws JSONException {
+ final short transactionId = 49;
+ final int reason = WifiNanNative.NAN_STATUS_NDP_DATA_INITIATOR_REQUEST_FAILED;
+
+ Bundle args = new Bundle();
+ args.putInt("status", reason);
+ args.putInt("value", 0);
+ args.putInt("response_type", WifiNanNative.NAN_RESPONSE_DP_INITIATOR_RESPONSE);
+ args.putInt("body.data_request_response.ndp_instance_id", 5555); // NOP
+
+ WifiNanHalMock.callNotifyResponse(transactionId,
+ HalMockUtils.convertBundleToJson(args).toString());
+
+ verify(mNanStateManager).onInitiateDataPathResponseFail(transactionId, reason);
+ verifyNoMoreInteractions(mNanStateManager);
+ }
+
+ @Test
+ public void testNotifyResponseRespondToDataPathSetupRequest() throws JSONException {
+ final short transactionId = 50;
+ final int reason = WifiNanNative.NAN_STATUS_DISABLE_IN_PROGRESS;
+
+ Bundle args = new Bundle();
+ args.putInt("status", reason);
+ args.putInt("value", 0);
+ args.putInt("response_type", WifiNanNative.NAN_RESPONSE_DP_RESPONDER_RESPONSE);
+
+ WifiNanHalMock.callNotifyResponse(transactionId,
+ HalMockUtils.convertBundleToJson(args).toString());
+
+ verify(mNanStateManager).onRespondToDataPathSetupRequestResponse(transactionId, false,
+ reason);
+ verifyNoMoreInteractions(mNanStateManager);
+ }
+
+ @Test
+ public void testNotifyResponseEndDataPath() throws JSONException {
+ final short transactionId = 50;
+ final int reason = WifiNanNative.NAN_STATUS_NDP_END_FAILED;
+
+ Bundle args = new Bundle();
+ args.putInt("status", reason);
+ args.putInt("value", 0);
+ args.putInt("response_type", WifiNanNative.NAN_RESPONSE_DP_END);
+
+ WifiNanHalMock.callNotifyResponse(transactionId,
+ HalMockUtils.convertBundleToJson(args).toString());
+
+ verify(mNanStateManager).onEndDataPathResponse(transactionId, false, reason);
+ verifyNoMoreInteractions(mNanStateManager);
+ }
+
+ @Test
+ public void testNotifyResponseUnknown() throws JSONException {
+ final int invalidTransactionId = 99999;
+ final short transactionId = 46;
+
+ Bundle args = new Bundle();
+ args.putInt("status", WifiNanNative.NAN_STATUS_SUCCESS);
+ args.putInt("value", 0);
+ args.putInt("response_type", invalidTransactionId);
+
+ WifiNanHalMock.callNotifyResponse(transactionId,
+ HalMockUtils.convertBundleToJson(args).toString());
+
+ verifyNoMoreInteractions(mNanStateManager);
}
@Test
@@ -488,8 +640,9 @@
WifiNanHalMock.callPublishTerminated(HalMockUtils.convertBundleToJson(args).toString());
- verify(mNanStateManager).onPublishTerminated(publishId,
- WifiNanSessionListener.TERMINATE_REASON_DONE);
+ verify(mNanStateManager).onSessionTerminatedNotification(publishId,
+ WifiNanSessionCallback.TERMINATE_REASON_DONE, true);
+ verifyNoMoreInteractions(mNanStateManager);
}
@Test
@@ -502,8 +655,9 @@
WifiNanHalMock.callSubscribeTerminated(HalMockUtils.convertBundleToJson(args).toString());
- verify(mNanStateManager).onSubscribeTerminated(subscribeId,
- WifiNanSessionListener.TERMINATE_REASON_FAIL);
+ verify(mNanStateManager).onSessionTerminatedNotification(subscribeId,
+ WifiNanSessionCallback.TERMINATE_REASON_FAIL, false);
+ verifyNoMoreInteractions(mNanStateManager);
}
@Test
@@ -523,8 +677,9 @@
WifiNanHalMock.callFollowup(HalMockUtils.convertBundleToJson(args).toString());
- verify(mNanStateManager).onMessageReceived(pubSubId, reqInstanceId, peer,
+ verify(mNanStateManager).onMessageReceivedNotification(pubSubId, reqInstanceId, peer,
message.getBytes(), message.length());
+ verifyNoMoreInteractions(mNanStateManager);
}
@Test
@@ -546,8 +701,9 @@
WifiNanHalMock.callMatch(HalMockUtils.convertBundleToJson(args).toString());
- verify(mNanStateManager).onMatch(pubSubId, reqInstanceId, peer, ssi.getBytes(),
+ verify(mNanStateManager).onMatchNotification(pubSubId, reqInstanceId, peer, ssi.getBytes(),
ssi.length(), filter.getBytes(), filter.length());
+ verifyNoMoreInteractions(mNanStateManager);
}
@Test
@@ -560,11 +716,12 @@
WifiNanHalMock.callDiscEngEvent(HalMockUtils.convertBundleToJson(args).toString());
- verify(mNanStateManager).onInterfaceAddressChange(mac);
+ verify(mNanStateManager).onInterfaceAddressChangeNotification(mac);
+ verifyNoMoreInteractions(mNanStateManager);
}
@Test
- public void testClusterChange() throws JSONException {
+ public void testClusterJoined() throws JSONException {
final byte[] mac = HexEncoding.decode("060504030201".toCharArray(), false);
Bundle args = new Bundle();
@@ -573,8 +730,24 @@
WifiNanHalMock.callDiscEngEvent(HalMockUtils.convertBundleToJson(args).toString());
- verify(mNanStateManager).onClusterChange(WifiNanClientState.CLUSTER_CHANGE_EVENT_JOINED,
- mac);
+ verify(mNanStateManager)
+ .onClusterChangeNotification(WifiNanClientState.CLUSTER_CHANGE_EVENT_JOINED, mac);
+ verifyNoMoreInteractions(mNanStateManager);
+ }
+
+ @Test
+ public void testClusterStarted() throws JSONException {
+ final byte[] mac = HexEncoding.decode("0A0B0C0B0A00".toCharArray(), false);
+
+ Bundle args = new Bundle();
+ args.putInt("event_type", WifiNanNative.NAN_EVENT_ID_STARTED_CLUSTER);
+ args.putByteArray("data", mac);
+
+ WifiNanHalMock.callDiscEngEvent(HalMockUtils.convertBundleToJson(args).toString());
+
+ verify(mNanStateManager)
+ .onClusterChangeNotification(WifiNanClientState.CLUSTER_CHANGE_EVENT_STARTED, mac);
+ verifyNoMoreInteractions(mNanStateManager);
}
@Test
@@ -584,7 +757,206 @@
WifiNanHalMock.callDisabled(HalMockUtils.convertBundleToJson(args).toString());
- verify(mNanStateManager).onNanDown(WifiNanSessionListener.FAIL_REASON_OTHER);
+ verify(mNanStateManager).onNanDownNotification(WifiNanEventCallback.REASON_OTHER);
+ verifyNoMoreInteractions(mNanStateManager);
+ }
+
+ @Test
+ public void testTransmitFollowupSuccess() throws JSONException {
+ final short transactionId = 123;
+
+ Bundle args = new Bundle();
+ args.putInt("id", transactionId);
+ args.putInt("reason", WifiNanNative.NAN_STATUS_SUCCESS);
+
+ WifiNanHalMock.callTransmitFollowup(HalMockUtils.convertBundleToJson(args).toString());
+
+ verify(mNanStateManager).onMessageSendSuccessNotification(transactionId);
+ verifyNoMoreInteractions(mNanStateManager);
+ }
+
+ @Test
+ public void testTransmitFollowupFail() throws JSONException {
+ final short transactionId = 5689;
+
+ Bundle args = new Bundle();
+ args.putInt("id", transactionId);
+ args.putInt("reason", WifiNanNative.NAN_STATUS_TX_FAIL);
+
+ WifiNanHalMock.callTransmitFollowup(HalMockUtils.convertBundleToJson(args).toString());
+
+ verify(mNanStateManager).onMessageSendFailNotification(transactionId,
+ WifiNanSessionCallback.REASON_TX_FAIL);
+ verifyNoMoreInteractions(mNanStateManager);
+ }
+
+ @Test
+ public void testCreateNanNetworkInterface() throws JSONException {
+ final short transactionId = 10;
+ final String interfaceName = "nan0";
+
+ mDut.createNanNetworkInterface(transactionId, interfaceName);
+
+ verify(mNanHalMock).createNanNetworkInterfaceMockNative(eq(transactionId), mArgs.capture());
+
+ Bundle argsData = HalMockUtils.convertJsonToBundle(mArgs.getValue());
+
+ collector.checkThat("iface_name", new String(argsData
+ .getByteArray("iface_name")), equalTo(interfaceName));
+ }
+
+ @Test
+ public void testDeleteNanNetworkInterface() throws JSONException {
+ final short transactionId = 10;
+ final String interfaceName = "nan0";
+
+ mDut.deleteNanNetworkInterface(transactionId, interfaceName);
+
+ verify(mNanHalMock).deleteNanNetworkInterfaceMockNative(eq(transactionId), mArgs.capture());
+
+ Bundle argsData = HalMockUtils.convertJsonToBundle(mArgs.getValue());
+
+ collector.checkThat("iface_name", new String(argsData.getByteArray("iface_name")),
+ equalTo(interfaceName));
+ }
+
+ @Test
+ public void testInitiateDataPath() throws JSONException {
+ final short transactionId = 123;
+ final int pubSubId = 55;
+ final int channelRequestType = 0;
+ final int channel = 2437;
+ final byte[] peer = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false);
+ final String interfaceName = "nan1";
+ final String msg = "let me talk!";
+
+ mDut.initiateDataPath(transactionId, pubSubId, channelRequestType, channel, peer,
+ interfaceName, msg.getBytes(), msg.length());
+
+ verify(mNanHalMock).initiateDataPathMockNative(eq(transactionId), mArgs.capture());
+
+ Bundle argsData = HalMockUtils.convertJsonToBundle(mArgs.getValue());
+
+ collector.checkThat("service_instance_id", argsData.getInt("service_instance_id"),
+ equalTo(pubSubId));
+ collector.checkThat("channel_request_type", argsData.getInt("channel_request_type"),
+ equalTo(channelRequestType));
+ collector.checkThat("channel", argsData.getInt("channel"), equalTo(channel));
+ collector.checkThat("peer_disc_mac_addr", argsData.getByteArray("peer_disc_mac_addr"),
+ equalTo(peer));
+ collector.checkThat("ndp_iface", new String(argsData.getByteArray("ndp_iface")),
+ equalTo(interfaceName));
+ collector.checkThat("ndp_cfg.security_cfg", argsData.getInt("ndp_cfg.security_cfg"),
+ equalTo(0));
+ collector.checkThat("ndp_cfg.qos_cfg", argsData.getInt("ndp_cfg.qos_cfg"), equalTo(0));
+ collector.checkThat("app_info.ndp_app_info_len",
+ argsData.getInt("app_info.ndp_app_info_len"), equalTo(msg.length()));
+ collector.checkThat("app_info.ndp_app_info", argsData.getByteArray("app_info.ndp_app_info"),
+ equalTo(msg.getBytes()));
+ }
+
+ @Test
+ public void testRespondToDataPathRequest() throws JSONException {
+ final short transactionId = 123;
+ final boolean accept = true;
+ final int ndpId = 523;
+ final String interfaceName = "nan1";
+ final String msg = "fine - you can talk ...";
+
+ mDut.respondToDataPathRequest(transactionId, accept, ndpId, interfaceName, msg
+ .getBytes(), msg.length());
+
+ verify(mNanHalMock).respondToDataPathRequestMockNative(eq(transactionId), mArgs.capture());
+
+ Bundle argsData = HalMockUtils.convertJsonToBundle(mArgs.getValue());
+
+ collector.checkThat("ndp_instance_id", argsData.getInt("ndp_instance_id"), equalTo(ndpId));
+ collector.checkThat("ndp_iface", new String(argsData
+ .getByteArray("ndp_iface")), equalTo(interfaceName));
+ collector.checkThat("ndp_cfg.security_cfg", argsData
+ .getInt("ndp_cfg.security_cfg"), equalTo(0));
+ collector.checkThat("ndp_cfg.qos_cfg", argsData.getInt("ndp_cfg.qos_cfg"), equalTo(0));
+ collector.checkThat("app_info.ndp_app_info_len", argsData
+ .getInt("app_info.ndp_app_info_len"), equalTo(msg.length()));
+ collector.checkThat("app_info.ndp_app_info", argsData
+ .getByteArray("app_info.ndp_app_info"), equalTo(msg.getBytes()));
+ collector.checkThat("rsp_code", argsData.getInt("rsp_code"), equalTo(accept ? 0 : 1));
+ }
+
+ @Test
+ public void testEndDataPath() throws JSONException {
+ final short transactionId = 123;
+ final int ndpId = 523;
+
+ mDut.endDataPath(transactionId, ndpId);
+
+ verify(mNanHalMock).endDataPathMockNative(eq(transactionId), mArgs.capture());
+
+ Bundle argsData = HalMockUtils.convertJsonToBundle(mArgs.getValue());
+
+ collector.checkThat("num_ndp_instances", argsData.getInt("num_ndp_instances"), equalTo(1));
+ collector.checkThat("ndp_instance_id", argsData.getInt("ndp_instance_id"), equalTo(ndpId));
+ }
+
+ @Test
+ public void testOnDataRequest() throws JSONException {
+ final int pubSubId = 1234;
+ final byte[] peer = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false);
+ final int ndpId = 752;
+ final String msg = "some request or other - doesn't have to be a String!";
+
+ Bundle args = new Bundle();
+ args.putInt("service_instance_id", pubSubId);
+ args.putByteArray("peer_disc_mac_addr", peer);
+ args.putInt("ndp_instance_id", ndpId);
+ args.putInt("ndp_cfg.security_cfg", 0);
+ args.putInt("ndp_cfg.qos_cfg", 0);
+ args.putInt("app_info.ndp_app_info_len", msg.length());
+ args.putByteArray("app_info.ndp_app_info", msg.getBytes());
+
+ WifiNanHalMock.callDataPathRequest(HalMockUtils.convertBundleToJson(args).toString());
+
+ verify(mNanStateManager).onDataPathRequestNotification(pubSubId, peer, ndpId,
+ msg.getBytes(), msg.length());
+ verifyNoMoreInteractions(mNanStateManager);
+ }
+
+ @Test
+ public void testOnDataConfirm() throws JSONException {
+ final int ndpId = 752;
+ final byte[] peer = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false);
+ final String msg = "some request or other - doesn't have to be a String!";
+ final boolean accept = true;
+ final int reason = 13412;
+
+ Bundle args = new Bundle();
+ args.putInt("ndp_instance_id", ndpId);
+ args.putByteArray("peer_ndi_mac_addr", peer);
+ args.putInt("app_info.ndp_app_info_len", msg.length());
+ args.putByteArray("app_info.ndp_app_info", msg.getBytes());
+ args.putInt("rsp_code", accept ? 0 : 1);
+ args.putInt("reason_code", reason);
+
+ WifiNanHalMock.callDataPathConfirm(HalMockUtils.convertBundleToJson(args).toString());
+
+ verify(mNanStateManager).onDataPathConfirmNotification(ndpId, peer, accept, reason,
+ msg.getBytes(), msg.length());
+ verifyNoMoreInteractions(mNanStateManager);
+ }
+
+ @Test
+ public void testOnDataEnd() throws JSONException {
+ testOnDataEndMultiples(1, 752);
+ }
+
+ @Test
+ public void testOnDataEndMultiples() throws JSONException {
+ testOnDataEndMultiples(5, 842);
+ }
+
+ @Test
+ public void testOnDataEndZero() throws JSONException {
+ testOnDataEndMultiples(0, 2134);
}
/*
@@ -597,7 +969,7 @@
.setClusterHigh(clusterHigh).setMasterPreference(masterPref)
.setSupport5gBand(enable5g).build();
- mDut.enableAndConfigure(transactionId, configRequest);
+ mDut.enableAndConfigure(transactionId, configRequest, true);
verify(mNanHalMock).enableHalMockNative(eq(transactionId), mArgs.capture());
@@ -646,19 +1018,63 @@
equalTo(0));
}
+ private void testConfig(short transactionId, int masterPref) throws JSONException {
+ ConfigRequest configRequest = new ConfigRequest.Builder().setMasterPreference(masterPref)
+ .build();
+
+ mDut.enableAndConfigure(transactionId, configRequest, false);
+
+ verify(mNanHalMock).configHalMockNative(eq(transactionId), mArgs.capture());
+
+ Bundle argsData = HalMockUtils.convertJsonToBundle(mArgs.getValue());
+
+ collector.checkThat("config_master_pref", argsData.getInt("config_master_pref"),
+ equalTo(1));
+ collector.checkThat("master_pref", argsData.getInt("master_pref"), equalTo(masterPref));
+
+ collector.checkThat("config_sid_beacon", argsData.getInt("config_sid_beacon"), equalTo(0));
+ collector.checkThat("sid_beacon", argsData.getInt("sid_beacon"), equalTo(0));
+ collector.checkThat("config_rssi_proximity", argsData.getInt("config_rssi_proximity"),
+ equalTo(0));
+ collector.checkThat("rssi_proximity", argsData.getInt("rssi_proximity"), equalTo(0));
+ collector.checkThat("config_5g_rssi_close_proximity",
+ argsData.getInt("config_5g_rssi_close_proximity"), equalTo(0));
+ collector.checkThat("rssi_close_proximity_5g_val",
+ argsData.getInt("rssi_close_proximity_5g_val"), equalTo(0));
+ collector.checkThat("config_rssi_window_size", argsData.getInt("config_rssi_window_size"),
+ equalTo(0));
+ collector.checkThat("rssi_window_size_val", argsData.getInt("rssi_window_size_val"),
+ equalTo(0));
+ collector.checkThat("config_cluster_attribute_val",
+ argsData.getInt("config_cluster_attribute_val"), equalTo(0));
+ collector.checkThat("config_scan_params", argsData.getInt("config_scan_params"),
+ equalTo(0));
+ collector.checkThat("config_random_factor_force",
+ argsData.getInt("config_random_factor_force"), equalTo(0));
+ collector.checkThat("random_factor_force_val", argsData.getInt("random_factor_force_val"),
+ equalTo(0));
+ collector.checkThat("config_hop_count_force", argsData.getInt("config_hop_count_force"),
+ equalTo(0));
+ collector.checkThat("hop_count_force_val", argsData.getInt("hop_count_force_val"),
+ equalTo(0));
+ collector.checkThat("config_conn_capability", argsData.getInt("config_conn_capability"),
+ equalTo(0));
+ collector.checkThat("num_config_discovery_attr",
+ argsData.getInt("num_config_discovery_attr"), equalTo(0));
+ collector.checkThat("config_fam", argsData.getInt("config_fam"), equalTo(0));
+ }
+
private void testPublish(short transactionId, int publishId, int publishType,
String serviceName, String ssi, TlvBufferUtils.TlvConstructor tlvTx,
- TlvBufferUtils.TlvConstructor tlvRx, int publishCount, int publishTtl)
- throws JSONException {
- PublishData publishData = new PublishData.Builder().setServiceName(serviceName)
- .setServiceSpecificInfo(ssi)
- .setTxFilter(tlvTx.getArray(), tlvTx.getActualLength())
- .setRxFilter(tlvRx.getArray(), tlvRx.getActualLength()).build();
+ TlvBufferUtils.TlvConstructor tlvRx, int publishCount, int publishTtl,
+ boolean enableTerminateNotification) throws JSONException {
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
+ .setServiceSpecificInfo(ssi).setTxFilter(tlvTx.getArray(), tlvTx.getActualLength())
+ .setRxFilter(tlvRx.getArray(), tlvRx.getActualLength()).setPublishType(publishType)
+ .setPublishCount(publishCount).setTtlSec(publishTtl)
+ .setEnableTerminateNotification(enableTerminateNotification).build();
- PublishSettings publishSettings = new PublishSettings.Builder().setPublishType(publishType)
- .setPublishCount(publishCount).setTtlSec(publishTtl).build();
-
- mDut.publish(transactionId, publishId, publishData, publishSettings);
+ mDut.publish(transactionId, publishId, publishConfig);
verify(mNanHalMock).publishHalMockNative(eq(transactionId), mArgs.capture());
@@ -668,7 +1084,7 @@
collector.checkThat("ttl", argsData.getInt("ttl"), equalTo(publishTtl));
collector.checkThat("publish_type", argsData.getInt("publish_type"), equalTo(publishType));
collector.checkThat("tx_type", argsData.getInt("tx_type"),
- equalTo(publishType == PublishSettings.PUBLISH_TYPE_UNSOLICITED ? 0 : 1));
+ equalTo(publishType == PublishConfig.PUBLISH_TYPE_UNSOLICITED ? 0 : 1));
collector.checkThat("publish_count", argsData.getInt("publish_count"),
equalTo(publishCount));
collector.checkThat("service_name_len", argsData.getInt("service_name_len"),
@@ -692,22 +1108,23 @@
collector.checkThat("rssi_threshold_flag", argsData.getInt("rssi_threshold_flag"),
equalTo(0));
collector.checkThat("connmap", argsData.getInt("connmap"), equalTo(0));
+ collector.checkThat("recv_indication_cfg", argsData.getInt("recv_indication_cfg"),
+ equalTo(enableTerminateNotification ? 0x0 : 0x1));
}
private void testSubscribe(short transactionId, int subscribeId, int subscribeType,
String serviceName, String ssi, TlvBufferUtils.TlvConstructor tlvTx,
- TlvBufferUtils.TlvConstructor tlvRx, int subscribeCount, int subscribeTtl)
- throws JSONException {
- SubscribeData subscribeData = new SubscribeData.Builder().setServiceName(serviceName)
+ TlvBufferUtils.TlvConstructor tlvRx, int subscribeCount, int subscribeTtl,
+ int matchStyle, boolean enableTerminateNotification) throws JSONException {
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
.setServiceSpecificInfo(ssi)
.setTxFilter(tlvTx.getArray(), tlvTx.getActualLength())
- .setRxFilter(tlvRx.getArray(), tlvRx.getActualLength()).build();
-
- SubscribeSettings subscribeSettings = new SubscribeSettings.Builder()
+ .setRxFilter(tlvRx.getArray(), tlvRx.getActualLength())
.setSubscribeType(subscribeType).setSubscribeCount(subscribeCount)
- .setTtlSec(subscribeTtl).build();
+ .setTtlSec(subscribeTtl).setMatchStyle(matchStyle)
+ .setEnableTerminateNotification(enableTerminateNotification).build();
- mDut.subscribe(transactionId, subscribeId, subscribeData, subscribeSettings);
+ mDut.subscribe(transactionId, subscribeId, subscribeConfig);
verify(mNanHalMock).subscribeHalMockNative(eq(transactionId), mArgs.capture());
@@ -727,7 +1144,7 @@
collector.checkThat("ssiRequiredForMatchIndication",
argsData.getInt("ssiRequiredForMatchIndication"), equalTo(0));
collector.checkThat("subscribe_match_indicator",
- argsData.getInt("subscribe_match_indicator"), equalTo(0));
+ argsData.getInt("subscribe_match_indicator"), equalTo(matchStyle));
collector.checkThat("subscribe_count", argsData.getInt("subscribe_count"),
equalTo(subscribeCount));
collector.checkThat("service_name_len", argsData.getInt("service_name_len"),
@@ -751,6 +1168,27 @@
collector.checkThat("connmap", argsData.getInt("connmap"), equalTo(0));
collector.checkThat("num_intf_addr_present", argsData.getInt("num_intf_addr_present"),
equalTo(0));
+ collector.checkThat("recv_indication_cfg", argsData.getInt("recv_indication_cfg"),
+ equalTo(enableTerminateNotification ? 0x0 : 0x1));
+ }
+
+ private void testOnDataEndMultiples(int numInstances, int ndpIdBase) throws JSONException {
+ ArgumentCaptor<Integer> ndpIdsCaptor = ArgumentCaptor.forClass(Integer.class);
+
+ Bundle args = new Bundle();
+ args.putInt("num_ndp_instances", numInstances);
+ args.putInt("ndp_instance_id", ndpIdBase);
+
+ WifiNanHalMock.callDataPathEnd(HalMockUtils.convertBundleToJson(args).toString());
+
+ verify(mNanStateManager, times(numInstances)).onDataPathEndNotification(
+ ndpIdsCaptor.capture());
+ verifyNoMoreInteractions(mNanStateManager);
+
+ for (int i = 0; i < numInstances; ++i) {
+ collector.checkThat("ndp id #" + i, ndpIdsCaptor.getAllValues().get(i),
+ equalTo(ndpIdBase + i));
+ }
}
private static void installMockNanStateManager(WifiNanStateManager nanStateManager)
@@ -759,4 +1197,10 @@
field.setAccessible(true);
field.set(null, nanStateManager);
}
+
+ private static void resetWifiNanNative() throws Exception {
+ Field field = WifiNanNative.class.getDeclaredField("sWifiNanNativeSingleton");
+ field.setAccessible(true);
+ field.set(null, null);
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/nan/WifiNanManagerTest.java b/tests/wifitests/src/com/android/server/wifi/nan/WifiNanManagerTest.java
index bd4d43b..e143fd8 100644
--- a/tests/wifitests/src/com/android/server/wifi/nan/WifiNanManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/nan/WifiNanManagerTest.java
@@ -18,107 +18,609 @@
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+import android.net.wifi.RttManager;
import android.net.wifi.nan.ConfigRequest;
-import android.net.wifi.nan.PublishData;
-import android.net.wifi.nan.PublishSettings;
-import android.net.wifi.nan.SubscribeData;
-import android.net.wifi.nan.SubscribeSettings;
+import android.net.wifi.nan.IWifiNanEventCallback;
+import android.net.wifi.nan.IWifiNanManager;
+import android.net.wifi.nan.IWifiNanSessionCallback;
+import android.net.wifi.nan.PublishConfig;
+import android.net.wifi.nan.SubscribeConfig;
+import android.net.wifi.nan.WifiNanEventCallback;
+import android.net.wifi.nan.WifiNanManager;
+import android.net.wifi.nan.WifiNanPublishSession;
+import android.net.wifi.nan.WifiNanSessionCallback;
+import android.net.wifi.nan.WifiNanSubscribeSession;
+import android.os.IBinder;
import android.os.Parcel;
+import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Base64;
+import libcore.util.HexEncoding;
+
+import org.json.JSONObject;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
-import org.junit.rules.ExpectedException;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
/**
* Unit test harness for WifiNanManager class.
*/
@SmallTest
public class WifiNanManagerTest {
+ private WifiNanManager mDut;
+ private TestLooper mMockLooper;
+
@Rule
public ErrorCollector collector = new ErrorCollector();
- @Rule
- public ExpectedException thrown = ExpectedException.none();
+ @Mock
+ public WifiNanEventCallback mockCallback;
+
+ @Mock
+ public WifiNanSessionCallback mockSessionCallback;
+
+ @Mock
+ public IWifiNanManager mockNanService;
+
+ @Mock
+ public WifiNanPublishSession mockPublishSession;
+
+ @Mock
+ public WifiNanSubscribeSession mockSubscribeSession;
+
+ @Mock
+ public RttManager.RttListener mockRttListener;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mDut = new WifiNanManager(mockNanService);
+ mMockLooper = new TestLooper();
+ }
+
+ /*
+ * Straight pass-through tests
+ */
+
+ /**
+ * Validate pass-through of enableUsage() API.
+ */
+ @Test
+ public void testEnableUsage() throws Exception {
+ mDut.enableUsage();
+
+ verify(mockNanService).enableUsage();
+ }
+
+ /**
+ * Validate pass-through of disableUsage() API.
+ */
+ @Test
+ public void testDisableUsage() throws Exception {
+ mDut.disableUsage();
+
+ verify(mockNanService).disableUsage();
+ }
+
+ /**
+ * Validate pass-through of isUsageEnabled() API.
+ */
+ @Test
+ public void testIsUsageEnable() throws Exception {
+ mDut.isUsageEnabled();
+
+ verify(mockNanService).isUsageEnabled();
+ }
+
+ /*
+ * WifiNanEventCallbackProxy Tests
+ */
+
+ /**
+ * Validate the successful connect flow: (1) try subscribing (2) connect +
+ * success (3) publish, (4) disconnect (5) try publishing (6) connect again
+ */
+ @Test
+ public void testConnectFlow() throws Exception {
+ final int clientId = 4565;
+
+ when(mockNanService.connect(any(IBinder.class), any(IWifiNanEventCallback.class),
+ any(ConfigRequest.class))).thenReturn(clientId);
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockNanService);
+ ArgumentCaptor<IWifiNanEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiNanEventCallback.class);
+ ArgumentCaptor<IBinder> binder = ArgumentCaptor.forClass(IBinder.class);
+
+ // (1) try subscribing on an unconnected manager: fails silently
+ mDut.subscribe(new SubscribeConfig.Builder().build(), mockSessionCallback);
+
+ // (2) connect + success
+ mDut.connect(mMockLooper.getLooper(), mockCallback);
+ inOrder.verify(mockNanService).connect(binder.capture(),
+ clientProxyCallback.capture(), (ConfigRequest) isNull());
+ clientProxyCallback.getValue().onConnectSuccess();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (3) publish - should succeed
+ PublishConfig publishConfig = new PublishConfig.Builder().build();
+ mDut.publish(publishConfig, mockSessionCallback);
+ inOrder.verify(mockNanService).publish(eq(clientId), eq(publishConfig),
+ any(IWifiNanSessionCallback.class));
+
+ // (4) disconnect
+ mDut.disconnect();
+ inOrder.verify(mockNanService).disconnect(eq(clientId), eq(binder.getValue()));
+
+ // (5) try publishing again - fails silently
+ mDut.publish(new PublishConfig.Builder().build(), mockSessionCallback);
+
+ // (6) connect
+ mDut.connect(mMockLooper.getLooper(), mockCallback);
+ inOrder.verify(mockNanService).connect(binder.capture(), any(IWifiNanEventCallback.class),
+ (ConfigRequest) isNull());
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockNanService);
+ }
+
+ /**
+ * Validate the failed connect flow: (1) connect + failure, (2) try
+ * publishing (3) connect + success (4) subscribe
+ */
+ @Test
+ public void testConnectFailure() throws Exception {
+ final int clientId = 4565;
+ final int reason = WifiNanEventCallback.REASON_OTHER;
+
+ when(mockNanService.connect(any(IBinder.class), any(IWifiNanEventCallback.class),
+ any(ConfigRequest.class))).thenReturn(clientId);
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockNanService);
+ ArgumentCaptor<IWifiNanEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiNanEventCallback.class);
+
+ // (1) connect + failure
+ mDut.connect(mMockLooper.getLooper(), mockCallback);
+ inOrder.verify(mockNanService).connect(any(IBinder.class), clientProxyCallback.capture(),
+ (ConfigRequest) isNull());
+ clientProxyCallback.getValue().onConnectFail(reason);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectFail(reason);
+
+ // (2) try publishing - silent failure (since already know that no
+ // connection)
+ mDut.publish(new PublishConfig.Builder().build(), mockSessionCallback);
+
+ // (3) connect + success
+ mDut.connect(mMockLooper.getLooper(), mockCallback);
+ inOrder.verify(mockNanService).connect(any(IBinder.class), clientProxyCallback.capture(),
+ (ConfigRequest) isNull());
+ clientProxyCallback.getValue().onConnectSuccess();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (4) subscribe: should succeed
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+ mDut.subscribe(subscribeConfig, mockSessionCallback);
+ inOrder.verify(mockNanService).subscribe(eq(clientId), eq(subscribeConfig),
+ any(IWifiNanSessionCallback.class));
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockNanService);
+ }
+
+ /**
+ * Validate that cannot call connect on an existing connection: (1) connect
+ * + success, (2) try connect again
+ */
+ @Test
+ public void testInvalidConnectSequence() throws Exception {
+ final int clientId = 4565;
+
+ when(mockNanService.connect(any(IBinder.class), any(IWifiNanEventCallback.class),
+ any(ConfigRequest.class))).thenReturn(clientId);
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockNanService);
+ ArgumentCaptor<IWifiNanEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiNanEventCallback.class);
+
+ // (1) connect + success
+ mDut.connect(mMockLooper.getLooper(), mockCallback);
+ inOrder.verify(mockNanService).connect(any(IBinder.class), clientProxyCallback.capture(),
+ (ConfigRequest) isNull());
+ clientProxyCallback.getValue().onConnectSuccess();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (2) connect - forward to service (though will fail silently)
+ mDut.connect(mMockLooper.getLooper(), mockCallback);
+ inOrder.verify(mockNanService).connect(any(IBinder.class), clientProxyCallback.capture(),
+ (ConfigRequest) isNull());
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockNanService);
+ }
+
+ /*
+ * WifiNanSessionCallbackProxy Tests
+ */
+
+ /**
+ * Validate the publish flow: (0) connect + success, (1) publish, (2)
+ * success creates session, (3) pass through everything, (4) update publish
+ * through session, (5) terminate locally, (6) try another command -
+ * ignored.
+ */
+ @Test
+ public void testPublishFlow() throws Exception {
+ final int clientId = 4565;
+ final int sessionId = 123;
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final PublishConfig publishConfig = new PublishConfig.Builder().build();
+ final int peerId = 873;
+ final String string1 = "hey from here...";
+ final String string2 = "some other arbitrary string...";
+ final int messageId = 2123;
+ final int reason = WifiNanSessionCallback.REASON_OTHER;
+
+ when(mockNanService.connect(any(IBinder.class), any(IWifiNanEventCallback.class),
+ eq(configRequest))).thenReturn(clientId);
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockNanService,
+ mockPublishSession);
+ ArgumentCaptor<IWifiNanEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiNanEventCallback.class);
+ ArgumentCaptor<IWifiNanSessionCallback> sessionProxyCallback = ArgumentCaptor
+ .forClass(IWifiNanSessionCallback.class);
+ ArgumentCaptor<WifiNanPublishSession> publishSession = ArgumentCaptor
+ .forClass(WifiNanPublishSession.class);
+
+ // (0) connect + success
+ mDut.connect(mMockLooper.getLooper(), mockCallback, configRequest);
+ inOrder.verify(mockNanService).connect(any(IBinder.class),
+ clientProxyCallback.capture(), eq(configRequest));
+ clientProxyCallback.getValue().onConnectSuccess();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (1) publish
+ mDut.publish(publishConfig, mockSessionCallback);
+ inOrder.verify(mockNanService).publish(eq(clientId), eq(publishConfig),
+ sessionProxyCallback.capture());
+
+ // (2) publish session created
+ sessionProxyCallback.getValue().onSessionStarted(sessionId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
+
+ // (3) ...
+ publishSession.getValue().sendMessage(peerId, string1.getBytes(), string1.length(),
+ messageId);
+ sessionProxyCallback.getValue().onMatch(peerId, string1.getBytes(),
+ string1.length(), string2.getBytes(), string2.length());
+ sessionProxyCallback.getValue().onMessageReceived(peerId, string1.getBytes(),
+ string1.length());
+ sessionProxyCallback.getValue().onMessageSendFail(messageId, reason);
+ sessionProxyCallback.getValue().onMessageSendSuccess(messageId);
+ mMockLooper.dispatchAll();
+
+ inOrder.verify(mockNanService).sendMessage(eq(clientId), eq(sessionId), eq(peerId),
+ eq(string1.getBytes()), eq(string1.length()), eq(messageId), eq(0));
+ inOrder.verify(mockSessionCallback).onMatch(eq(peerId), eq(string1.getBytes()),
+ eq(string1.length()), eq(string2.getBytes()), eq(string2.length()));
+ inOrder.verify(mockSessionCallback).onMessageReceived(eq(peerId), eq(string1.getBytes()),
+ eq(string1.length()));
+ inOrder.verify(mockSessionCallback).onMessageSendFail(eq(messageId), eq(reason));
+ inOrder.verify(mockSessionCallback).onMessageSendSuccess(eq(messageId));
+
+ // (4) update publish
+ publishSession.getValue().updatePublish(publishConfig);
+ sessionProxyCallback.getValue().onSessionConfigFail(reason);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockNanService).updatePublish(eq(clientId), eq(sessionId),
+ eq(publishConfig));
+ inOrder.verify(mockSessionCallback).onSessionConfigFail(eq(reason));
+
+ // (5) terminate
+ publishSession.getValue().terminate();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockNanService).terminateSession(clientId, sessionId);
+
+ // (6) try an update (nothing)
+ publishSession.getValue().updatePublish(publishConfig);
+ mMockLooper.dispatchAll();
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockNanService,
+ mockPublishSession);
+ }
+
+ /**
+ * Validate race condition of session terminate and session action: (1)
+ * connect, (2) publish success + terminate, (3) update.
+ */
+ @Test
+ public void testPublishRemoteTerminate() throws Exception {
+ final int clientId = 4565;
+ final int sessionId = 123;
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final PublishConfig publishConfig = new PublishConfig.Builder().build();
+ final int reason = WifiNanSessionCallback.TERMINATE_REASON_DONE;
+
+ when(mockNanService.connect(any(IBinder.class), any(IWifiNanEventCallback.class),
+ eq(configRequest))).thenReturn(clientId);
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockNanService,
+ mockPublishSession);
+ ArgumentCaptor<IWifiNanEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiNanEventCallback.class);
+ ArgumentCaptor<IWifiNanSessionCallback> sessionProxyCallback = ArgumentCaptor
+ .forClass(IWifiNanSessionCallback.class);
+ ArgumentCaptor<WifiNanPublishSession> publishSession = ArgumentCaptor
+ .forClass(WifiNanPublishSession.class);
+
+ // (1) connect successfully
+ mDut.connect(mMockLooper.getLooper(), mockCallback, configRequest);
+ inOrder.verify(mockNanService).connect(any(IBinder.class), clientProxyCallback.capture(),
+ eq(configRequest));
+ clientProxyCallback.getValue().onConnectSuccess();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (2) publish: successfully - then terminated
+ mDut.publish(publishConfig, mockSessionCallback);
+ inOrder.verify(mockNanService).publish(eq(clientId), eq(publishConfig),
+ sessionProxyCallback.capture());
+ sessionProxyCallback.getValue().onSessionStarted(sessionId);
+ sessionProxyCallback.getValue().onSessionTerminated(reason);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
+ inOrder.verify(mockSessionCallback).onSessionTerminated(reason);
+
+ // (3) failure when trying to update: NOP
+ publishSession.getValue().updatePublish(publishConfig);
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockNanService,
+ mockPublishSession);
+ }
+
+ /**
+ * Validate the subscribe flow: (0) connect + success, (1) subscribe, (2)
+ * success creates session, (3) pass through everything, (4) update
+ * subscribe through session, (5) terminate locally, (6) try another command
+ * - ignored.
+ */
+ @Test
+ public void testSubscribeFlow() throws Exception {
+ final int clientId = 4565;
+ final int sessionId = 123;
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+ final int peerId = 873;
+ final String string1 = "hey from here...";
+ final String string2 = "some other arbitrary string...";
+ final int messageId = 2123;
+ final int reason = WifiNanSessionCallback.REASON_OTHER;
+
+ when(mockNanService.connect(any(IBinder.class), any(IWifiNanEventCallback.class),
+ eq(configRequest))).thenReturn(clientId);
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockNanService,
+ mockSubscribeSession);
+ ArgumentCaptor<IWifiNanEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiNanEventCallback.class);
+ ArgumentCaptor<IWifiNanSessionCallback> sessionProxyCallback = ArgumentCaptor
+ .forClass(IWifiNanSessionCallback.class);
+ ArgumentCaptor<WifiNanSubscribeSession> subscribeSession = ArgumentCaptor
+ .forClass(WifiNanSubscribeSession.class);
+
+ // (0) connect + success
+ mDut.connect(mMockLooper.getLooper(), mockCallback, configRequest);
+ inOrder.verify(mockNanService).connect(any(IBinder.class), clientProxyCallback.capture(),
+ eq(configRequest));
+ clientProxyCallback.getValue().onConnectSuccess();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (1) subscribe
+ mDut.subscribe(subscribeConfig, mockSessionCallback);
+ inOrder.verify(mockNanService).subscribe(eq(clientId), eq(subscribeConfig),
+ sessionProxyCallback.capture());
+
+ // (2) subscribe session created
+ sessionProxyCallback.getValue().onSessionStarted(sessionId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSubscribeStarted(subscribeSession.capture());
+
+ // (3) ...
+ subscribeSession.getValue().sendMessage(peerId, string1.getBytes(), string1.length(),
+ messageId);
+ sessionProxyCallback.getValue().onMatch(peerId, string1.getBytes(), string1.length(),
+ string2.getBytes(), string2.length());
+ sessionProxyCallback.getValue().onMessageReceived(peerId, string1.getBytes(),
+ string1.length());
+ sessionProxyCallback.getValue().onMessageSendFail(messageId, reason);
+ sessionProxyCallback.getValue().onMessageSendSuccess(messageId);
+ mMockLooper.dispatchAll();
+
+ inOrder.verify(mockNanService).sendMessage(eq(clientId), eq(sessionId), eq(peerId),
+ eq(string1.getBytes()), eq(string1.length()), eq(messageId), eq(0));
+ inOrder.verify(mockSessionCallback).onMatch(eq(peerId), eq(string1.getBytes()),
+ eq(string1.length()), eq(string2.getBytes()), eq(string2.length()));
+ inOrder.verify(mockSessionCallback).onMessageReceived(eq(peerId), eq(string1.getBytes()),
+ eq(string1.length()));
+ inOrder.verify(mockSessionCallback).onMessageSendFail(eq(messageId), eq(reason));
+ inOrder.verify(mockSessionCallback).onMessageSendSuccess(eq(messageId));
+
+ // (4) update subscribe
+ subscribeSession.getValue().updateSubscribe(subscribeConfig);
+ sessionProxyCallback.getValue().onSessionConfigFail(reason);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockNanService).updateSubscribe(eq(clientId), eq(sessionId),
+ eq(subscribeConfig));
+ inOrder.verify(mockSessionCallback).onSessionConfigFail(eq(reason));
+
+ // (5) terminate
+ subscribeSession.getValue().terminate();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockNanService).terminateSession(clientId, sessionId);
+
+ // (6) try an update (nothing)
+ subscribeSession.getValue().updateSubscribe(subscribeConfig);
+ mMockLooper.dispatchAll();
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockNanService,
+ mockSubscribeSession);
+ }
+
+ /**
+ * Validate race condition of session terminate and session action: (1)
+ * connect, (2) subscribe success + terminate, (3) update.
+ */
+ @Test
+ public void testSubscribeRemoteTerminate() throws Exception {
+ final int clientId = 4565;
+ final int sessionId = 123;
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+ final int reason = WifiNanSessionCallback.TERMINATE_REASON_DONE;
+
+ when(mockNanService.connect(any(IBinder.class), any(IWifiNanEventCallback.class),
+ eq(configRequest))).thenReturn(clientId);
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockNanService,
+ mockSubscribeSession);
+ ArgumentCaptor<IWifiNanEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiNanEventCallback.class);
+ ArgumentCaptor<IWifiNanSessionCallback> sessionProxyCallback = ArgumentCaptor
+ .forClass(IWifiNanSessionCallback.class);
+ ArgumentCaptor<WifiNanSubscribeSession> subscribeSession = ArgumentCaptor
+ .forClass(WifiNanSubscribeSession.class);
+
+ // (1) connect successfully
+ mDut.connect(mMockLooper.getLooper(), mockCallback, configRequest);
+ inOrder.verify(mockNanService).connect(any(IBinder.class), clientProxyCallback.capture(),
+ eq(configRequest));
+ clientProxyCallback.getValue().onConnectSuccess();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (2) subscribe: successfully - then terminated
+ mDut.subscribe(subscribeConfig, mockSessionCallback);
+ inOrder.verify(mockNanService).subscribe(eq(clientId), eq(subscribeConfig),
+ sessionProxyCallback.capture());
+ sessionProxyCallback.getValue().onSessionStarted(sessionId);
+ sessionProxyCallback.getValue().onSessionTerminated(reason);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSubscribeStarted(subscribeSession.capture());
+ inOrder.verify(mockSessionCallback).onSessionTerminated(reason);
+
+ // (3) failure when trying to update: NOP
+ subscribeSession.getValue().updateSubscribe(subscribeConfig);
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockNanService,
+ mockSubscribeSession);
+ }
/*
* ConfigRequest Tests
*/
@Test
+ public void testConfigRequestBuilderDefaults() {
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+
+ collector.checkThat("mClusterHigh", ConfigRequest.CLUSTER_ID_MAX,
+ equalTo(configRequest.mClusterHigh));
+ collector.checkThat("mClusterLow", ConfigRequest.CLUSTER_ID_MIN,
+ equalTo(configRequest.mClusterLow));
+ collector.checkThat("mMasterPreference", 0,
+ equalTo(configRequest.mMasterPreference));
+ collector.checkThat("mSupport5gBand", false, equalTo(configRequest.mSupport5gBand));
+ collector.checkThat("mEnableIdentityChangeCallback", false,
+ equalTo(configRequest.mEnableIdentityChangeCallback));
+ }
+
+ @Test
public void testConfigRequestBuilder() {
final int clusterHigh = 100;
final int clusterLow = 5;
final int masterPreference = 55;
final boolean supportBand5g = true;
+ final boolean enableIdentityChangeCallback = true;
ConfigRequest configRequest = new ConfigRequest.Builder().setClusterHigh(clusterHigh)
.setClusterLow(clusterLow).setMasterPreference(masterPreference)
- .setSupport5gBand(supportBand5g).build();
+ .setSupport5gBand(supportBand5g)
+ .setEnableIdentityChangeCallback(enableIdentityChangeCallback).build();
collector.checkThat("mClusterHigh", clusterHigh, equalTo(configRequest.mClusterHigh));
collector.checkThat("mClusterLow", clusterLow, equalTo(configRequest.mClusterLow));
collector.checkThat("mMasterPreference", masterPreference,
equalTo(configRequest.mMasterPreference));
collector.checkThat("mSupport5gBand", supportBand5g, equalTo(configRequest.mSupport5gBand));
+ collector.checkThat("mEnableIdentityChangeCallback", enableIdentityChangeCallback,
+ equalTo(configRequest.mEnableIdentityChangeCallback));
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testConfigRequestBuilderMasterPrefNegative() {
- thrown.expect(IllegalArgumentException.class);
ConfigRequest.Builder builder = new ConfigRequest.Builder();
builder.setMasterPreference(-1);
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testConfigRequestBuilderMasterPrefReserved1() {
- thrown.expect(IllegalArgumentException.class);
new ConfigRequest.Builder().setMasterPreference(1);
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testConfigRequestBuilderMasterPrefReserved255() {
- thrown.expect(IllegalArgumentException.class);
new ConfigRequest.Builder().setMasterPreference(255);
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testConfigRequestBuilderMasterPrefTooLarge() {
- thrown.expect(IllegalArgumentException.class);
new ConfigRequest.Builder().setMasterPreference(256);
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testConfigRequestBuilderClusterLowNegative() {
- thrown.expect(IllegalArgumentException.class);
new ConfigRequest.Builder().setClusterLow(-1);
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testConfigRequestBuilderClusterHighNegative() {
- thrown.expect(IllegalArgumentException.class);
new ConfigRequest.Builder().setClusterHigh(-1);
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testConfigRequestBuilderClusterLowAboveMax() {
- thrown.expect(IllegalArgumentException.class);
new ConfigRequest.Builder().setClusterLow(ConfigRequest.CLUSTER_ID_MAX + 1);
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testConfigRequestBuilderClusterHighAboveMax() {
- thrown.expect(IllegalArgumentException.class);
new ConfigRequest.Builder().setClusterHigh(ConfigRequest.CLUSTER_ID_MAX + 1);
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testConfigRequestBuilderClusterLowLargerThanHigh() {
- thrown.expect(IllegalArgumentException.class);
- ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(100)
- .setClusterHigh(5).build();
+ new ConfigRequest.Builder().setClusterLow(100).setClusterHigh(5).build();
}
@Test
@@ -127,10 +629,12 @@
final int clusterLow = 25;
final int masterPreference = 177;
final boolean supportBand5g = true;
+ final boolean enableIdentityChangeCallback = true;
ConfigRequest configRequest = new ConfigRequest.Builder().setClusterHigh(clusterHigh)
.setClusterLow(clusterLow).setMasterPreference(masterPreference)
- .setSupport5gBand(supportBand5g).build();
+ .setSupport5gBand(supportBand5g)
+ .setEnableIdentityChangeCallback(enableIdentityChangeCallback).build();
Parcel parcelW = Parcel.obtain();
configRequest.writeToParcel(parcelW, 0);
@@ -146,237 +650,430 @@
}
/*
- * SubscribeData Tests
+ * SubscribeConfig Tests
*/
@Test
- public void testSubscribeDataBuilder() {
+ public void testSubscribeConfigBuilderDefaults() {
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+ collector.checkThat("mServiceName", subscribeConfig.mServiceName, equalTo(null));
+ collector.checkThat("mServiceSpecificInfoLength",
+ subscribeConfig.mServiceSpecificInfoLength, equalTo(0));
+ collector.checkThat("mTxFilterLength", subscribeConfig.mTxFilterLength, equalTo(0));
+ collector.checkThat("mRxFilterLength", subscribeConfig.mRxFilterLength, equalTo(0));
+ collector.checkThat("mSubscribeType", subscribeConfig.mSubscribeType,
+ equalTo(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE));
+ collector.checkThat("mSubscribeCount", subscribeConfig.mSubscribeCount, equalTo(0));
+ collector.checkThat("mTtlSec", subscribeConfig.mTtlSec, equalTo(0));
+ collector.checkThat("mMatchStyle", subscribeConfig.mMatchStyle,
+ equalTo(SubscribeConfig.MATCH_STYLE_ALL));
+ collector.checkThat("mEnableTerminateNotification",
+ subscribeConfig.mEnableTerminateNotification, equalTo(true));
+ }
+
+ @Test
+ public void testSubscribeConfigBuilder() {
final String serviceName = "some_service_or_other";
final String serviceSpecificInfo = "long arbitrary string with some info";
final byte[] txFilter = {
0, 1, 16, 1, 22 };
final byte[] rxFilter = {
1, 127, 0, 1, -5, 1, 22 };
-
- SubscribeData subscribeData = new SubscribeData.Builder().setServiceName(serviceName)
- .setServiceSpecificInfo(serviceSpecificInfo).setTxFilter(txFilter, txFilter.length)
- .setRxFilter(rxFilter, rxFilter.length).build();
-
- collector.checkThat("mServiceName", serviceName, equalTo(subscribeData.mServiceName));
- String mServiceSpecificInfo = new String(subscribeData.mServiceSpecificInfo, 0,
- subscribeData.mServiceSpecificInfoLength);
- collector.checkThat("mServiceSpecificInfo",
- utilAreArraysEqual(serviceSpecificInfo.getBytes(), serviceSpecificInfo.length(),
- subscribeData.mServiceSpecificInfo,
- subscribeData.mServiceSpecificInfoLength),
- equalTo(true));
- collector.checkThat("mTxFilter", utilAreArraysEqual(txFilter, txFilter.length,
- subscribeData.mTxFilter, subscribeData.mTxFilterLength), equalTo(true));
- collector.checkThat("mRxFilter", utilAreArraysEqual(rxFilter, rxFilter.length,
- subscribeData.mRxFilter, subscribeData.mRxFilterLength), equalTo(true));
- }
-
- @Test
- public void testSubscribeDataParcel() {
- final String serviceName = "some_service_or_other";
- final String serviceSpecificInfo = "long arbitrary string with some info";
- final byte[] txFilter = {
- 0, 1, 16, 1, 22 };
- final byte[] rxFilter = {
- 1, 127, 0, 1, -5, 1, 22 };
-
- SubscribeData subscribeData = new SubscribeData.Builder().setServiceName(serviceName)
- .setServiceSpecificInfo(serviceSpecificInfo).setTxFilter(txFilter, txFilter.length)
- .setTxFilter(rxFilter, rxFilter.length).build();
-
- Parcel parcelW = Parcel.obtain();
- subscribeData.writeToParcel(parcelW, 0);
- byte[] bytes = parcelW.marshall();
- parcelW.recycle();
-
- Parcel parcelR = Parcel.obtain();
- parcelR.unmarshall(bytes, 0, bytes.length);
- parcelR.setDataPosition(0);
- SubscribeData rereadSubscribeData = SubscribeData.CREATOR.createFromParcel(parcelR);
-
- assertEquals(subscribeData, rereadSubscribeData);
- }
-
- /*
- * SubscribeSettings Tests
- */
-
- @Test
- public void testSubscribeSettingsBuilder() {
- final int subscribeType = SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE;
+ final int subscribeType = SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE;
final int subscribeCount = 10;
final int subscribeTtl = 15;
+ final int matchStyle = SubscribeConfig.MATCH_STYLE_FIRST_ONLY;
+ final boolean enableTerminateNotification = false;
- SubscribeSettings subscribeSetting = new SubscribeSettings.Builder()
- .setSubscribeType(subscribeType).setSubscribeCount(subscribeCount)
- .setTtlSec(subscribeTtl).build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
+ .setServiceSpecificInfo(serviceSpecificInfo).setTxFilter(txFilter, txFilter.length)
+ .setRxFilter(rxFilter, rxFilter.length).setSubscribeType(subscribeType)
+ .setSubscribeCount(subscribeCount).setTtlSec(subscribeTtl).setMatchStyle(matchStyle)
+ .setEnableTerminateNotification(enableTerminateNotification).build();
+ collector.checkThat("mServiceName", serviceName.getBytes(),
+ equalTo(subscribeConfig.mServiceName));
+ collector.checkThat("mServiceSpecificInfo",
+ utilAreArraysEqual(serviceSpecificInfo.getBytes(), serviceSpecificInfo.length(),
+ subscribeConfig.mServiceSpecificInfo,
+ subscribeConfig.mServiceSpecificInfoLength),
+ equalTo(true));
+ collector.checkThat("mTxFilter", utilAreArraysEqual(txFilter, txFilter.length,
+ subscribeConfig.mTxFilter, subscribeConfig.mTxFilterLength), equalTo(true));
+ collector.checkThat("mRxFilter", utilAreArraysEqual(rxFilter, rxFilter.length,
+ subscribeConfig.mRxFilter, subscribeConfig.mRxFilterLength), equalTo(true));
collector.checkThat("mSubscribeType", subscribeType,
- equalTo(subscribeSetting.mSubscribeType));
+ equalTo(subscribeConfig.mSubscribeType));
collector.checkThat("mSubscribeCount", subscribeCount,
- equalTo(subscribeSetting.mSubscribeCount));
- collector.checkThat("mTtlSec", subscribeTtl, equalTo(subscribeSetting.mTtlSec));
+ equalTo(subscribeConfig.mSubscribeCount));
+ collector.checkThat("mTtlSec", subscribeTtl, equalTo(subscribeConfig.mTtlSec));
+ collector.checkThat("mMatchStyle", matchStyle, equalTo(subscribeConfig.mMatchStyle));
+ collector.checkThat("mEnableTerminateNotification", enableTerminateNotification,
+ equalTo(subscribeConfig.mEnableTerminateNotification));
}
@Test
- public void testSubscribeSettingsBuilderBadSubscribeType() {
- thrown.expect(IllegalArgumentException.class);
- new SubscribeSettings.Builder().setSubscribeType(10);
- }
-
- @Test
- public void testSubscribeSettingsBuilderNegativeCount() {
- thrown.expect(IllegalArgumentException.class);
- new SubscribeSettings.Builder().setSubscribeCount(-1);
- }
-
- @Test
- public void testSubscribeSettingsBuilderNegativeTtl() {
- thrown.expect(IllegalArgumentException.class);
- new SubscribeSettings.Builder().setTtlSec(-100);
- }
-
- @Test
- public void testSubscribeSettingsParcel() {
- final int subscribeType = SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE;
+ public void testSubscribeConfigParcel() {
+ final String serviceName = "some_service_or_other";
+ final String serviceSpecificInfo = "long arbitrary string with some info";
+ final byte[] txFilter = {
+ 0, 1, 16, 1, 22 };
+ final byte[] rxFilter = {
+ 1, 127, 0, 1, -5, 1, 22 };
+ final int subscribeType = SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE;
final int subscribeCount = 10;
final int subscribeTtl = 15;
+ final int matchStyle = SubscribeConfig.MATCH_STYLE_FIRST_ONLY;
+ final boolean enableTerminateNotification = true;
- SubscribeSettings subscribeSetting = new SubscribeSettings.Builder()
- .setSubscribeType(subscribeType).setSubscribeCount(subscribeCount)
- .setTtlSec(subscribeTtl).build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
+ .setServiceSpecificInfo(serviceSpecificInfo).setTxFilter(txFilter, txFilter.length)
+ .setTxFilter(rxFilter, rxFilter.length).setSubscribeType(subscribeType)
+ .setSubscribeCount(subscribeCount).setTtlSec(subscribeTtl).setMatchStyle(matchStyle)
+ .setEnableTerminateNotification(enableTerminateNotification).build();
Parcel parcelW = Parcel.obtain();
- subscribeSetting.writeToParcel(parcelW, 0);
+ subscribeConfig.writeToParcel(parcelW, 0);
byte[] bytes = parcelW.marshall();
parcelW.recycle();
Parcel parcelR = Parcel.obtain();
parcelR.unmarshall(bytes, 0, bytes.length);
parcelR.setDataPosition(0);
- SubscribeSettings rereadSubscribeSettings = SubscribeSettings.CREATOR
- .createFromParcel(parcelR);
+ SubscribeConfig rereadSubscribeConfig = SubscribeConfig.CREATOR.createFromParcel(parcelR);
- assertEquals(subscribeSetting, rereadSubscribeSettings);
+ assertEquals(subscribeConfig, rereadSubscribeConfig);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSubscribeConfigBuilderBadSubscribeType() {
+ new SubscribeConfig.Builder().setSubscribeType(10);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSubscribeConfigBuilderNegativeCount() {
+ new SubscribeConfig.Builder().setSubscribeCount(-1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSubscribeConfigBuilderNegativeTtl() {
+ new SubscribeConfig.Builder().setTtlSec(-100);
+ }
+
+ /**
+ * Validate that a bad match style configuration throws an exception.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testSubscribeConfigBuilderBadMatchStyle() {
+ new SubscribeConfig.Builder().setMatchStyle(10);
}
/*
- * PublishData Tests
+ * PublishConfig Tests
*/
@Test
- public void testPublishDataBuilder() {
+ public void testPublishConfigBuilderDefaults() {
+ PublishConfig publishConfig = new PublishConfig.Builder().build();
+
+ collector.checkThat("mServiceName", publishConfig.mServiceName, equalTo(null));
+ collector.checkThat("mServiceSpecificInfoLength", publishConfig.mServiceSpecificInfoLength,
+ equalTo(0));
+ collector.checkThat("mTxFilterLength", publishConfig.mTxFilterLength, equalTo(0));
+ collector.checkThat("mRxFilterLength", publishConfig.mRxFilterLength, equalTo(0));
+ collector.checkThat("mPublishType", publishConfig.mPublishType,
+ equalTo(PublishConfig.PUBLISH_TYPE_UNSOLICITED));
+ collector.checkThat("mPublishCount", publishConfig.mPublishCount, equalTo(0));
+ collector.checkThat("mTtlSec", publishConfig.mTtlSec, equalTo(0));
+ collector.checkThat("mEnableTerminateNotification",
+ publishConfig.mEnableTerminateNotification, equalTo(true));
+ }
+
+ @Test
+ public void testPublishConfigBuilder() {
final String serviceName = "some_service_or_other";
final String serviceSpecificInfo = "long arbitrary string with some info";
final byte[] txFilter = {
0, 1, 16, 1, 22 };
final byte[] rxFilter = {
1, 127, 0, 1, -5, 1, 22 };
+ final int publishType = PublishConfig.PUBLISH_TYPE_SOLICITED;
+ final int publishCount = 10;
+ final int publishTtl = 15;
+ final boolean enableTerminateNotification = false;
- PublishData publishData = new PublishData.Builder().setServiceName(serviceName)
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
.setServiceSpecificInfo(serviceSpecificInfo).setTxFilter(txFilter, txFilter.length)
- .setRxFilter(rxFilter, rxFilter.length).build();
+ .setRxFilter(rxFilter, rxFilter.length).setPublishType(publishType)
+ .setPublishCount(publishCount).setTtlSec(publishTtl)
+ .setEnableTerminateNotification(enableTerminateNotification).build();
- collector.checkThat("mServiceName", serviceName, equalTo(publishData.mServiceName));
- String mServiceSpecificInfo = new String(publishData.mServiceSpecificInfo, 0,
- publishData.mServiceSpecificInfoLength);
+ collector.checkThat("mServiceName", serviceName.getBytes(),
+ equalTo(publishConfig.mServiceName));
collector.checkThat("mServiceSpecificInfo",
utilAreArraysEqual(serviceSpecificInfo.getBytes(), serviceSpecificInfo.length(),
- publishData.mServiceSpecificInfo, publishData.mServiceSpecificInfoLength),
+ publishConfig.mServiceSpecificInfo,
+ publishConfig.mServiceSpecificInfoLength),
equalTo(true));
collector.checkThat("mTxFilter", utilAreArraysEqual(txFilter, txFilter.length,
- publishData.mTxFilter, publishData.mTxFilterLength), equalTo(true));
+ publishConfig.mTxFilter, publishConfig.mTxFilterLength), equalTo(true));
collector.checkThat("mRxFilter", utilAreArraysEqual(rxFilter, rxFilter.length,
- publishData.mRxFilter, publishData.mRxFilterLength), equalTo(true));
+ publishConfig.mRxFilter, publishConfig.mRxFilterLength), equalTo(true));
+ collector.checkThat("mPublishType", publishType, equalTo(publishConfig.mPublishType));
+ collector.checkThat("mPublishCount", publishCount, equalTo(publishConfig.mPublishCount));
+ collector.checkThat("mTtlSec", publishTtl, equalTo(publishConfig.mTtlSec));
+ collector.checkThat("mEnableTerminateNotification", enableTerminateNotification,
+ equalTo(publishConfig.mEnableTerminateNotification));
}
@Test
- public void testPublishDataParcel() {
+ public void testPublishConfigParcel() {
final String serviceName = "some_service_or_other";
final String serviceSpecificInfo = "long arbitrary string with some info";
final byte[] txFilter = {
0, 1, 16, 1, 22 };
final byte[] rxFilter = {
1, 127, 0, 1, -5, 1, 22 };
+ final int publishType = PublishConfig.PUBLISH_TYPE_SOLICITED;
+ final int publishCount = 10;
+ final int publishTtl = 15;
+ final boolean enableTerminateNotification = false;
- PublishData publishData = new PublishData.Builder().setServiceName(serviceName)
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
.setServiceSpecificInfo(serviceSpecificInfo).setTxFilter(txFilter, txFilter.length)
- .setTxFilter(rxFilter, rxFilter.length).build();
+ .setTxFilter(rxFilter, rxFilter.length).setPublishType(publishType)
+ .setPublishCount(publishCount).setTtlSec(publishTtl)
+ .setEnableTerminateNotification(enableTerminateNotification).build();
Parcel parcelW = Parcel.obtain();
- publishData.writeToParcel(parcelW, 0);
+ publishConfig.writeToParcel(parcelW, 0);
byte[] bytes = parcelW.marshall();
parcelW.recycle();
Parcel parcelR = Parcel.obtain();
parcelR.unmarshall(bytes, 0, bytes.length);
parcelR.setDataPosition(0);
- PublishData rereadPublishData = PublishData.CREATOR.createFromParcel(parcelR);
+ PublishConfig rereadPublishConfig = PublishConfig.CREATOR.createFromParcel(parcelR);
- assertEquals(publishData, rereadPublishData);
+ assertEquals(publishConfig, rereadPublishConfig);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testPublishConfigBuilderBadPublishType() {
+ new PublishConfig.Builder().setPublishType(5);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testPublishConfigBuilderNegativeCount() {
+ new PublishConfig.Builder().setPublishCount(-4);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testPublishConfigBuilderNegativeTtl() {
+ new PublishConfig.Builder().setTtlSec(-10);
}
/*
- * PublishSettings Tests
+ * Ranging tests
*/
+ /**
+ * Validate ranging + success flow: (1) connect, (2) create a (publish) session, (3) start
+ * ranging, (4) ranging success callback, (5) ranging aborted callback ignored (since
+ * listener removed).
+ */
@Test
- public void testPublishSettingsBuilder() {
- final int publishType = PublishSettings.PUBLISH_TYPE_SOLICITED;
- final int publishCount = 10;
- final int publishTtl = 15;
+ public void testRangingCallbacks() throws Exception {
+ final int clientId = 4565;
+ final int sessionId = 123;
+ final int rangingId = 3482;
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final PublishConfig publishConfig = new PublishConfig.Builder().build();
+ final RttManager.RttParams rttParams = new RttManager.RttParams();
+ rttParams.deviceType = RttManager.RTT_PEER_NAN;
+ rttParams.bssid = Integer.toString(1234);
+ final RttManager.RttResult rttResults = new RttManager.RttResult();
+ rttResults.distance = 10;
- PublishSettings publishSetting = new PublishSettings.Builder().setPublishType(publishType)
- .setPublishCount(publishCount).setTtlSec(publishTtl).build();
+ when(mockNanService.connect(any(IBinder.class), any(IWifiNanEventCallback.class),
+ eq(configRequest))).thenReturn(clientId);
+ when(mockNanService.startRanging(anyInt(), anyInt(),
+ any(RttManager.ParcelableRttParams.class))).thenReturn(rangingId);
- collector.checkThat("mPublishType", publishType, equalTo(publishSetting.mPublishType));
- collector.checkThat("mPublishCount", publishCount, equalTo(publishSetting.mPublishCount));
- collector.checkThat("mTtlSec", publishTtl, equalTo(publishSetting.mTtlSec));
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockNanService,
+ mockPublishSession, mockRttListener);
+ ArgumentCaptor<IWifiNanEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiNanEventCallback.class);
+ ArgumentCaptor<IWifiNanSessionCallback> sessionProxyCallback = ArgumentCaptor
+ .forClass(IWifiNanSessionCallback.class);
+ ArgumentCaptor<WifiNanPublishSession> publishSession = ArgumentCaptor
+ .forClass(WifiNanPublishSession.class);
+ ArgumentCaptor<RttManager.ParcelableRttParams> rttParamCaptor = ArgumentCaptor
+ .forClass(RttManager.ParcelableRttParams.class);
+ ArgumentCaptor<RttManager.RttResult[]> rttResultsCaptor = ArgumentCaptor
+ .forClass(RttManager.RttResult[].class);
+
+ // (1) connect successfully
+ mDut.connect(mMockLooper.getLooper(), mockCallback, configRequest);
+ inOrder.verify(mockNanService).connect(any(IBinder.class), clientProxyCallback.capture(),
+ eq(configRequest));
+ clientProxyCallback.getValue().onConnectSuccess();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (2) publish successfully
+ mDut.publish(publishConfig, mockSessionCallback);
+ inOrder.verify(mockNanService).publish(eq(clientId), eq(publishConfig),
+ sessionProxyCallback.capture());
+ sessionProxyCallback.getValue().onSessionStarted(sessionId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
+
+ // (3) start ranging
+ publishSession.getValue().startRanging(new RttManager.RttParams[]{rttParams},
+ mockRttListener);
+ inOrder.verify(mockNanService).startRanging(eq(clientId), eq(sessionId),
+ rttParamCaptor.capture());
+ collector.checkThat("RttParams.deviceType", rttParams.deviceType,
+ equalTo(rttParamCaptor.getValue().mParams[0].deviceType));
+ collector.checkThat("RttParams.bssid", rttParams.bssid,
+ equalTo(rttParamCaptor.getValue().mParams[0].bssid));
+
+ // (4) ranging success callback
+ clientProxyCallback.getValue().onRangingSuccess(rangingId,
+ new RttManager.ParcelableRttResults(new RttManager.RttResult[] { rttResults }));
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockRttListener).onSuccess(rttResultsCaptor.capture());
+ collector.checkThat("RttResult.distance", rttResults.distance,
+ equalTo(rttResultsCaptor.getValue()[0].distance));
+
+ // (5) ranging aborted callback (should be ignored since listener cleared on first callback)
+ clientProxyCallback.getValue().onRangingAborted(rangingId);
+ mMockLooper.dispatchAll();
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockNanService,
+ mockPublishSession, mockRttListener);
}
+ /*
+ * Data-path tests
+ */
+
+ /**
+ * Validate that correct network specifier is generated for client-based data-path.
+ */
@Test
- public void testPublishSettingsBuilderBadPublishType() {
- thrown.expect(IllegalArgumentException.class);
- new PublishSettings.Builder().setPublishType(5);
+ public void testNetworkSpecifierWithClient() throws Exception {
+ final int clientId = 4565;
+ final int sessionId = 123;
+ final int peerId = 123412;
+ final int role = WifiNanManager.WIFI_NAN_DATA_PATH_ROLE_INITIATOR;
+ final String token = "Some arbitrary token string - can really be anything";
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final PublishConfig publishConfig = new PublishConfig.Builder().build();
+
+ String tokenB64 = Base64.encodeToString(token.getBytes(), Base64.DEFAULT);
+
+ ArgumentCaptor<IWifiNanEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiNanEventCallback.class);
+ ArgumentCaptor<IWifiNanSessionCallback> sessionProxyCallback = ArgumentCaptor
+ .forClass(IWifiNanSessionCallback.class);
+ ArgumentCaptor<WifiNanPublishSession> publishSession = ArgumentCaptor
+ .forClass(WifiNanPublishSession.class);
+
+ when(mockNanService.connect(any(IBinder.class), any(IWifiNanEventCallback.class),
+ any(ConfigRequest.class))).thenReturn(clientId);
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockNanService,
+ mockPublishSession, mockRttListener);
+
+ // (1) connect successfully
+ mDut.connect(mMockLooper.getLooper(), mockCallback, configRequest);
+ inOrder.verify(mockNanService).connect(any(IBinder.class), clientProxyCallback.capture(),
+ eq(configRequest));
+ clientProxyCallback.getValue().onConnectSuccess();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (2) publish successfully
+ mDut.publish(publishConfig, mockSessionCallback);
+ inOrder.verify(mockNanService).publish(eq(clientId), eq(publishConfig),
+ sessionProxyCallback.capture());
+ sessionProxyCallback.getValue().onSessionStarted(sessionId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
+
+ // (3) request a network specifier from the session
+ String networkSpecifier = publishSession.getValue().createNetworkSpecifier(role, peerId,
+ token.getBytes(), token.length());
+
+ // validate format
+ JSONObject jsonObject = new JSONObject(networkSpecifier);
+ collector.checkThat("role", role,
+ equalTo(jsonObject.getInt(WifiNanManager.NETWORK_SPECIFIER_KEY_ROLE)));
+ collector.checkThat("client_id", clientId,
+ equalTo(jsonObject.getInt(WifiNanManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
+ collector.checkThat("session_id", sessionId,
+ equalTo(jsonObject.getInt(WifiNanManager.NETWORK_SPECIFIER_KEY_SESSION_ID)));
+ collector.checkThat("peer_id", peerId,
+ equalTo(jsonObject.getInt(WifiNanManager.NETWORK_SPECIFIER_KEY_PEER_ID)));
+ collector.checkThat("token", tokenB64,
+ equalTo(jsonObject.getString(WifiNanManager.NETWORK_SPECIFIER_KEY_TOKEN)));
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockNanService,
+ mockPublishSession, mockRttListener);
}
+ /**
+ * Validate that correct network specifier is generated for a direct data-path (i.e.
+ * specifying MAC address as opposed to a client-based oqaque specification).
+ */
@Test
- public void testPublishSettingsBuilderNegativeCount() {
- thrown.expect(IllegalArgumentException.class);
- new PublishSettings.Builder().setPublishCount(-4);
- }
+ public void testNetworkSpecifierDirect() throws Exception {
+ final int clientId = 134;
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false);
+ final int role = WifiNanManager.WIFI_NAN_DATA_PATH_ROLE_INITIATOR;
+ final String token = "Some arbitrary token string - can really be anything";
- @Test
- public void testPublishSettingsBuilderNegativeTtl() {
- thrown.expect(IllegalArgumentException.class);
- new PublishSettings.Builder().setTtlSec(-10);
- }
+ String tokenB64 = Base64.encodeToString(token.getBytes(), Base64.DEFAULT);
- @Test
- public void testPublishSettingsParcel() {
- final int publishType = PublishSettings.PUBLISH_TYPE_SOLICITED;
- final int publishCount = 10;
- final int publishTtl = 15;
+ ArgumentCaptor<IWifiNanEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiNanEventCallback.class);
- PublishSettings configSetting = new PublishSettings.Builder().setPublishType(publishType)
- .setPublishCount(publishCount).setTtlSec(publishTtl).build();
+ when(mockNanService.connect(any(IBinder.class), any(IWifiNanEventCallback.class),
+ any(ConfigRequest.class))).thenReturn(clientId);
- Parcel parcelW = Parcel.obtain();
- configSetting.writeToParcel(parcelW, 0);
- byte[] bytes = parcelW.marshall();
- parcelW.recycle();
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockNanService,
+ mockPublishSession, mockRttListener);
- Parcel parcelR = Parcel.obtain();
- parcelR.unmarshall(bytes, 0, bytes.length);
- parcelR.setDataPosition(0);
- PublishSettings rereadPublishSettings = PublishSettings.CREATOR.createFromParcel(parcelR);
+ // (1) connect successfully
+ mDut.connect(mMockLooper.getLooper(), mockCallback, configRequest);
+ inOrder.verify(mockNanService).connect(any(IBinder.class), clientProxyCallback.capture(),
+ eq(configRequest));
+ clientProxyCallback.getValue().onConnectSuccess();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
- assertEquals(configSetting, rereadPublishSettings);
+ /* (2) request a direct network specifier*/
+ String networkSpecifier = mDut.createNetworkSpecifier(role, someMac, token.getBytes(),
+ token.length());
+
+ /* validate format*/
+ JSONObject jsonObject = new JSONObject(networkSpecifier);
+ collector.checkThat("role", role,
+ equalTo(jsonObject.getInt(WifiNanManager.NETWORK_SPECIFIER_KEY_ROLE)));
+ collector.checkThat("client_id", clientId,
+ equalTo(jsonObject.getInt(WifiNanManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
+ collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode(
+ jsonObject.getString(WifiNanManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(),
+ false)));
+ collector.checkThat("token", tokenB64,
+ equalTo(jsonObject.getString(WifiNanManager.NETWORK_SPECIFIER_KEY_TOKEN)));
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockNanService,
+ mockPublishSession, mockRttListener);
}
/*
diff --git a/tests/wifitests/src/com/android/server/wifi/nan/WifiNanRttStateManagerTest.java b/tests/wifitests/src/com/android/server/wifi/nan/WifiNanRttStateManagerTest.java
new file mode 100644
index 0000000..35a600a
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/nan/WifiNanRttStateManagerTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi.nan;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.wifi.IRttManager;
+import android.net.wifi.RttManager;
+import android.os.Handler;
+import android.os.Message;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.util.test.BidirectionalAsyncChannelServer;
+import android.os.test.TestLooper;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ErrorCollector;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit test harness for WifiNanManager class.
+ */
+@SmallTest
+public class WifiNanRttStateManagerTest {
+ private WifiNanRttStateManager mDut;
+ private TestLooper mTestLooper;
+
+ @Mock
+ private Context mMockContext;
+
+ @Mock
+ private Handler mMockHandler;
+
+ @Mock
+ private IRttManager mMockRttService;
+
+ @Rule
+ public ErrorCollector collector = new ErrorCollector();
+
+ /**
+ * Initialize mocks.
+ */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mDut = new WifiNanRttStateManager();
+ mTestLooper = new TestLooper();
+ BidirectionalAsyncChannelServer server = new BidirectionalAsyncChannelServer(
+ mMockContext, mTestLooper.getLooper(), mMockHandler);
+ when(mMockRttService.getMessenger()).thenReturn(server.getMessenger());
+
+ mDut.startWithRttService(mMockContext, mTestLooper.getLooper(), mMockRttService);
+ }
+
+ /**
+ * Validates that startRanging flow works: (1) start ranging, (2) get success callback - pass
+ * to client (while nulling BSSID info), (3) get fail callback - ignored (since client
+ * cleaned-out after first callback).
+ */
+ @Test
+ public void testStartRanging() throws Exception {
+ final int rangingId = 1234;
+ WifiNanClientState mockClient = mock(WifiNanClientState.class);
+ RttManager.RttParams[] params = new RttManager.RttParams[1];
+ params[0] = new RttManager.RttParams();
+ RttManager.ParcelableRttResults results =
+ new RttManager.ParcelableRttResults(new RttManager.RttResult[2]);
+ results.mResults[0] = new RttManager.RttResult();
+ results.mResults[0].bssid = "something non-null";
+ results.mResults[1] = new RttManager.RttResult();
+ results.mResults[1].bssid = "really really non-null";
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ ArgumentCaptor<RttManager.ParcelableRttResults> rttResultsCaptor =
+ ArgumentCaptor.forClass(RttManager.ParcelableRttResults.class);
+
+ InOrder inOrder = inOrder(mMockHandler, mockClient);
+
+ // (1) start ranging
+ mDut.startRanging(rangingId, mockClient, params);
+ mTestLooper.dispatchAll();
+ inOrder.verify(mMockHandler).handleMessage(messageCaptor.capture());
+ Message msg = messageCaptor.getValue();
+ collector.checkThat("msg.what=RttManager.CMD_OP_START_RANGING", msg.what,
+ equalTo(RttManager.CMD_OP_START_RANGING));
+ collector.checkThat("rangingId", msg.arg2, equalTo(rangingId));
+ collector.checkThat("RTT params", ((RttManager.ParcelableRttParams) msg.obj).mParams,
+ equalTo(params));
+
+ // (2) get success callback - pass to client
+ Message successMessage = Message.obtain();
+ successMessage.what = RttManager.CMD_OP_SUCCEEDED;
+ successMessage.arg2 = rangingId;
+ successMessage.obj = results;
+ msg.replyTo.send(successMessage);
+ mTestLooper.dispatchAll();
+ inOrder.verify(mockClient).onRangingSuccess(eq(rangingId), rttResultsCaptor.capture());
+ collector.checkThat("ParcelableRttResults object", results,
+ equalTo(rttResultsCaptor.getValue()));
+ collector.checkThat("RttResults[0].bssid null",
+ rttResultsCaptor.getValue().mResults[0].bssid, nullValue());
+ collector.checkThat("RttResults[1].bssid null",
+ rttResultsCaptor.getValue().mResults[1].bssid, nullValue());
+
+ // (3) get fail callback - ignored
+ Message failMessage = Message.obtain();
+ failMessage.what = RttManager.CMD_OP_ABORTED;
+ failMessage.arg2 = rangingId;
+ msg.replyTo.send(failMessage);
+ mTestLooper.dispatchAll();
+
+ verifyNoMoreInteractions(mMockHandler, mockClient);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/nan/WifiNanServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/nan/WifiNanServiceImplTest.java
new file mode 100644
index 0000000..3fb2abb
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/nan/WifiNanServiceImplTest.java
@@ -0,0 +1,518 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi.nan;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.wifi.RttManager;
+import android.net.wifi.nan.ConfigRequest;
+import android.net.wifi.nan.IWifiNanEventCallback;
+import android.net.wifi.nan.IWifiNanSessionCallback;
+import android.net.wifi.nan.PublishConfig;
+import android.net.wifi.nan.SubscribeConfig;
+import android.os.IBinder;
+import android.os.Looper;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.lang.reflect.Field;
+
+/**
+ * Unit test harness for WifiNanStateManager.
+ */
+@SmallTest
+public class WifiNanServiceImplTest {
+ private WifiNanServiceImplSpy mDut;
+ private int mDefaultUid = 1500;
+
+ @Mock
+ private Context mContextMock;
+ @Mock
+ private PackageManager mPackageManagerMock;
+ @Mock
+ private WifiNanStateManager mNanStateManagerMock;
+ @Mock
+ private IBinder mBinderMock;
+ @Mock
+ private IWifiNanEventCallback mCallbackMock;
+ @Mock
+ private IWifiNanSessionCallback mSessionCallbackMock;
+
+ /**
+ * Using instead of spy to avoid native crash failures - possibly due to
+ * spy's copying of state.
+ */
+ private class WifiNanServiceImplSpy extends WifiNanServiceImpl {
+ public int fakeUid;
+
+ WifiNanServiceImplSpy(Context context) {
+ super(context);
+ }
+
+ /**
+ * Return the fake UID instead of the real one: pseudo-spy
+ * implementation.
+ */
+ @Override
+ public int getMockableCallingUid() {
+ return fakeUid;
+ }
+ }
+
+ /**
+ * Initializes mocks.
+ */
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContextMock.getApplicationContext()).thenReturn(mContextMock);
+ when(mContextMock.getPackageManager()).thenReturn(mPackageManagerMock);
+ when(mPackageManagerMock.hasSystemFeature(PackageManager.FEATURE_WIFI_NAN))
+ .thenReturn(true);
+
+ installMockNanStateManager();
+
+ mDut = new WifiNanServiceImplSpy(mContextMock);
+ mDut.fakeUid = mDefaultUid;
+ }
+
+ /**
+ * Validate start() function: passes a valid looper.
+ */
+ @Test
+ public void testStart() {
+ mDut.start();
+
+ verify(mNanStateManagerMock).start(eq(mContextMock), any(Looper.class));
+ }
+
+ /**
+ * Validate enableUsage() function
+ */
+ @Test
+ public void testEnableUsage() {
+ mDut.enableUsage();
+
+ verify(mNanStateManagerMock).enableUsage();
+ }
+
+ /**
+ * Validate disableUsage() function
+ */
+ @Test
+ public void testDisableUsage() throws Exception {
+ mDut.enableUsage();
+ doConnect();
+ mDut.disableUsage();
+
+ verify(mNanStateManagerMock).disableUsage();
+ }
+
+ /**
+ * Validate isUsageEnabled() function
+ */
+ @Test
+ public void testIsUsageEnabled() {
+ mDut.isUsageEnabled();
+
+ verify(mNanStateManagerMock).isUsageEnabled();
+ }
+
+
+ /**
+ * Validate connect() - returns and uses a client ID.
+ */
+ @Test
+ public void testConnect() {
+ doConnect();
+ }
+
+ /**
+ * Validate connect() when a non-null config is passed.
+ */
+ @Test
+ public void testConnectWithConfig() {
+ ConfigRequest configRequest = new ConfigRequest.Builder().setMasterPreference(55).build();
+
+ int returnedClientId = mDut.connect(mBinderMock, mCallbackMock, configRequest);
+
+ ArgumentCaptor<Integer> clientId = ArgumentCaptor.forClass(Integer.class);
+ verify(mNanStateManagerMock).connect(clientId.capture(), anyInt(), eq(mCallbackMock),
+ eq(configRequest));
+ assertEquals(returnedClientId, (int) clientId.getValue());
+ }
+
+ /**
+ * Validate disconnect() - correct pass-through args.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testDisconnect() throws Exception {
+ int clientId = doConnect();
+
+ mDut.disconnect(clientId, mBinderMock);
+
+ verify(mNanStateManagerMock).disconnect(clientId);
+ validateInternalStateCleanedUp(clientId);
+ }
+
+ /**
+ * Validate that security exception thrown when attempting operation using
+ * an invalid client ID.
+ */
+ @Test(expected = SecurityException.class)
+ public void testFailOnInvalidClientId() {
+ mDut.disconnect(-1, mBinderMock);
+ }
+
+ /**
+ * Validate that security exception thrown when attempting operation using a
+ * client ID which was already cleared-up.
+ */
+ @Test(expected = SecurityException.class)
+ public void testFailOnClearedUpClientId() throws Exception {
+ int clientId = doConnect();
+
+ mDut.disconnect(clientId, mBinderMock);
+
+ verify(mNanStateManagerMock).disconnect(clientId);
+ validateInternalStateCleanedUp(clientId);
+
+ mDut.disconnect(clientId, mBinderMock);
+ }
+
+ /**
+ * Validate that trying to use a client ID from a UID which is different
+ * from the one that created it fails - and that the internal state is not
+ * modified so that a valid call (from the correct UID) will subsequently
+ * succeed.
+ */
+ @Test
+ public void testFailOnAccessClientIdFromWrongUid() throws Exception {
+ int clientId = doConnect();
+
+ mDut.fakeUid = mDefaultUid + 1;
+
+ /*
+ * Not using thrown.expect(...) since want to test that subsequent
+ * access works.
+ */
+ boolean failsAsExpected = false;
+ try {
+ mDut.disconnect(clientId, mBinderMock);
+ } catch (SecurityException e) {
+ failsAsExpected = true;
+ }
+
+ mDut.fakeUid = mDefaultUid;
+
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName("valid.value")
+ .build();
+ mDut.publish(clientId, publishConfig, mSessionCallbackMock);
+
+ verify(mNanStateManagerMock).publish(clientId, publishConfig, mSessionCallbackMock);
+ assertTrue("SecurityException for invalid access from wrong UID thrown", failsAsExpected);
+ }
+
+ /**
+ * Validates that on binder death we get a disconnect().
+ */
+ @Test
+ public void testBinderDeath() throws Exception {
+ ArgumentCaptor<IBinder.DeathRecipient> deathRecipient = ArgumentCaptor
+ .forClass(IBinder.DeathRecipient.class);
+
+ int clientId = doConnect();
+
+ verify(mBinderMock).linkToDeath(deathRecipient.capture(), eq(0));
+ deathRecipient.getValue().binderDied();
+ verify(mNanStateManagerMock).disconnect(clientId);
+ validateInternalStateCleanedUp(clientId);
+ }
+
+ /**
+ * Validates that sequential connect() calls return increasing client IDs.
+ */
+ @Test
+ public void testClientIdIncrementing() {
+ int loopCount = 100;
+
+ int prevId = 0;
+ for (int i = 0; i < loopCount; ++i) {
+ int id = mDut.connect(mBinderMock, mCallbackMock, null);
+ if (i != 0) {
+ assertTrue("Client ID incrementing", id > prevId);
+ }
+ prevId = id;
+ }
+ }
+
+ /**
+ * Validate terminateSession() - correct pass-through args.
+ */
+ @Test
+ public void testTerminateSession() {
+ int sessionId = 1024;
+ int clientId = doConnect();
+
+ mDut.terminateSession(clientId, sessionId);
+
+ verify(mNanStateManagerMock).terminateSession(clientId, sessionId);
+ }
+
+ /**
+ * Validate publish() - correct pass-through args.
+ */
+ @Test
+ public void testPublish() {
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName("something.valid")
+ .build();
+ int clientId = doConnect();
+ IWifiNanSessionCallback mockCallback = mock(IWifiNanSessionCallback.class);
+
+ mDut.publish(clientId, publishConfig, mockCallback);
+
+ verify(mNanStateManagerMock).publish(clientId, publishConfig, mockCallback);
+ }
+
+ /**
+ * Validate that publish() verifies the input PublishConfig and fails on an invalid service
+ * name.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testPublishBadServiceName() {
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
+ "Including invalid characters - spaces").build();
+ int clientId = doConnect();
+ IWifiNanSessionCallback mockCallback = mock(IWifiNanSessionCallback.class);
+
+ mDut.publish(clientId, publishConfig, mockCallback);
+
+ verify(mNanStateManagerMock).publish(clientId, publishConfig, mockCallback);
+ }
+
+ /**
+ * Validate updatePublish() - correct pass-through args.
+ */
+ @Test
+ public void testUpdatePublish() {
+ int sessionId = 1232;
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName("something.valid")
+ .build();
+ int clientId = doConnect();
+
+ mDut.updatePublish(clientId, sessionId, publishConfig);
+
+ verify(mNanStateManagerMock).updatePublish(clientId, sessionId, publishConfig);
+ }
+
+ /**
+ * Validate subscribe() - correct pass-through args.
+ */
+ @Test
+ public void testSubscribe() {
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder()
+ .setServiceName("something.valid").build();
+ int clientId = doConnect();
+ IWifiNanSessionCallback mockCallback = mock(IWifiNanSessionCallback.class);
+
+ mDut.subscribe(clientId, subscribeConfig, mockCallback);
+
+ verify(mNanStateManagerMock).subscribe(clientId, subscribeConfig, mockCallback);
+ }
+
+ /**
+ * Validate that subscribe() verifies the input SubscribeConfig and fails on an invalid service
+ * name.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testSubscribeBadServiceName() {
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(
+ "InvalidServiceCharacters__").build();
+ int clientId = doConnect();
+ IWifiNanSessionCallback mockCallback = mock(IWifiNanSessionCallback.class);
+
+ mDut.subscribe(clientId, subscribeConfig, mockCallback);
+
+ verify(mNanStateManagerMock).subscribe(clientId, subscribeConfig, mockCallback);
+ }
+
+ /**
+ * Validate updateSubscribe() - correct pass-through args.
+ */
+ @Test
+ public void testUpdateSubscribe() {
+ int sessionId = 1232;
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder()
+ .setServiceName("something.valid").build();
+ int clientId = doConnect();
+
+ mDut.updateSubscribe(clientId, sessionId, subscribeConfig);
+
+ verify(mNanStateManagerMock).updateSubscribe(clientId, sessionId, subscribeConfig);
+ }
+
+ /**
+ * Validate sendMessage() - correct pass-through args.
+ */
+ @Test
+ public void testSendMessage() {
+ int sessionId = 2394;
+ int peerId = 2032;
+ byte[] message = new byte[23];
+ int messageId = 2043;
+ int clientId = doConnect();
+
+ mDut.sendMessage(clientId, sessionId, peerId, message, message.length, messageId, 0);
+
+ verify(mNanStateManagerMock).sendMessage(clientId, sessionId, peerId, message,
+ message.length, messageId, 0);
+ }
+
+ /**
+ * Validate startRanging() - correct pass-through args
+ */
+ @Test
+ public void testStartRanging() {
+ int clientId = doConnect();
+ int sessionId = 65345;
+ RttManager.ParcelableRttParams params =
+ new RttManager.ParcelableRttParams(new RttManager.RttParams[1]);
+
+ ArgumentCaptor<RttManager.RttParams[]> paramsCaptor =
+ ArgumentCaptor.forClass(RttManager.RttParams[].class);
+
+ int rangingId = mDut.startRanging(clientId, sessionId, params);
+
+ verify(mNanStateManagerMock).startRanging(eq(clientId), eq(sessionId),
+ paramsCaptor.capture(), eq(rangingId));
+
+ assertArrayEquals(paramsCaptor.getValue(), params.mParams);
+ }
+
+ /**
+ * Validates that sequential startRanging() calls return increasing ranging IDs.
+ */
+ @Test
+ public void testRangingIdIncrementing() {
+ int loopCount = 100;
+ int clientId = doConnect();
+ int sessionId = 65345;
+ RttManager.ParcelableRttParams params =
+ new RttManager.ParcelableRttParams(new RttManager.RttParams[1]);
+
+ int prevRangingId = 0;
+ for (int i = 0; i < loopCount; ++i) {
+ int rangingId = mDut.startRanging(clientId, sessionId, params);
+ if (i != 0) {
+ assertTrue("Client ID incrementing", rangingId > prevRangingId);
+ }
+ prevRangingId = rangingId;
+ }
+ }
+
+ /**
+ * Validates that startRanging() requires a non-empty list
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testStartRangingZeroArgs() {
+ int clientId = doConnect();
+ int sessionId = 65345;
+ RttManager.ParcelableRttParams params =
+ new RttManager.ParcelableRttParams(new RttManager.RttParams[0]);
+
+ ArgumentCaptor<RttManager.RttParams[]> paramsCaptor =
+ ArgumentCaptor.forClass(RttManager.RttParams[].class);
+
+ int rangingId = mDut.startRanging(clientId, sessionId, params);
+ }
+
+ /*
+ * Tests of internal state of WifiNanServiceImpl: very limited (not usually
+ * a good idea). However, these test that the internal state is cleaned-up
+ * appropriately. Alternatively would cause issues with memory leaks or
+ * information leak between sessions.
+ */
+
+ private void validateInternalStateCleanedUp(int clientId) throws Exception {
+ int uidEntry = getInternalStateUid(clientId);
+ assertEquals(-1, uidEntry);
+
+ IBinder.DeathRecipient dr = getInternalStateDeathRecipient(clientId);
+ assertEquals(null, dr);
+ }
+
+ /*
+ * Utilities
+ */
+
+ private int doConnect() {
+ int returnedClientId = mDut.connect(mBinderMock, mCallbackMock, null);
+
+ ArgumentCaptor<Integer> clientId = ArgumentCaptor.forClass(Integer.class);
+ verify(mNanStateManagerMock).connect(clientId.capture(), anyInt(), eq(mCallbackMock),
+ eq(new ConfigRequest.Builder().build()));
+ assertEquals(returnedClientId, (int) clientId.getValue());
+
+ return returnedClientId;
+ }
+
+ private void installMockNanStateManager()
+ throws Exception {
+ Field field = WifiNanStateManager.class.getDeclaredField("sNanStateManagerSingleton");
+ field.setAccessible(true);
+ field.set(null, mNanStateManagerMock);
+ }
+
+ private int getInternalStateUid(int clientId) throws Exception {
+ Field field = WifiNanServiceImpl.class.getDeclaredField("mUidByClientId");
+ field.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ SparseIntArray uidByClientId = (SparseIntArray) field.get(mDut);
+
+ return uidByClientId.get(clientId, -1);
+ }
+
+ private IBinder.DeathRecipient getInternalStateDeathRecipient(int clientId) throws Exception {
+ Field field = WifiNanServiceImpl.class.getDeclaredField("mDeathRecipientsByClientId");
+ field.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ SparseArray<IBinder.DeathRecipient> deathRecipientsByClientId =
+ (SparseArray<IBinder.DeathRecipient>) field.get(mDut);
+
+ return deathRecipientsByClientId.get(clientId);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/nan/WifiNanStateManagerTest.java b/tests/wifitests/src/com/android/server/wifi/nan/WifiNanStateManagerTest.java
index 49abd27..f21ee62 100644
--- a/tests/wifitests/src/com/android/server/wifi/nan/WifiNanStateManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/nan/WifiNanStateManagerTest.java
@@ -17,28 +17,42 @@
package com.android.server.wifi.nan;
import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.core.IsNull.notNullValue;
import static org.hamcrest.core.IsNull.nullValue;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyShort;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+import android.app.test.MockAnswerUtil;
+import android.app.test.TestAlarmManager;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.wifi.RttManager;
import android.net.wifi.nan.ConfigRequest;
-import android.net.wifi.nan.IWifiNanEventListener;
-import android.net.wifi.nan.IWifiNanSessionListener;
-import android.net.wifi.nan.PublishData;
-import android.net.wifi.nan.PublishSettings;
-import android.net.wifi.nan.SubscribeData;
-import android.net.wifi.nan.SubscribeSettings;
-import android.net.wifi.nan.WifiNanEventListener;
-import android.net.wifi.nan.WifiNanSessionListener;
+import android.net.wifi.nan.IWifiNanEventCallback;
+import android.net.wifi.nan.IWifiNanSessionCallback;
+import android.net.wifi.nan.PublishConfig;
+import android.net.wifi.nan.SubscribeConfig;
+import android.net.wifi.nan.WifiNanEventCallback;
+import android.net.wifi.nan.WifiNanManager;
+import android.net.wifi.nan.WifiNanSessionCallback;
+import android.os.Message;
+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.MockLooper;
+import android.util.SparseIntArray;
import libcore.util.HexEncoding;
@@ -53,436 +67,825 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Random;
/**
* Unit test harness for WifiNanStateManager.
*/
@SmallTest
public class WifiNanStateManagerTest {
- private MockLooper mMockLooper;
+ private TestLooper mMockLooper;
+ private Random mRandomNg = new Random(15687);
private WifiNanStateManager mDut;
@Mock private WifiNanNative mMockNative;
+ @Mock private Context mMockContext;
+ @Mock private WifiNanRttStateManager mMockNanRttStateManager;
+ TestAlarmManager mAlarmManager;
+ @Mock private WifiNanDataPathStateManager mMockNanDataPathStatemanager;
@Rule
public ErrorCollector collector = new ErrorCollector();
+ /**
+ * Pre-test configuration. Initialize and install mocks.
+ */
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mMockLooper = new MockLooper();
+ mAlarmManager = new TestAlarmManager();
+ when(mMockContext.getSystemService(Context.ALARM_SERVICE))
+ .thenReturn(mAlarmManager.getAlarmManager());
- mDut = installNewNanStateManagerAndResetState();
- mDut.start(mMockLooper.getLooper());
+ when(mMockContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(
+ mock(ConnectivityManager.class));
+
+ mMockLooper = new TestLooper();
+
+ mDut = installNewNanStateManager();
+ mDut.start(mMockContext, mMockLooper.getLooper());
+ installMocksInStateManager(mDut, mMockNanRttStateManager, mMockNanDataPathStatemanager);
+
+ when(mMockNative.enableAndConfigure(anyShort(), any(ConfigRequest.class), anyBoolean()))
+ .thenReturn(true);
+ when(mMockNative.disable(anyShort())).thenReturn(true);
+ when(mMockNative.publish(anyShort(), anyInt(), any(PublishConfig.class))).thenReturn(true);
+ when(mMockNative.subscribe(anyShort(), anyInt(), any(SubscribeConfig.class)))
+ .thenReturn(true);
+ when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(byte[].class),
+ any(byte[].class), anyInt(), anyInt())).thenReturn(true);
+ when(mMockNative.stopPublish(anyShort(), anyInt())).thenReturn(true);
+ when(mMockNative.stopSubscribe(anyShort(), anyInt())).thenReturn(true);
+ when(mMockNative.getCapabilities(anyShort())).thenReturn(true);
installMockWifiNanNative(mMockNative);
}
+ /**
+ * Validate that NAN data-path interfaces are brought up and down correctly.
+ */
@Test
- public void testNanEventsDelivered() throws Exception {
- final int uid = 1005;
- final int clusterLow1 = 5;
- final int clusterHigh1 = 100;
- final int masterPref1 = 111;
- final int clusterLow2 = 7;
- final int clusterHigh2 = 155;
- final int masterPref2 = 0;
- final int reason = WifiNanSessionListener.FAIL_REASON_NO_RESOURCES;
+ public void testNanDataPathInterfaceUpDown() throws Exception {
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ InOrder inOrder = inOrder(mMockContext, mMockNative, mMockNanDataPathStatemanager);
+
+ // (1) enable usage
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ validateCorrectNanStatusChangeBroadcast(inOrder, true);
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNanDataPathStatemanager).createAllInterfaces();
+ collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
+
+ // (2) disable usage
+ mDut.disableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).disable((short) 0);
+ inOrder.verify(mMockNative).deInitNan();
+ inOrder.verify(mMockNanDataPathStatemanager).onNanDownCleanupDataPaths();
+ validateCorrectNanStatusChangeBroadcast(inOrder, false);
+ inOrder.verify(mMockNanDataPathStatemanager).deleteAllInterfaces();
+ collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
+
+ verifyNoMoreInteractions(mMockNative, mMockNanDataPathStatemanager);
+ }
+
+ /**
+ * Validate that APIs aren't functional when usage is disabled.
+ */
+ @Test
+ public void testDisableUsageDisablesApis() throws Exception {
+ final int clientId = 12314;
+ final int uid = 1000;
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ InOrder inOrder = inOrder(mMockContext, mMockNative, mockCallback);
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+
+ // (1) check initial state
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ validateCorrectNanStatusChangeBroadcast(inOrder, true);
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+ collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
+
+ // (2) disable usage and validate state
+ mDut.disableUsage();
+ mMockLooper.dispatchAll();
+ collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
+ inOrder.verify(mMockNative).disable((short) 0);
+ inOrder.verify(mMockNative).deInitNan();
+ validateCorrectNanStatusChangeBroadcast(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)
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+
+ verifyNoMoreInteractions(mMockNative, mockCallback);
+ }
+
+ /**
+ * Validate that when API usage is disabled while in the middle of a connection that internal
+ * state is cleaned-up, and that all subsequent operations are NOP. Then enable usage again and
+ * validate that operates correctly.
+ */
+ @Test
+ public void testDisableUsageFlow() throws Exception {
+ final int clientId = 12341;
+ final int uid = 1000;
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ InOrder inOrder = inOrder(mMockContext, mMockNative, mockCallback);
+
+ // (1) check initial state
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ validateCorrectNanStatusChangeBroadcast(inOrder, true);
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
+
+ // (2) connect (successfully)
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (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).deInitNan();
+ validateCorrectNanStatusChangeBroadcast(inOrder, false);
+ validateInternalClientInfoCleanedUp(clientId);
+
+ // (4) try connecting again and validate that just get an onNanDown
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+
+ // (5) disable usage again and validate that not much happens
+ mDut.disableUsage();
+ mMockLooper.dispatchAll();
+ collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
+
+ // (6) enable usage
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
+ validateCorrectNanStatusChangeBroadcast(inOrder, true);
+
+ // (7) connect (should be successful)
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ verifyNoMoreInteractions(mMockNative, mockCallback);
+ }
+
+ /**
+ * Validates that all events are delivered with correct arguments. Validates
+ * that IdentityChanged not delivered if configuration disables delivery.
+ */
+ @Test
+ public void testNanEventsDelivery() throws Exception {
+ final int clientId1 = 1005;
+ final int clientId2 = 1007;
+ final int clusterLow = 5;
+ final int clusterHigh = 100;
+ final int masterPref = 111;
+ final int uid = 1000;
+ final int reason = WifiNanEventCallback.REASON_OTHER;
final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false);
- ConfigRequest configRequest1 = new ConfigRequest.Builder().setClusterLow(clusterLow1)
- .setClusterHigh(clusterHigh1).setMasterPreference(masterPref1).build();
+ ConfigRequest configRequest1 = new ConfigRequest.Builder().setClusterLow(clusterLow)
+ .setClusterHigh(clusterHigh).setMasterPreference(masterPref)
+ .setEnableIdentityChangeCallback(false).build();
- ConfigRequest configRequest2 = new ConfigRequest.Builder().setClusterLow(clusterLow2)
- .setClusterHigh(clusterHigh2).setMasterPreference(masterPref2).build();
+ ConfigRequest configRequest2 = new ConfigRequest.Builder().setClusterLow(clusterLow)
+ .setClusterHigh(clusterHigh).setMasterPreference(masterPref)
+ .setEnableIdentityChangeCallback(true).build();
- IWifiNanEventListener mockListener = mock(IWifiNanEventListener.class);
- ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- InOrder inOrder = inOrder(mockListener, mMockNative);
+ IWifiNanEventCallback mockCallback1 = mock(IWifiNanEventCallback.class);
+ IWifiNanEventCallback mockCallback2 = mock(IWifiNanEventCallback.class);
+ ArgumentCaptor<Short> transactionIdCapture = ArgumentCaptor.forClass(Short.class);
+ InOrder inOrder = inOrder(mockCallback1, mockCallback2, mMockNative);
- mDut.connect(uid, mockListener,
- WifiNanEventListener.LISTEN_CONFIG_COMPLETED
- | WifiNanEventListener.LISTEN_CONFIG_FAILED
- | WifiNanEventListener.LISTEN_IDENTITY_CHANGED
- | WifiNanEventListener.LISTEN_NAN_DOWN);
- mDut.requestConfig(uid, configRequest1);
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionIdCapture.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionIdCapture.getValue(), getCapabilities());
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest1));
- short transactionId1 = transactionId.getValue();
+ // (1) connect 1st and 2nd clients
+ mDut.connect(clientId1, uid, mockCallback1, configRequest1);
+ mDut.connect(clientId2, uid, mockCallback2, configRequest2);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionIdCapture.capture(),
+ eq(configRequest1), eq(true));
+ short transactionId = transactionIdCapture.getValue();
+ mDut.onConfigSuccessResponse(transactionId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback1).onConnectSuccess();
- mDut.requestConfig(uid, configRequest2);
+ // (2) finish connection of 2nd client
+ inOrder.verify(mMockNative).enableAndConfigure(transactionIdCapture.capture(),
+ eq(configRequest2), eq(false));
+ transactionId = transactionIdCapture.getValue();
+ mDut.onConfigSuccessResponse(transactionId);
+
+ // (3) deliver NAN events
+ mDut.onClusterChangeNotification(WifiNanClientState.CLUSTER_CHANGE_EVENT_STARTED, someMac);
+ mDut.onInterfaceAddressChangeNotification(someMac);
+ mDut.onNanDownNotification(reason);
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest2));
- short transactionId2 = transactionId.getValue();
+ inOrder.verify(mockCallback2).onConnectSuccess();
+ inOrder.verify(mockCallback2).onIdentityChanged(someMac);
- mDut.onClusterChange(WifiNanClientState.CLUSTER_CHANGE_EVENT_STARTED, someMac);
- mDut.onConfigCompleted(transactionId1);
- mDut.onConfigFailed(transactionId2, reason);
- mDut.onInterfaceAddressChange(someMac);
- mDut.onNanDown(reason);
- mMockLooper.dispatchAll();
+ validateInternalClientInfoCleanedUp(clientId1);
+ validateInternalClientInfoCleanedUp(clientId2);
- inOrder.verify(mockListener).onIdentityChanged();
- inOrder.verify(mockListener).onConfigCompleted(configRequest1);
- inOrder.verify(mockListener).onConfigFailed(configRequest2, reason);
- inOrder.verify(mockListener).onIdentityChanged();
- inOrder.verify(mockListener).onNanDown(reason);
- verifyNoMoreInteractions(mockListener);
-
- validateInternalTransactionInfoCleanedUp(transactionId1);
- validateInternalTransactionInfoCleanedUp(transactionId2);
+ verifyNoMoreInteractions(mockCallback1, mockCallback2, mMockNative);
}
+ /**
+ * Validate that when the HAL doesn't respond we get a TIMEOUT (which
+ * results in a failure response) at which point we can process additional
+ * commands. Steps: (1) connect, (2) publish - timeout, (3) publish +
+ * success.
+ */
@Test
- public void testNanEventsNotDelivered() throws Exception {
- final int uid = 1005;
- final int clusterLow1 = 5;
- final int clusterHigh1 = 100;
- final int masterPref1 = 111;
- final int clusterLow2 = 7;
- final int clusterHigh2 = 155;
- final int masterPref2 = 0;
- final int reason = WifiNanSessionListener.FAIL_REASON_NO_RESOURCES;
- final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false);
+ public void testHalNoResponseTimeout() throws Exception {
+ final int clientId = 12341;
+ final int uid = 1000;
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final PublishConfig publishConfig = new PublishConfig.Builder().build();
- ConfigRequest configRequest1 = new ConfigRequest.Builder().setClusterLow(clusterLow1)
- .setClusterHigh(clusterHigh1).setMasterPreference(masterPref1).build();
-
- ConfigRequest configRequest2 = new ConfigRequest.Builder().setClusterLow(clusterLow2)
- .setClusterHigh(clusterHigh2).setMasterPreference(masterPref2).build();
-
- IWifiNanEventListener mockListener = mock(IWifiNanEventListener.class);
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- InOrder inOrder = inOrder(mockListener, mMockNative);
+ InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
- mDut.connect(uid, mockListener, 0);
- mDut.requestConfig(uid, configRequest1);
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest1));
- short transactionId1 = transactionId.getValue();
-
- mDut.requestConfig(uid, configRequest2);
+ // (1) connect (successfully)
+ mDut.connect(clientId, uid, mockCallback, configRequest);
mMockLooper.dispatchAll();
-
- inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest2));
- short transactionId2 = transactionId.getValue();
-
- mDut.onClusterChange(WifiNanClientState.CLUSTER_CHANGE_EVENT_JOINED, someMac);
- mDut.onConfigCompleted(transactionId1);
- mDut.onConfigFailed(transactionId2, reason);
- mDut.onInterfaceAddressChange(someMac);
- mDut.onNanDown(reason);
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
- verifyZeroInteractions(mockListener);
+ // (2) publish + timeout
+ mDut.publish(clientId, publishConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(anyShort(), eq(0), eq(publishConfig));
+ assertTrue(mAlarmManager.dispatch(WifiNanStateManager.HAL_COMMAND_TIMEOUT_TAG));
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback)
+ .onSessionConfigFail(WifiNanSessionCallback.REASON_OTHER);
+ validateInternalNoSessions(clientId);
- validateInternalTransactionInfoCleanedUp(transactionId1);
- validateInternalTransactionInfoCleanedUp(transactionId2);
+ // (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);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(anyInt());
+
+ verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
}
+ /**
+ * Validates publish flow: (1) initial publish (2) fail. Expected: get a
+ * failure callback.
+ */
@Test
- public void testPublish() throws Exception {
- final int uid = 1005;
- final int sessionId = 20;
- final String serviceName = "some-service-name";
- final String ssi = "some much longer and more arbitrary data";
- final int publishCount = 7;
- final int reasonFail = WifiNanSessionListener.FAIL_REASON_NO_RESOURCES;
- final int reasonTerminate = WifiNanSessionListener.TERMINATE_REASON_DONE;
- final int publishId1 = 15;
- final int publishId2 = 22;
+ public void testPublishFail() throws Exception {
+ final int clientId = 1005;
+ final int uid = 1000;
+ final int reasonFail = WifiNanSessionCallback.REASON_NO_RESOURCES;
- PublishData publishData = new PublishData.Builder().setServiceName(serviceName)
- .setServiceSpecificInfo(ssi).build();
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ PublishConfig publishConfig = new PublishConfig.Builder().build();
- PublishSettings publishSettings = new PublishSettings.Builder()
- .setPublishType(PublishSettings.PUBLISH_TYPE_UNSOLICITED)
- .setPublishCount(publishCount).build();
-
- IWifiNanSessionListener mockListener = mock(IWifiNanSessionListener.class);
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- InOrder inOrder = inOrder(mockListener, mMockNative);
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
- int allEvents = WifiNanSessionListener.LISTEN_PUBLISH_FAIL
- | WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED
- | WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
- | WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED
- | WifiNanSessionListener.LISTEN_MATCH
- | WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS
- | WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL
- | WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED;
-
- mDut.connect(uid, null, 0);
- mDut.createSession(uid, sessionId, mockListener, allEvents);
-
- // publish - fail
- mDut.publish(uid, sessionId, publishData, publishSettings);
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
- eq(publishSettings));
-
- mDut.onPublishFail(transactionId.getValue(), reasonFail);
+ // (0) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
mMockLooper.dispatchAll();
-
- inOrder.verify(mockListener).onPublishFail(reasonFail);
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
-
- // publish - success/terminate
- mDut.publish(uid, sessionId, publishData, publishSettings);
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
- eq(publishSettings));
-
- mDut.onPublishSuccess(transactionId.getValue(), publishId1);
+ // (1) initial publish
+ mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
- mDut.onPublishTerminated(publishId1, reasonTerminate);
+ // (2) publish failure
+ mDut.onSessionConfigFailResponse(transactionId.getValue(), true, reasonFail);
mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
+ validateInternalNoSessions(clientId);
- inOrder.verify(mockListener).onPublishTerminated(reasonTerminate);
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
-
- // re-publish
- mDut.publish(uid, sessionId, publishData, publishSettings);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
- eq(publishSettings));
-
- mDut.onPublishSuccess(transactionId.getValue(), publishId2);
- mDut.publish(uid, sessionId, publishData, publishSettings);
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(publishId2),
- eq(publishData), eq(publishSettings));
- verifyNoMoreInteractions(mockListener, mMockNative);
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
+ /**
+ * Validates the publish flow: (1) initial publish (2) success (3)
+ * termination (e.g. DONE) (4) update session attempt (5) terminateSession
+ * (6) update session attempt. Expected: session ID callback + session
+ * cleaned-up.
+ */
@Test
- public void testPublishNoCallbacks() throws Exception {
- final int uid = 1005;
- final int sessionId = 20;
- final String serviceName = "some-service-name";
- final String ssi = "some much longer and more arbitrary data";
- final int publishCount = 7;
- final int reasonFail = WifiNanSessionListener.FAIL_REASON_NO_RESOURCES;
- final int reasonTerminate = WifiNanSessionListener.TERMINATE_REASON_DONE;
+ public void testPublishSuccessTerminated() throws Exception {
+ final int clientId = 2005;
+ final int uid = 1000;
+ final int reasonTerminate = WifiNanSessionCallback.TERMINATE_REASON_DONE;
final int publishId = 15;
- PublishData publishData = new PublishData.Builder().setServiceName(serviceName)
- .setServiceSpecificInfo(ssi).build();
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ PublishConfig publishConfig = new PublishConfig.Builder().build();
- PublishSettings publishSettings = new PublishSettings.Builder()
- .setPublishType(PublishSettings.PUBLISH_TYPE_UNSOLICITED)
- .setPublishCount(publishCount).build();
-
- IWifiNanSessionListener mockListener = mock(IWifiNanSessionListener.class);
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- InOrder inOrder = inOrder(mockListener, mMockNative);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
- int allEvents = WifiNanSessionListener.LISTEN_PUBLISH_FAIL
- | WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED
- | WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
- | WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED
- | WifiNanSessionListener.LISTEN_MATCH
- | WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS
- | WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL
- | WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED;
-
- mDut.connect(uid, null, 0);
- mDut.createSession(uid, sessionId, mockListener,
- allEvents & ~WifiNanSessionListener.LISTEN_PUBLISH_FAIL
- & ~WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED);
-
- // publish - fail
- mDut.publish(uid, sessionId, publishData, publishSettings);
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
- eq(publishSettings));
+ // (0) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
- mDut.onPublishFail(transactionId.getValue(), reasonFail);
+ // (1) initial publish
+ mDut.publish(clientId, publishConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+
+ // (2) publish success
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+
+ // (3) publish termination (from firmware - not app!)
+ mDut.onSessionTerminatedNotification(publishId, reasonTerminate, true);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionTerminated(reasonTerminate);
+
+ // (4) app update session (race condition: app didn't get termination
+ // yet)
+ mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
mMockLooper.dispatchAll();
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
-
- // publish - success/terminate
- mDut.publish(uid, sessionId, publishData, publishSettings);
+ // (5) app terminates session
+ mDut.terminateSession(clientId, sessionId.getValue());
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
- eq(publishSettings));
-
- mDut.onPublishSuccess(transactionId.getValue(), publishId);
+ // (6) app updates session (app already knows that terminated - will get
+ // a local FAIL).
+ mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
mMockLooper.dispatchAll();
- mDut.onPublishTerminated(publishId, reasonTerminate);
- mMockLooper.dispatchAll();
+ validateInternalSessionInfoCleanedUp(clientId, sessionId.getValue());
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- verifyNoMoreInteractions(mockListener, mMockNative);
+ verifyNoMoreInteractions(mockSessionCallback, mMockNative);
}
+ /**
+ * Validate the publish flow: (1) initial publish + (2) success + (3) update
+ * + (4) update fails + (5) update + (6). Expected: session is still alive
+ * after update failure so second update succeeds (no callbacks).
+ */
@Test
- public void testSubscribe() throws Exception {
- final int uid = 1005;
- final int sessionId = 20;
- final String serviceName = "some-service-name";
- final String ssi = "some much longer and more arbitrary data";
- final int subscribeCount = 7;
- final int reasonFail = WifiNanSessionListener.FAIL_REASON_NO_RESOURCES;
- final int reasonTerminate = WifiNanSessionListener.TERMINATE_REASON_DONE;
- final int subscribeId1 = 15;
- final int subscribeId2 = 10;
+ public void testPublishUpdateFail() throws Exception {
+ final int clientId = 2005;
+ final int uid = 1000;
+ final int publishId = 15;
+ final int reasonFail = WifiNanSessionCallback.REASON_INVALID_ARGS;
- SubscribeData subscribeData = new SubscribeData.Builder().setServiceName(serviceName)
- .setServiceSpecificInfo(ssi).build();
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ PublishConfig publishConfig = new PublishConfig.Builder().build();
- SubscribeSettings subscribeSettings = new SubscribeSettings.Builder()
- .setSubscribeType(SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE)
- .setSubscribeCount(subscribeCount).build();
-
- IWifiNanSessionListener mockListener = mock(IWifiNanSessionListener.class);
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- InOrder inOrder = inOrder(mockListener, mMockNative);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
- int allEvents = WifiNanSessionListener.LISTEN_PUBLISH_FAIL
- | WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED
- | WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
- | WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED
- | WifiNanSessionListener.LISTEN_MATCH
- | WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS
- | WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL
- | WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED;
-
- mDut.connect(uid, null, 0);
- mDut.createSession(uid, sessionId, mockListener, allEvents);
-
- // subscribe - fail
- mDut.subscribe(uid, sessionId, subscribeData, subscribeSettings);
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
- eq(subscribeSettings));
-
- mDut.onSubscribeFail(transactionId.getValue(), reasonFail);
+ // (0) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mockListener).onSubscribeFail(reasonFail);
-
- // subscribe - success/terminate
- mDut.subscribe(uid, sessionId, subscribeData, subscribeSettings);
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
- eq(subscribeSettings));
-
- mDut.onSubscribeSuccess(transactionId.getValue(), subscribeId1);
+ // (1) initial publish
+ mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
- mDut.onSubscribeTerminated(subscribeId1, reasonTerminate);
+ // (2) publish success
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mockListener).onSubscribeTerminated(reasonTerminate);
-
- // re-subscribe
- mDut.subscribe(uid, sessionId, subscribeData, subscribeSettings);
+ // (3) update publish
+ mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(publishId),
+ eq(publishConfig));
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
- eq(subscribeSettings));
-
- mDut.onSubscribeSuccess(transactionId.getValue(), subscribeId2);
- mDut.subscribe(uid, sessionId, subscribeData, subscribeSettings);
+ // (4) update fails
+ mDut.onSessionConfigFailResponse(transactionId.getValue(), true, reasonFail);
mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(subscribeId2),
- eq(subscribeData), eq(subscribeSettings));
- verifyNoMoreInteractions(mockListener, mMockNative);
+ // (5) another update publish
+ mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(publishId),
+ eq(publishConfig));
+
+ // (6) update succeeds
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionConfigSuccess();
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
+ /**
+ * Validate race condition: publish pending but session terminated (due to
+ * disconnect - can't terminate such a session directly from app). Need to
+ * make sure that once publish succeeds (failure isn't a problem) the
+ * session is immediately terminated since no-one is listening for it.
+ */
@Test
- public void testSubscribeNoCallbacks() throws Exception {
- final int uid = 1005;
- final int sessionId = 20;
- final String serviceName = "some-service-name";
- final String ssi = "some much longer and more arbitrary data";
- final int subscribeCount = 7;
- final int reasonFail = WifiNanSessionListener.FAIL_REASON_NO_RESOURCES;
- final int reasonTerminate = WifiNanSessionListener.TERMINATE_REASON_DONE;
+ public void testDisconnectWhilePublishPending() throws Exception {
+ final int clientId = 2005;
+ final int uid = 1000;
+ final int publishId = 15;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ PublishConfig publishConfig = new PublishConfig.Builder().build();
+
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (0) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (1) initial publish
+ mDut.publish(clientId, publishConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+
+ // (2) disconnect (but doesn't get executed until get response for
+ // publish command)
+ mDut.disconnect(clientId);
+ mMockLooper.dispatchAll();
+
+ // (3) publish success
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(anyInt());
+ inOrder.verify(mMockNative).stopPublish(transactionId.capture(), eq(publishId));
+ inOrder.verify(mMockNative).disable((short) 0);
+
+ validateInternalClientInfoCleanedUp(clientId);
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
+ }
+
+ /**
+ * Validates subscribe flow: (1) initial subscribe (2) fail. Expected: get a
+ * failure callback.
+ */
+ @Test
+ public void testSubscribeFail() throws Exception {
+ final int clientId = 1005;
+ final int uid = 1000;
+ final int reasonFail = WifiNanSessionCallback.REASON_NO_RESOURCES;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (0) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (1) initial subscribe
+ mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+
+ // (2) subscribe failure
+ mDut.onSessionConfigFailResponse(transactionId.getValue(), false, reasonFail);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
+ validateInternalNoSessions(clientId);
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
+ }
+
+ /**
+ * Validates the subscribe flow: (1) initial subscribe (2) success (3)
+ * termination (e.g. DONE) (4) update session attempt (5) terminateSession
+ * (6) update session attempt. Expected: session ID callback + session
+ * cleaned-up
+ */
+ @Test
+ public void testSubscribeSuccessTerminated() throws Exception {
+ final int clientId = 2005;
+ final int uid = 1000;
+ final int reasonTerminate = WifiNanSessionCallback.TERMINATE_REASON_DONE;
final int subscribeId = 15;
- SubscribeData subscribeData = new SubscribeData.Builder().setServiceName(serviceName)
- .setServiceSpecificInfo(ssi).build();
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
- SubscribeSettings subscribeSettings = new SubscribeSettings.Builder()
- .setSubscribeType(SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE)
- .setSubscribeCount(subscribeCount).build();
-
- IWifiNanSessionListener mockListener = mock(IWifiNanSessionListener.class);
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- InOrder inOrder = inOrder(mockListener, mMockNative);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
- int allEvents = WifiNanSessionListener.LISTEN_PUBLISH_FAIL
- | WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED
- | WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
- | WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED
- | WifiNanSessionListener.LISTEN_MATCH
- | WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS
- | WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL
- | WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED;
-
- mDut.connect(uid, null, 0);
- mDut.createSession(uid, sessionId, mockListener,
- allEvents & ~WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
- & ~WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED);
-
- // subscribe - fail
- mDut.subscribe(uid, sessionId, subscribeData, subscribeSettings);
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
- eq(subscribeSettings));
+ // (0) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
- mDut.onSubscribeFail(transactionId.getValue(), reasonFail);
+ // (1) initial subscribe
+ mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+
+ // (2) subscribe success
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+
+ // (3) subscribe termination (from firmware - not app!)
+ mDut.onSessionTerminatedNotification(subscribeId, reasonTerminate, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionTerminated(reasonTerminate);
+
+ // (4) app update session (race condition: app didn't get termination
+ // yet)
+ mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
mMockLooper.dispatchAll();
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
-
- // subscribe - success/terminate
- mDut.subscribe(uid, sessionId, subscribeData, subscribeSettings);
+ // (5) app terminates session
+ mDut.terminateSession(clientId, sessionId.getValue());
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
- eq(subscribeSettings));
-
- mDut.onSubscribeSuccess(transactionId.getValue(), subscribeId);
+ // (6) app updates session
+ mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
mMockLooper.dispatchAll();
- mDut.onSubscribeTerminated(subscribeId, reasonTerminate);
- mMockLooper.dispatchAll();
+ validateInternalSessionInfoCleanedUp(clientId, sessionId.getValue());
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- verifyNoMoreInteractions(mockListener, mMockNative);
+ verifyNoMoreInteractions(mockSessionCallback, mMockNative);
}
+ /**
+ * Validate the subscribe flow: (1) initial subscribe + (2) success + (3)
+ * update + (4) update fails + (5) update + (6). Expected: session is still
+ * alive after update failure so second update succeeds (no callbacks).
+ */
+ @Test
+ public void testSubscribeUpdateFail() throws Exception {
+ final int clientId = 2005;
+ final int uid = 1000;
+ final int subscribeId = 15;
+ final int reasonFail = WifiNanSessionCallback.REASON_INVALID_ARGS;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (0) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (1) initial subscribe
+ mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+
+ // (2) subscribe success
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+
+ // (3) update subscribe
+ mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(subscribeId),
+ eq(subscribeConfig));
+
+ // (4) update fails
+ mDut.onSessionConfigFailResponse(transactionId.getValue(), false, reasonFail);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
+
+ // (5) another update subscribe
+ mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(subscribeId),
+ eq(subscribeConfig));
+
+ // (6) update succeeds
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionConfigSuccess();
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
+ }
+
+ /**
+ * Validate race condition: subscribe pending but session terminated (due to
+ * disconnect - can't terminate such a session directly from app). Need to
+ * make sure that once subscribe succeeds (failure isn't a problem) the
+ * session is immediately terminated since no-one is listening for it.
+ */
+ @Test
+ public void testDisconnectWhileSubscribePending() throws Exception {
+ final int clientId = 2005;
+ final int uid = 1000;
+ final int subscribeId = 15;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (0) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (1) initial subscribe
+ mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+
+ // (2) disconnect (but doesn't get executed until get response for
+ // subscribe command)
+ mDut.disconnect(clientId);
+ mMockLooper.dispatchAll();
+
+ // (3) subscribe success
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(anyInt());
+ inOrder.verify(mMockNative).stopSubscribe((short) 0, subscribeId);
+ inOrder.verify(mMockNative).disable((short) 0);
+
+ validateInternalClientInfoCleanedUp(clientId);
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
+ }
+
+ /**
+ * Validate (1) subscribe (success), (2) match (i.e. discovery), (3) message reception,
+ * (4) message transmission failed (after ok queuing), (5) message transmission success.
+ */
@Test
public void testMatchAndMessages() throws Exception {
- final int uid = 1005;
- final int sessionId = 20;
+ final int clientId = 1005;
+ final int uid = 1000;
final String serviceName = "some-service-name";
final String ssi = "some much longer and more arbitrary data";
final int subscribeCount = 7;
- final int reasonFail = WifiNanSessionListener.FAIL_REASON_NO_RESOURCES;
+ final int reasonFail = WifiNanSessionCallback.REASON_TX_FAIL;
final int subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
@@ -490,73 +893,87 @@
final String peerMatchFilter = "filter binary array represented as string";
final String peerMsg = "some message from peer";
final int messageId = 6948;
+ final int messageId2 = 6949;
- SubscribeData subscribeData = new SubscribeData.Builder().setServiceName(serviceName)
- .setServiceSpecificInfo(ssi).build();
-
- SubscribeSettings subscribeSettings = new SubscribeSettings.Builder()
- .setSubscribeType(SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE)
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
+ .setServiceSpecificInfo(ssi)
+ .setSubscribeType(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE)
.setSubscribeCount(subscribeCount).build();
- IWifiNanSessionListener mockListener = mock(IWifiNanSessionListener.class);
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- InOrder inOrder = inOrder(mockListener, mMockNative);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
- int allEvents = WifiNanSessionListener.LISTEN_PUBLISH_FAIL
- | WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED
- | WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
- | WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED
- | WifiNanSessionListener.LISTEN_MATCH
- | WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS
- | WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL
- | WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED;
-
- mDut.connect(uid, null, 0);
- mDut.createSession(uid, sessionId, mockListener, allEvents);
- mDut.subscribe(uid, sessionId, subscribeData, subscribeSettings);
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
- eq(subscribeSettings));
+ // (0) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
- mDut.onSubscribeSuccess(transactionId.getValue(), subscribeId);
- mDut.onMatch(subscribeId, requestorId, peerMac, peerSsi.getBytes(), peerSsi.length(),
- peerMatchFilter.getBytes(), peerMatchFilter.length());
- mDut.onMessageReceived(subscribeId, requestorId, peerMac, peerMsg.getBytes(),
+ // (1) subscribe
+ mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+
+ // (2) match
+ mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
+ peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
+ peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length());
+
+ // (3) message Rx
+ mDut.onMessageReceivedNotification(subscribeId, requestorId, peerMac, peerMsg.getBytes(),
peerMsg.length());
mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mockListener).onMatch(requestorId, peerSsi.getBytes(), peerSsi.length(),
- peerMatchFilter.getBytes(), peerMatchFilter.length());
- inOrder.verify(mockListener).onMessageReceived(requestorId, peerMsg.getBytes(),
+ inOrder.verify(mockSessionCallback).onMessageReceived(requestorId, peerMsg.getBytes(),
peerMsg.length());
- mDut.sendMessage(uid, sessionId, requestorId, ssi.getBytes(), ssi.length(), messageId);
+ // (4) message Tx successful queuing
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), ssi.length(),
+ messageId, 0);
mMockLooper.dispatchAll();
-
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
- eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length()));
-
- mDut.onMessageSendFail(transactionId.getValue(), reasonFail);
+ eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length()), eq(messageId));
+ short tid1 = transactionId.getValue();
+ mDut.onMessageSendQueuedSuccessResponse(tid1);
mMockLooper.dispatchAll();
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mockListener).onMessageSendFail(messageId, reasonFail);
-
- mDut.sendMessage(uid, sessionId, requestorId, ssi.getBytes(), ssi.length(), messageId);
+ // (5) message Tx successful queuing
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), ssi.length(),
+ messageId2, 0);
mMockLooper.dispatchAll();
-
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
- eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length()));
-
- mDut.onMessageSendSuccess(transactionId.getValue());
+ eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length()), eq(messageId2));
+ short tid2 = transactionId.getValue();
+ mDut.onMessageSendQueuedSuccessResponse(tid2);
mMockLooper.dispatchAll();
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mockListener).onMessageSendSuccess(messageId);
+ // (4) and (5) final Tx results (on-air results)
+ mDut.onMessageSendFailNotification(tid1, reasonFail);
+ mDut.onMessageSendSuccessNotification(tid2);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageSendFail(messageId, reasonFail);
+ inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId2);
+ validateInternalSendMessageQueuesCleanedUp(messageId);
+ validateInternalSendMessageQueuesCleanedUp(messageId2);
- verifyNoMoreInteractions(mockListener, mMockNative);
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
/**
@@ -565,11 +982,11 @@
*/
@Test
public void testMultipleMessageSources() throws Exception {
- final int uid = 300;
+ final int clientId = 300;
+ final int uid = 1000;
final int clusterLow = 7;
final int clusterHigh = 7;
final int masterPref = 0;
- final int sessionId = 26;
final String serviceName = "some-service-name";
final int publishId = 88;
final int peerId1 = 568;
@@ -582,83 +999,79 @@
final String msgToPeer2 = "hey there 0506...";
final int msgToPeerId1 = 546;
final int msgToPeerId2 = 9654;
- final int reason = WifiNanSessionListener.FAIL_REASON_OTHER;
+ final int reason = WifiNanSessionCallback.REASON_OTHER;
ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
.setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
- PublishData publishData = new PublishData.Builder().setServiceName(serviceName).build();
-
- PublishSettings publishSettings = new PublishSettings.Builder()
- .setPublishType(PublishSettings.PUBLISH_TYPE_UNSOLICITED).build();
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
+ .setPublishType(PublishConfig.PUBLISH_TYPE_UNSOLICITED).build();
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- IWifiNanEventListener mockListener = mock(IWifiNanEventListener.class);
- IWifiNanSessionListener mockSessionListener = mock(IWifiNanSessionListener.class);
- InOrder inOrder = inOrder(mMockNative, mockListener, mockSessionListener);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
+ InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
- int allEvents = WifiNanEventListener.LISTEN_CONFIG_COMPLETED
- | WifiNanEventListener.LISTEN_CONFIG_FAILED
- | WifiNanEventListener.LISTEN_IDENTITY_CHANGED
- | WifiNanEventListener.LISTEN_NAN_DOWN;
-
- int allSessionEvents = WifiNanSessionListener.LISTEN_PUBLISH_FAIL
- | WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED
- | WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
- | WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED
- | WifiNanSessionListener.LISTEN_MATCH
- | WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS
- | WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL
- | WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED;
-
- mDut.connect(uid, mockListener, allEvents);
- mDut.requestConfig(uid, configRequest);
- mDut.createSession(uid, sessionId, mockSessionListener, allSessionEvents);
- mDut.publish(uid, sessionId, publishData, publishSettings);
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest));
- short transactionIdConfig = transactionId.getValue();
-
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
- eq(publishSettings));
- short transactionIdPublish = transactionId.getValue();
-
- mDut.onConfigCompleted(transactionIdConfig);
- mDut.onPublishSuccess(transactionIdPublish, publishId);
- mDut.onMessageReceived(publishId, peerId1, peerMac1, msgFromPeer1.getBytes(),
- msgFromPeer1.length());
- mDut.onMessageReceived(publishId, peerId2, peerMac2, msgFromPeer2.getBytes(),
- msgFromPeer2.length());
- mDut.sendMessage(uid, sessionId, peerId2, msgToPeer2.getBytes(), msgToPeer2.length(),
- msgToPeerId2);
- mDut.sendMessage(uid, sessionId, peerId1, msgToPeer1.getBytes(), msgToPeer1.length(),
- msgToPeerId1);
+ // (1) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
- validateInternalTransactionInfoCleanedUp(transactionIdConfig);
- validateInternalTransactionInfoCleanedUp(transactionIdPublish);
- inOrder.verify(mockListener).onConfigCompleted(configRequest);
- inOrder.verify(mockSessionListener).onMessageReceived(peerId1, msgFromPeer1.getBytes(),
+ // (2) publish
+ mDut.publish(clientId, publishConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(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(),
msgFromPeer1.length());
- inOrder.verify(mockSessionListener).onMessageReceived(peerId2, msgFromPeer2.getBytes(),
+ mDut.onMessageReceivedNotification(publishId, peerId2, peerMac2, msgFromPeer2.getBytes(),
msgFromPeer2.length());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageReceived(peerId1, msgFromPeer1.getBytes(),
+ msgFromPeer1.length());
+ inOrder.verify(mockSessionCallback).onMessageReceived(peerId2, msgFromPeer2.getBytes(),
+ msgFromPeer2.length());
+
+ // (4) sending messages back to same peers: one Tx fails, other succeeds
+ mDut.sendMessage(clientId, sessionId.getValue(), peerId2, msgToPeer2.getBytes(),
+ msgToPeer2.length(), msgToPeerId2, 0);
+ mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId2),
- eq(peerMac2), eq(msgToPeer2.getBytes()), eq(msgToPeer2.length()));
- short transactionIdMsg2 = transactionId.getValue();
- inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId1),
- eq(peerMac1), eq(msgToPeer1.getBytes()), eq(msgToPeer1.length()));
- short transactionIdMsg1 = transactionId.getValue();
+ eq(peerMac2), eq(msgToPeer2.getBytes()), eq(msgToPeer2.length()), eq(msgToPeerId2));
+ short transactionIdVal = transactionId.getValue();
+ mDut.onMessageSendQueuedSuccessResponse(transactionIdVal);
+ mDut.onMessageSendSuccessNotification(transactionIdVal);
- mDut.onMessageSendFail(transactionIdMsg1, reason);
- mDut.onMessageSendSuccess(transactionIdMsg2);
+ mDut.sendMessage(clientId, sessionId.getValue(), peerId1, msgToPeer1.getBytes(),
+ msgToPeer1.length(), 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(msgToPeer1.length()), eq(msgToPeerId1));
+ transactionIdVal = transactionId.getValue();
+ mDut.onMessageSendQueuedSuccessResponse(transactionIdVal);
+ mDut.onMessageSendFailNotification(transactionIdVal, reason);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageSendFail(msgToPeerId1, reason);
+ validateInternalSendMessageQueuesCleanedUp(msgToPeerId1);
+ validateInternalSendMessageQueuesCleanedUp(msgToPeerId2);
- validateInternalTransactionInfoCleanedUp(transactionIdMsg1);
- validateInternalTransactionInfoCleanedUp(transactionIdMsg2);
- inOrder.verify(mockSessionListener).onMessageSendFail(msgToPeerId1, reason);
- inOrder.verify(mockSessionListener).onMessageSendSuccess(msgToPeerId2);
- verifyNoMoreInteractions(mMockNative, mockListener, mockSessionListener);
+ verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
}
/**
@@ -667,11 +1080,11 @@
*/
@Test
public void testMessageWhilePeerChangesIdentity() throws Exception {
- final int uid = 300;
+ final int clientId = 300;
+ final int uid = 1000;
final int clusterLow = 7;
final int clusterHigh = 7;
final int masterPref = 0;
- final int sessionId = 26;
final String serviceName = "some-service-name";
final int publishId = 88;
final int peerId = 568;
@@ -686,387 +1099,1125 @@
ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
.setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
- PublishData publishData = new PublishData.Builder().setServiceName(serviceName).build();
-
- PublishSettings publishSettings = new PublishSettings.Builder()
- .setPublishType(PublishSettings.PUBLISH_TYPE_UNSOLICITED).build();
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
+ .setPublishType(PublishConfig.PUBLISH_TYPE_UNSOLICITED).build();
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- IWifiNanEventListener mockListener = mock(IWifiNanEventListener.class);
- IWifiNanSessionListener mockSessionListener = mock(IWifiNanSessionListener.class);
- InOrder inOrder = inOrder(mMockNative, mockListener, mockSessionListener);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
+ InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
- int allEvents = WifiNanEventListener.LISTEN_CONFIG_COMPLETED
- | WifiNanEventListener.LISTEN_CONFIG_FAILED
- | WifiNanEventListener.LISTEN_IDENTITY_CHANGED
- | WifiNanEventListener.LISTEN_NAN_DOWN;
-
- int allSessionEvents = WifiNanSessionListener.LISTEN_PUBLISH_FAIL
- | WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED
- | WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
- | WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED
- | WifiNanSessionListener.LISTEN_MATCH
- | WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS
- | WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL
- | WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED;
-
- mDut.connect(uid, mockListener, allEvents);
- mDut.requestConfig(uid, configRequest);
- mDut.createSession(uid, sessionId, mockSessionListener, allSessionEvents);
- mDut.publish(uid, sessionId, publishData, publishSettings);
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest));
- short transactionIdConfig = transactionId.getValue();
+ // (1) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
- eq(publishSettings));
- short transactionIdPublish = transactionId.getValue();
+ // (2) publish
+ mDut.publish(clientId, publishConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
- mDut.onConfigCompleted(transactionIdConfig);
- mDut.onPublishSuccess(transactionIdPublish, publishId);
- mDut.onMessageReceived(publishId, peerId, peerMacOrig, msgFromPeer1.getBytes(),
+ // (3) message received & responded to
+ mDut.onMessageReceivedNotification(publishId, peerId, peerMacOrig, msgFromPeer1.getBytes(),
msgFromPeer1.length());
- mDut.sendMessage(uid, sessionId, peerId, msgToPeer1.getBytes(), msgToPeer1.length(),
- msgToPeerId1);
+ mDut.sendMessage(clientId, sessionId.getValue(), peerId, msgToPeer1.getBytes(),
+ msgToPeer1.length(), msgToPeerId1, 0);
mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionIdConfig);
- validateInternalTransactionInfoCleanedUp(transactionIdPublish);
- inOrder.verify(mockListener).onConfigCompleted(configRequest);
- inOrder.verify(mockSessionListener).onMessageReceived(peerId, msgFromPeer1.getBytes(),
+ inOrder.verify(mockSessionCallback).onMessageReceived(peerId, msgFromPeer1.getBytes(),
msgFromPeer1.length());
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId),
- eq(peerMacOrig), eq(msgToPeer1.getBytes()), eq(msgToPeer1.length()));
- short transactionIdMsg = transactionId.getValue();
-
- mDut.onMessageSendSuccess(transactionIdMsg);
- mDut.onMessageReceived(publishId, peerId, peerMacLater, msgFromPeer2.getBytes(),
- msgFromPeer2.length());
- mDut.sendMessage(uid, sessionId, peerId, msgToPeer2.getBytes(), msgToPeer2.length(),
- msgToPeerId2);
+ eq(peerMacOrig), eq(msgToPeer1.getBytes()), eq(msgToPeer1.length()),
+ eq(msgToPeerId1));
+ mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
+ mDut.onMessageSendSuccessNotification(transactionId.getValue());
mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageSendSuccess(msgToPeerId1);
+ validateInternalSendMessageQueuesCleanedUp(msgToPeerId1);
- validateInternalTransactionInfoCleanedUp(transactionIdMsg);
- inOrder.verify(mockSessionListener).onMessageSendSuccess(msgToPeerId1);
- inOrder.verify(mockSessionListener).onMessageReceived(peerId, msgFromPeer2.getBytes(),
+ // (4) message received with same peer ID but different MAC
+ mDut.onMessageReceivedNotification(publishId, peerId, peerMacLater, msgFromPeer2.getBytes(),
+ msgFromPeer2.length());
+ mDut.sendMessage(clientId, sessionId.getValue(), peerId, msgToPeer2.getBytes(),
+ msgToPeer2.length(), msgToPeerId2, 0);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageReceived(peerId, msgFromPeer2.getBytes(),
msgFromPeer2.length());
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId),
- eq(peerMacLater), eq(msgToPeer2.getBytes()), eq(msgToPeer2.length()));
- transactionIdMsg = transactionId.getValue();
+ eq(peerMacLater), eq(msgToPeer2.getBytes()), eq(msgToPeer2.length()),
+ eq(msgToPeerId2));
+ mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
+ mDut.onMessageSendSuccessNotification(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageSendSuccess(msgToPeerId2);
+ validateInternalSendMessageQueuesCleanedUp(msgToPeerId2);
- mDut.onMessageSendSuccess(transactionIdMsg);
+ verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
+ }
+
+ /**
+ * Validate that get failure (with correct code) when trying to send a
+ * message to an invalid peer ID.
+ */
+ @Test
+ public void testSendMessageToInvalidPeerId() throws Exception {
+ final int clientId = 1005;
+ final int uid = 1000;
+ final String ssi = "some much longer and more arbitrary data";
+ final int 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 messageId = 6948;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
- validateInternalTransactionInfoCleanedUp(transactionIdMsg);
- inOrder.verify(mockSessionListener).onMessageSendSuccess(msgToPeerId2);
+ // (1) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
- verifyNoMoreInteractions(mMockNative, mockListener, mockSessionListener);
+ // (2) subscribe & match
+ mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
+ mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
+ peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+ inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
+ peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length());
+
+ // (3) send message to invalid peer ID
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId + 5, ssi.getBytes(),
+ ssi.length(), messageId, 0);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageSendFail(messageId,
+ WifiNanSessionCallback.REASON_NO_MATCH_SESSION);
+ validateInternalSendMessageQueuesCleanedUp(messageId);
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
+ }
+
+ /**
+ * Validate that on send message timeout correct callback is dispatched and that a later
+ * firmware notification is ignored.
+ */
+ @Test
+ public void testSendMessageTimeout() throws Exception {
+ final int clientId = 1005;
+ final int uid = 1000;
+ final String ssi = "some much longer and more arbitrary data";
+ final int 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 messageId = 6948;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (1) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (2) subscribe & match
+ mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
+ mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
+ peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+ inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
+ peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length());
+
+ // (3) send 2 messages and enqueue successfully
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
+ ssi.length(), messageId, 0);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length()), eq(messageId));
+ short transactionId1 = transactionId.getValue();
+ mDut.onMessageSendQueuedSuccessResponse(transactionId1);
+ mMockLooper.dispatchAll();
+
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
+ ssi.length(), messageId + 1, 0);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length()),
+ eq(messageId + 1));
+ short transactionId2 = transactionId.getValue();
+ mDut.onMessageSendQueuedSuccessResponse(transactionId2);
+ mMockLooper.dispatchAll();
+
+ // (4) message send timeout
+ assertTrue(mAlarmManager.dispatch(WifiNanStateManager.HAL_SEND_MESSAGE_TIMEOUT_TAG));
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageSendFail(messageId,
+ WifiNanSessionCallback.REASON_TX_FAIL);
+ validateInternalSendMessageQueuesCleanedUp(messageId);
+
+ // (5) firmware response (unlikely - but good to check)
+ mDut.onMessageSendSuccessNotification(transactionId1);
+ mDut.onMessageSendSuccessNotification(transactionId2);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId + 1);
+
+ validateInternalSendMessageQueuesCleanedUp(messageId + 1);
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
+ }
+
+ /**
+ * Validate that when sending a message with a retry count the message is retried the specified
+ * number of times. Scenario ending with success.
+ */
+ @Test
+ public void testSendMessageRetransmitSuccess() throws Exception {
+ final int clientId = 1005;
+ final int uid = 1000;
+ final String ssi = "some much longer and more arbitrary data";
+ final int 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 messageId = 6948;
+ final int retryCount = 3;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (1) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (2) subscribe & match
+ mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
+ mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
+ peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+ inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
+ peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length());
+
+ // (3) send message and enqueue successfully
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
+ ssi.length(), messageId, retryCount);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length()), eq(messageId));
+ mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+
+ // (4) loop and fail until reach retryCount
+ for (int i = 0; i < retryCount; ++i) {
+ mDut.onMessageSendFailNotification(transactionId.getValue(),
+ WifiNanSessionCallback.REASON_TX_FAIL);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length()),
+ eq(messageId));
+ mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ }
+
+ // (5) succeed on last retry
+ mDut.onMessageSendSuccessNotification(transactionId.getValue());
+ mMockLooper.dispatchAll();
+
+ inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId);
+ validateInternalSendMessageQueuesCleanedUp(messageId);
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
+ }
+
+ /**
+ * Validate that when sending a message with a retry count the message is retried the specified
+ * number of times. Scenario ending with failure.
+ */
+ @Test
+ public void testSendMessageRetransmitFail() throws Exception {
+ final int clientId = 1005;
+ final int uid = 1000;
+ final String ssi = "some much longer and more arbitrary data";
+ final int 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 messageId = 6948;
+ final int retryCount = 3;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (1) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (2) subscribe & match
+ mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
+ mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
+ peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+ inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
+ peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length());
+
+ // (3) send message and enqueue successfully
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
+ ssi.length(), messageId, retryCount);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length()), eq(messageId));
+ mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+
+ // (4) loop and fail until reach retryCount+1
+ for (int i = 0; i < retryCount + 1; ++i) {
+ mDut.onMessageSendFailNotification(transactionId.getValue(),
+ WifiNanSessionCallback.REASON_TX_FAIL);
+ mMockLooper.dispatchAll();
+
+ if (i != retryCount) {
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length()),
+ eq(messageId));
+ mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ }
+ }
+
+ inOrder.verify(mockSessionCallback).onMessageSendFail(messageId,
+ WifiNanSessionCallback.REASON_TX_FAIL);
+ validateInternalSendMessageQueuesCleanedUp(messageId);
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
@Test
+ public void testSendMessageQueueAllQueueFail() throws Exception {
+ WifiNanNative.Capabilities cap = getCapabilities();
+ testSendMessageQueue(SendMessageAnswer.OP_QUEUE_FAIL, cap,
+ cap.maxQueuedTransmitMessages + 5);
+ }
+
+ @Test
+ public void testSendMessageQueueAllTxSuccess() throws Exception {
+ WifiNanNative.Capabilities cap = getCapabilities();
+ testSendMessageQueue(SendMessageAnswer.OP_QUEUE_OK_SEND_OK, cap,
+ cap.maxQueuedTransmitMessages + 5);
+ }
+
+ @Test
+ public void testSendMessageQueueAllTxFailRetxOk() throws Exception {
+ WifiNanNative.Capabilities cap = getCapabilities();
+ testSendMessageQueue(SendMessageAnswer.OP_QUEUE_OK_SEND_RETX_OK, cap,
+ cap.maxQueuedTransmitMessages + 5);
+ }
+
+ @Test
+ public void testSendMessageQueueAllTxFail() throws Exception {
+ WifiNanNative.Capabilities cap = getCapabilities();
+ testSendMessageQueue(SendMessageAnswer.OP_QUEUE_OK_SEND_RETX_FAIL, cap,
+ cap.maxQueuedTransmitMessages + 5);
+ }
+
+ @Test
+ public void testSendMessageQueueRandomize() throws Exception {
+ WifiNanNative.Capabilities cap = getCapabilities();
+ testSendMessageQueue(SendMessageAnswer.OP_QUEUE_RANDOMIZE, cap,
+ cap.maxQueuedTransmitMessages * 10);
+ }
+
+ /**
+ * Validate that when sending more messages than can be queued by the firmware (based on
+ * capability information) they are queued. Support all possible test success/failure codes.
+ * @param behavior: SendMessageAnswer.OP_*.
+ */
+ private void testSendMessageQueue(int behavior, WifiNanNative.Capabilities cap, int numMessages)
+ throws Exception {
+ final int clientId = 1005;
+ final int uid = 1000;
+ final String ssi = "some much longer and more arbitrary data";
+ final int 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 messageId = 6948;
+ final int retryCount = 3;
+ final int reason = WifiNanSessionCallback.REASON_OTHER;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> msgId = ArgumentCaptor.forClass(Integer.class);
+
+ // (0) initial conditions
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), cap);
+ mMockLooper.dispatchAll();
+
+ // (1) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ verify(mockCallback).onConnectSuccess();
+
+ // (2) subscribe & match
+ mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
+ mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
+ peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length());
+ mMockLooper.dispatchAll();
+ verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+ verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(), peerSsi.length(),
+ peerMatchFilter.getBytes(), peerMatchFilter.length());
+
+ // (3) send large number of messages
+ SendMessageAnswer answerObj = new SendMessageAnswer(behavior);
+ when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(byte[].class),
+ any(byte[].class), anyInt(), anyInt())).thenAnswer(answerObj);
+ for (int i = 0; i < numMessages; ++i) {
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
+ ssi.length(), messageId + i, retryCount);
+ }
+ mMockLooper.dispatchAll();
+
+ int numSends = answerObj.ops[SendMessageAnswer.OP_QUEUE_FAIL]
+ + answerObj.ops[SendMessageAnswer.OP_QUEUE_OK_SEND_OK]
+ + answerObj.ops[SendMessageAnswer.OP_QUEUE_OK_SEND_RETX_OK] * 2
+ + answerObj.ops[SendMessageAnswer.OP_QUEUE_OK_SEND_RETX_FAIL] * (retryCount + 1);
+ int numOnSendSuccess = answerObj.ops[SendMessageAnswer.OP_QUEUE_OK_SEND_OK]
+ + answerObj.ops[SendMessageAnswer.OP_QUEUE_OK_SEND_RETX_OK];
+ int numOnSendFail = answerObj.ops[SendMessageAnswer.OP_QUEUE_OK_SEND_RETX_FAIL];
+
+ Log.v("WifiNanStateManagerTest",
+ "testSendMessageQueue: ops=" + Arrays.toString(answerObj.ops) + ", numSends="
+ + numSends + ", numOnSendSuccess=" + numOnSendSuccess + ", numOnSendFail="
+ + numOnSendFail);
+
+ verify(mMockNative, times(numSends)).sendMessage(anyShort(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length()), anyInt());
+ verify(mockSessionCallback, times(numOnSendSuccess)).onMessageSendSuccess(anyInt());
+ verify(mockSessionCallback, times(numOnSendFail)).onMessageSendFail(anyInt(), anyInt());
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
+ }
+
+ private class SendMessageAnswer extends MockAnswerUtil.AnswerWithArguments {
+ public static final int OP_QUEUE_FAIL = 0;
+ public static final int OP_QUEUE_OK_SEND_OK = 1;
+ public static final int OP_QUEUE_OK_SEND_RETX_OK = 2;
+ public static final int OP_QUEUE_OK_SEND_RETX_FAIL = 3;
+
+ /* psuedo operation: randomly pick from the above 4 operations */
+ public static final int OP_QUEUE_RANDOMIZE = -1;
+
+ /* the number of operations which can be executed. Doesn't cound RANDOMIZE since it is
+ * resolved to one of the 4 types */
+ private static final int NUM_OPS = 4;
+
+ public int[] ops = new int[NUM_OPS];
+
+ private int mBehavior = 0;
+ private SparseIntArray mPacketBehavior = new SparseIntArray();
+
+ SendMessageAnswer(int behavior) {
+ mBehavior = behavior;
+ }
+
+ public boolean answer(short transactionId, int pubSubId, int requestorInstanceId,
+ byte[] dest, byte[] message, int messageLength, int messageId) throws Exception {
+ Log.v("WifiNanStateManagerTest",
+ "SendMessageAnswer.answer: mBehavior=" + mBehavior + ", transactionId="
+ + transactionId + ", messageId=" + messageId
+ + ", mPacketBehavior[messageId]" + mPacketBehavior.get(messageId, -1));
+
+ int behavior = mBehavior;
+ if (behavior == OP_QUEUE_RANDOMIZE) {
+ behavior = mRandomNg.nextInt(NUM_OPS);
+ }
+
+ boolean packetRetx = mPacketBehavior.get(messageId, -1) != -1;
+ if (packetRetx) {
+ behavior = mPacketBehavior.get(messageId);
+ } else {
+ mPacketBehavior.put(messageId, behavior);
+ }
+
+ if (behavior == OP_QUEUE_FAIL) {
+ ops[OP_QUEUE_FAIL]++;
+ mDut.onMessageSendQueuedFailResponse(transactionId,
+ WifiNanSessionCallback.REASON_OTHER);
+ } else if (behavior == OP_QUEUE_OK_SEND_OK) {
+ ops[OP_QUEUE_OK_SEND_OK]++;
+ mDut.onMessageSendQueuedSuccessResponse(transactionId);
+ mDut.onMessageSendSuccessNotification(transactionId);
+ } else if (behavior == OP_QUEUE_OK_SEND_RETX_OK) {
+ mDut.onMessageSendQueuedSuccessResponse(transactionId);
+ if (!packetRetx) {
+ mDut.onMessageSendFailNotification(transactionId,
+ WifiNanSessionCallback.REASON_TX_FAIL);
+ } else {
+ ops[OP_QUEUE_OK_SEND_RETX_OK]++;
+ mDut.onMessageSendSuccessNotification(transactionId);
+ }
+ } else if (behavior == OP_QUEUE_OK_SEND_RETX_FAIL) {
+ mDut.onMessageSendQueuedSuccessResponse(transactionId);
+ if (!packetRetx) {
+ ops[OP_QUEUE_OK_SEND_RETX_FAIL]++;
+ }
+ mDut.onMessageSendFailNotification(transactionId,
+ WifiNanSessionCallback.REASON_TX_FAIL);
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Validate that start ranging function fills-in correct MAC addresses for peer IDs and
+ * passed along to RTT module.
+ */
+ @Test
+ public void testStartRanging() throws Exception {
+ final int clientId = 1005;
+ final int uid = 1000;
+ final int 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();
+
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<WifiNanClientState> clientCaptor =
+ ArgumentCaptor.forClass(WifiNanClientState.class);
+ ArgumentCaptor<RttManager.RttParams[]> rttParamsCaptor =
+ ArgumentCaptor.forClass(RttManager.RttParams[].class);
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative,
+ mMockNanRttStateManager);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (1) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (2) subscribe & match
+ mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
+ mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
+ peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+ inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
+ peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length());
+
+ // (3) start ranging: pass along a valid peer ID and an invalid one
+ mDut.startRanging(clientId, sessionId.getValue(), params, rangingId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNanRttStateManager).startRanging(eq(rangingId), clientCaptor.capture(),
+ rttParamsCaptor.capture());
+ 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));
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative,
+ mMockNanRttStateManager);
+ }
+
+ /**
+ * Test sequence of configuration: (1) config1, (2) config2 - incompatible,
+ * (3) config3 - compatible with config1 (requiring upgrade), (4) disconnect
+ * config3 (should get a downgrade), (5) disconnect config1 (should get a
+ * disable).
+ */
+ @Test
public void testConfigs() throws Exception {
- final int uid1 = 9999;
+ final int clientId1 = 9999;
+ final int uid = 1000;
final int clusterLow1 = 5;
final int clusterHigh1 = 100;
final int masterPref1 = 111;
- final int uid2 = 1001;
+ final int clientId2 = 1001;
final boolean support5g2 = true;
final int clusterLow2 = 7;
final int clusterHigh2 = 155;
final int masterPref2 = 0;
- final int uid3 = 55;
+ final int clientId3 = 55;
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<ConfigRequest> crCapture = ArgumentCaptor.forClass(ConfigRequest.class);
ConfigRequest configRequest1 = new ConfigRequest.Builder().setClusterLow(clusterLow1)
- .setClusterHigh(clusterHigh1).setMasterPreference(masterPref1).build();
+ .setClusterHigh(clusterHigh1).setMasterPreference(masterPref1)
+ .setEnableIdentityChangeCallback(false).build();
ConfigRequest configRequest2 = new ConfigRequest.Builder().setSupport5gBand(support5g2)
.setClusterLow(clusterLow2).setClusterHigh(clusterHigh2)
.setMasterPreference(masterPref2).build();
- ConfigRequest configRequest3 = new ConfigRequest.Builder().build();
+ ConfigRequest configRequest3 = new ConfigRequest.Builder().setClusterLow(clusterLow1)
+ .setClusterHigh(clusterHigh1).setMasterPreference(masterPref1)
+ .setEnableIdentityChangeCallback(true).build();
- IWifiNanEventListener mockListener1 = mock(IWifiNanEventListener.class);
- IWifiNanEventListener mockListener2 = mock(IWifiNanEventListener.class);
- IWifiNanEventListener mockListener3 = mock(IWifiNanEventListener.class);
+ IWifiNanEventCallback mockCallback1 = mock(IWifiNanEventCallback.class);
+ IWifiNanEventCallback mockCallback2 = mock(IWifiNanEventCallback.class);
+ IWifiNanEventCallback mockCallback3 = mock(IWifiNanEventCallback.class);
- InOrder inOrder = inOrder(mMockNative, mockListener1, mockListener2, mockListener3);
+ InOrder inOrder = inOrder(mMockNative, mockCallback1, mockCallback2, mockCallback3);
- mDut.connect(uid1, mockListener1, WifiNanEventListener.LISTEN_CONFIG_COMPLETED);
- mDut.requestConfig(uid1, configRequest1);
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
+ // (1) config1 (valid)
+ mDut.connect(clientId1, uid, mockCallback1, configRequest1);
+ mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
- crCapture.capture());
- collector.checkThat("merge: stage 0", configRequest1, equalTo(crCapture.getValue()));
-
- mDut.onConfigCompleted(transactionId.getValue());
+ crCapture.capture(), eq(true));
+ collector.checkThat("merge: stage 1", crCapture.getValue(), equalTo(configRequest1));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback1).onConnectSuccess();
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mockListener1).onConfigCompleted(configRequest1);
-
- mDut.connect(uid2, mockListener2, WifiNanEventListener.LISTEN_CONFIG_COMPLETED);
- mDut.requestConfig(uid2, configRequest2);
+ // (2) config2 (incompatible with config1)
+ mDut.connect(clientId2, uid, mockCallback2, configRequest2);
mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback2)
+ .onConnectFail(WifiNanEventCallback.REASON_ALREADY_CONNECTED_INCOMPAT_CONFIG);
+ validateInternalClientInfoCleanedUp(clientId2);
+ // (3) config3 (compatible with config1 but requires upgrade - i.e. no
+ // OTA changes)
+ mDut.connect(clientId3, uid, mockCallback3, configRequest3);
+ mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
- crCapture.capture());
- collector.checkThat("merge: stage 1: support 5g", crCapture.getValue().mSupport5gBand,
- equalTo(true));
- collector.checkThat("merge: stage 1: master pref", crCapture.getValue().mMasterPreference,
- equalTo(Math.max(masterPref1, masterPref2)));
- collector.checkThat("merge: stage 1: cluster low", crCapture.getValue().mClusterLow,
- equalTo(Math.min(clusterLow1, clusterLow2)));
- collector.checkThat("merge: stage 1: cluster high", crCapture.getValue().mClusterHigh,
- equalTo(Math.max(clusterHigh1, clusterHigh2)));
-
- mDut.onConfigCompleted(transactionId.getValue());
+ crCapture.capture(), eq(false));
+ collector.checkThat("merge: stage 3: support 5g", crCapture.getValue().mSupport5gBand,
+ equalTo(false));
+ collector.checkThat("merge: stage 3: master pref", crCapture.getValue().mMasterPreference,
+ equalTo(masterPref1));
+ collector.checkThat("merge: stage 3: cluster low", crCapture.getValue().mClusterLow,
+ equalTo(clusterLow1));
+ collector.checkThat("merge: stage 3: cluster high", crCapture.getValue().mClusterHigh,
+ equalTo(clusterHigh1));
+ collector.checkThat("merge: stage 3: enable identity change callback",
+ crCapture.getValue().mEnableIdentityChangeCallback, equalTo(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback3).onConnectSuccess();
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mockListener1).onConfigCompleted(crCapture.getValue());
-
- mDut.connect(uid3, mockListener3, WifiNanEventListener.LISTEN_CONFIG_COMPLETED);
- mDut.requestConfig(uid3, configRequest3);
+ // (4) disconnect config3: want a downgrade
+ mDut.disconnect(clientId3);
mMockLooper.dispatchAll();
-
+ validateInternalClientInfoCleanedUp(clientId3);
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
- crCapture.capture());
- collector.checkThat("merge: stage 2: support 5g", crCapture.getValue().mSupport5gBand,
- equalTo(true));
- collector.checkThat("merge: stage 2: master pref", crCapture.getValue().mMasterPreference,
- equalTo(Math.max(masterPref1, masterPref2)));
- collector.checkThat("merge: stage 2: cluster low", crCapture.getValue().mClusterLow,
- equalTo(Math.min(clusterLow1, clusterLow2)));
- collector.checkThat("merge: stage 2: cluster high", crCapture.getValue().mClusterHigh,
- equalTo(Math.max(clusterHigh1, clusterHigh2)));
-
- mDut.onConfigCompleted(transactionId.getValue());
+ crCapture.capture(), eq(false));
+ collector.checkThat("merge: stage 4", crCapture.getValue(), equalTo(configRequest1));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mockListener1).onConfigCompleted(crCapture.getValue());
-
- mDut.disconnect(uid2);
+ // (5) disconnect config1: disable
+ mDut.disconnect(clientId1);
mMockLooper.dispatchAll();
+ validateInternalClientInfoCleanedUp(clientId1);
+ inOrder.verify(mMockNative).disable((short) 0);
- validateInternalClientInfoCleanedUp(uid2);
- inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
- crCapture.capture());
- collector.checkThat("merge: stage 3", configRequest1, equalTo(crCapture.getValue()));
-
- mDut.onConfigCompleted(transactionId.getValue());
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mockListener1).onConfigCompleted(crCapture.getValue());
-
- mDut.disconnect(uid1);
- mMockLooper.dispatchAll();
-
- validateInternalClientInfoCleanedUp(uid2);
- inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
- crCapture.capture());
- collector.checkThat("merge: stage 4", configRequest3, equalTo(crCapture.getValue()));
-
- mDut.onConfigCompleted(transactionId.getValue());
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mockListener3).onConfigCompleted(crCapture.getValue());
-
- mDut.disconnect(uid3);
- mMockLooper.dispatchAll();
-
- validateInternalClientInfoCleanedUp(uid2);
- inOrder.verify(mMockNative).disable(anyShort());
-
- verifyNoMoreInteractions(mMockNative);
+ verifyNoMoreInteractions(mMockNative, mockCallback1, mockCallback2, mockCallback3);
}
/**
* Summary: disconnect a client while there are pending transactions.
- * Validate that no callbacks are called and that internal state is
- * cleaned-up.
*/
@Test
public void testDisconnectWithPendingTransactions() throws Exception {
- final int uid = 125;
+ final int clientId = 125;
+ final int uid = 1000;
final int clusterLow = 5;
final int clusterHigh = 100;
final int masterPref = 111;
- final int sessionId = 20;
final String serviceName = "some-service-name";
final String ssi = "some much longer and more arbitrary data";
final int publishCount = 7;
- final int reason = WifiNanSessionListener.TERMINATE_REASON_DONE;
+ final int reason = WifiNanSessionCallback.TERMINATE_REASON_DONE;
final int publishId = 22;
ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
.setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
- PublishData publishData = new PublishData.Builder().setServiceName(serviceName)
- .setServiceSpecificInfo(ssi).build();
-
- PublishSettings publishSettings = new PublishSettings.Builder()
- .setPublishType(PublishSettings.PUBLISH_TYPE_UNSOLICITED)
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
+ .setServiceSpecificInfo(ssi).setPublishType(PublishConfig.PUBLISH_TYPE_UNSOLICITED)
.setPublishCount(publishCount).build();
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- IWifiNanEventListener mockListener = mock(IWifiNanEventListener.class);
- IWifiNanSessionListener mockSessionListener = mock(IWifiNanSessionListener.class);
- InOrder inOrder = inOrder(mMockNative, mockListener, mockSessionListener);
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
+ InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
- int allEvents = WifiNanEventListener.LISTEN_CONFIG_COMPLETED
- | WifiNanEventListener.LISTEN_CONFIG_FAILED
- | WifiNanEventListener.LISTEN_IDENTITY_CHANGED
- | WifiNanEventListener.LISTEN_NAN_DOWN;
-
- int allSessionEvents = WifiNanSessionListener.LISTEN_PUBLISH_FAIL
- | WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED
- | WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
- | WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED
- | WifiNanSessionListener.LISTEN_MATCH
- | WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS
- | WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL
- | WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED;
-
- mDut.connect(uid, mockListener, allEvents);
- mDut.createSession(uid, sessionId, mockSessionListener, allSessionEvents);
- mDut.requestConfig(uid, configRequest);
- mDut.publish(uid, sessionId, publishData, publishSettings);
- mDut.disconnect(uid);
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest));
- short transactionIdConfig = transactionId.getValue();
+ // (1) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
- eq(publishSettings));
- short transactionIdPublish = transactionId.getValue();
+ // (2) publish (no response yet)
+ mDut.publish(clientId, publishConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
- validateInternalClientInfoCleanedUp(uid);
- validateInternalTransactionInfoCleanedUp(transactionIdPublish);
-
- mDut.onConfigCompleted(transactionIdConfig);
- mDut.onPublishSuccess(transactionIdPublish, publishId);
+ // (3) disconnect (but doesn't get executed until get a RESPONSE to the
+ // previous publish)
+ mDut.disconnect(clientId);
mMockLooper.dispatchAll();
- mDut.onPublishTerminated(publishId, reason);
+ // (4) get successful response to the publish
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(anyInt());
+ inOrder.verify(mMockNative).stopPublish((short) 0, publishId);
+ inOrder.verify(mMockNative).disable((short) 0);
+
+ validateInternalClientInfoCleanedUp(clientId);
+
+ // (5) trying to publish on the same client: NOP
+ mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
- verifyZeroInteractions(mockListener, mockSessionListener);
+ // (6) got some callback on original publishId - should be ignored
+ mDut.onSessionTerminatedNotification(publishId, reason, true);
+ mMockLooper.dispatchAll();
+
+ verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
}
/**
- * Summary: destroy a session while there are pending transactions. Validate
- * that no callbacks are called and that internal state is cleaned-up.
+ * Validate that an unknown transaction (i.e. a callback from HAL with an
+ * unknown type) is simply ignored - but also cleans up its state.
*/
@Test
- public void testDestroySessionWithPendingTransactions() throws Exception {
- final int uid = 128;
+ public void testUnknownTransactionType() throws Exception {
+ final int clientId = 129;
+ final int uid = 1000;
final int clusterLow = 15;
final int clusterHigh = 192;
final int masterPref = 234;
- final int publishSessionId = 19;
- final int subscribeSessionId = 24;
final String serviceName = "some-service-name";
final String ssi = "some much longer and more arbitrary data";
final int publishCount = 15;
- final int subscribeCount = 22;
- final int reason = WifiNanSessionListener.TERMINATE_REASON_DONE;
- final int publishId = 23;
- final int subscribeId = 55;
ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
.setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
- PublishData publishData = new PublishData.Builder().setServiceName(serviceName)
- .setServiceSpecificInfo(ssi).build();
-
- PublishSettings publishSettings = new PublishSettings.Builder()
- .setPublishType(PublishSettings.PUBLISH_TYPE_UNSOLICITED)
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
+ .setServiceSpecificInfo(ssi).setPublishType(PublishConfig.PUBLISH_TYPE_UNSOLICITED)
.setPublishCount(publishCount).build();
- SubscribeData subscribeData = new SubscribeData.Builder().setServiceName(serviceName)
- .setServiceSpecificInfo(ssi).build();
-
- SubscribeSettings subscribeSettings = new SubscribeSettings.Builder()
- .setSubscribeType(SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE)
- .setSubscribeCount(subscribeCount).build();
-
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- IWifiNanEventListener mockListener = mock(IWifiNanEventListener.class);
- IWifiNanSessionListener mockPublishSessionListener = mock(IWifiNanSessionListener.class);
- IWifiNanSessionListener mockSubscribeSessionListener = mock(IWifiNanSessionListener.class);
- InOrder inOrder = inOrder(mMockNative, mockListener, mockPublishSessionListener,
- mockSubscribeSessionListener);
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockPublishSessionCallback = mock(IWifiNanSessionCallback.class);
+ InOrder inOrder = inOrder(mMockNative, mockCallback, mockPublishSessionCallback);
- int allEvents = WifiNanEventListener.LISTEN_CONFIG_COMPLETED
- | WifiNanEventListener.LISTEN_CONFIG_FAILED
- | WifiNanEventListener.LISTEN_IDENTITY_CHANGED
- | WifiNanEventListener.LISTEN_NAN_DOWN;
-
- int allSessionEvents = WifiNanSessionListener.LISTEN_PUBLISH_FAIL
- | WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED
- | WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
- | WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED
- | WifiNanSessionListener.LISTEN_MATCH
- | WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS
- | WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL
- | WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED;
-
- mDut.connect(uid, mockListener, allEvents);
- mDut.requestConfig(uid, configRequest);
- mDut.createSession(uid, publishSessionId, mockPublishSessionListener, allSessionEvents);
- mDut.publish(uid, publishSessionId, publishData, publishSettings);
- mDut.createSession(uid, subscribeSessionId, mockSubscribeSessionListener, allSessionEvents);
- mDut.subscribe(uid, subscribeSessionId, subscribeData, subscribeSettings);
- mDut.destroySession(uid, publishSessionId);
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
- inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest));
- short transactionIdConfig = transactionId.getValue();
-
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
- eq(publishSettings));
- short transactionIdPublish = transactionId.getValue();
-
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
- eq(subscribeSettings));
- short transactionIdSubscribe = transactionId.getValue();
-
- validateInternalTransactionInfoCleanedUp(transactionIdPublish);
-
- mDut.onConfigCompleted(transactionIdConfig);
- mDut.onPublishSuccess(transactionIdPublish, publishId);
- mDut.onSubscribeSuccess(transactionIdSubscribe, subscribeId);
+ // (1) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
mMockLooper.dispatchAll();
-
- mDut.onPublishTerminated(publishId, reason);
- mDut.destroySession(uid, subscribeSessionId);
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
- inOrder.verify(mockListener).onConfigCompleted(configRequest);
- verifyZeroInteractions(mockPublishSessionListener);
- verifyNoMoreInteractions(mockSubscribeSessionListener);
+ // (2) publish - no response
+ mDut.publish(clientId, publishConfig, mockPublishSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+
+ verifyNoMoreInteractions(mMockNative, mockCallback, mockPublishSessionCallback);
}
+ /**
+ * Validate that a NoOp transaction (i.e. a callback from HAL which doesn't
+ * require any action except clearing up state) actually cleans up its state
+ * (and does nothing else).
+ */
@Test
- public void testTransactionIdIncrement() {
+ public void testNoOpTransaction() throws Exception {
+ final int clientId = 1294;
+ final int uid = 1000;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
+ InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (1) connect (no response)
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+
+ verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
+ }
+
+ /**
+ * Validate that getting callbacks from HAL with unknown (expired)
+ * transaction ID or invalid publish/subscribe ID session doesn't have any
+ * impact.
+ */
+ @Test
+ public void testInvalidCallbackIdParameters() throws Exception {
+ final int pubSubId = 1235;
+ final int clientId = 132;
+ final int uid = 1000;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.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 and succeed
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ short transactionIdConfig = transactionId.getValue();
+ mDut.onConfigSuccessResponse(transactionIdConfig);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (2) use the same transaction ID to send a bunch of other responses
+ mDut.onConfigSuccessResponse(transactionIdConfig);
+ mDut.onConfigFailedResponse(transactionIdConfig, -1);
+ mDut.onSessionConfigFailResponse(transactionIdConfig, true, -1);
+ mDut.onMessageSendQueuedSuccessResponse(transactionIdConfig);
+ mDut.onMessageSendQueuedFailResponse(transactionIdConfig, -1);
+ mDut.onSessionConfigFailResponse(transactionIdConfig, false, -1);
+ mDut.onMatchNotification(-1, -1, new byte[0], new byte[0], 0, new byte[0], 0);
+ mDut.onSessionTerminatedNotification(-1, -1, true);
+ mDut.onSessionTerminatedNotification(-1, -1, false);
+ mDut.onMessageReceivedNotification(-1, -1, new byte[0], new byte[0], 0);
+ mDut.onSessionConfigSuccessResponse(transactionIdConfig, true, pubSubId);
+ mDut.onSessionConfigSuccessResponse(transactionIdConfig, false, pubSubId);
+ mMockLooper.dispatchAll();
+
+ verifyNoMoreInteractions(mMockNative, mockCallback);
+ }
+
+ /**
+ * Validate that trying to update-subscribe on a publish session fails.
+ */
+ @Test
+ public void testSubscribeOnPublishSessionType() throws Exception {
+ final int clientId = 188;
+ final int uid = 1000;
+ final int publishId = 25;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ PublishConfig publishConfig = new PublishConfig.Builder().build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
+ InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (1) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (2) publish
+ mDut.publish(clientId, publishConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+
+ // (3) update-subscribe -> failure
+ mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback)
+ .onSessionConfigFail(WifiNanSessionCallback.REASON_OTHER);
+
+ verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
+ }
+
+ /**
+ * Validate that trying to (re)subscribe on a publish session or (re)publish
+ * on a subscribe session fails.
+ */
+ @Test
+ public void testPublishOnSubscribeSessionType() throws Exception {
+ final int clientId = 188;
+ final int uid = 1000;
+ final int subscribeId = 25;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ PublishConfig publishConfig = new PublishConfig.Builder().build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
+ InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (1) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ // (2) subscribe
+ mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+
+ // (3) update-publish -> error
+ mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback)
+ .onSessionConfigFail(WifiNanSessionCallback.REASON_OTHER);
+
+ verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
+ }
+
+ /**
+ * Validate that the session ID increments monotonically
+ */
+ @Test
+ public void testSessionIdIncrement() throws Exception {
+ final int clientId = 188;
+ final int uid = 1000;
int loopCount = 100;
- short prevId = 0;
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ PublishConfig publishConfig = new PublishConfig.Builder().build();
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
+ IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class);
+ InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (1) connect
+ mDut.connect(clientId, uid, mockCallback, configRequest);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess();
+
+ int prevId = 0;
for (int i = 0; i < loopCount; ++i) {
- short id = mDut.createNextTransactionId();
+ // (2) publish
+ mDut.publish(clientId, publishConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+
+ // (3) publish-success
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, i + 1);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+
if (i != 0) {
- assertTrue("Transaction ID incrementing", id > prevId);
+ assertTrue("Session ID incrementing", sessionId.getValue() > prevId);
}
- prevId = id;
+ prevId = sessionId.getValue();
}
}
@@ -1079,34 +2230,83 @@
/**
* Utility routine used to validate that the internal state is cleaned-up
- * after the specific transaction ID. To be used in every test which
- * involves a transaction.
+ * after a client is disconnected. To be used in every test which terminates
+ * a client.
*
- * @param transactionId The transaction ID whose state should be erased.
+ * @param clientId The ID of the client which should be deleted.
*/
- public void validateInternalTransactionInfoCleanedUp(short transactionId) throws Exception {
- Object info = getInternalPendingTransactionInfo(mDut, transactionId);
- collector.checkThat("Transaction record not cleared up for transactionId=" + transactionId,
- info, nullValue());
+ private void validateInternalClientInfoCleanedUp(int clientId) throws Exception {
+ WifiNanClientState client = getInternalClientState(mDut, clientId);
+ collector.checkThat("Client record not cleared up for clientId=" + clientId, client,
+ nullValue());
}
/**
* Utility routine used to validate that the internal state is cleaned-up
- * after a client is disconnected. To be used in every test which terminates
- * a client.
+ * (deleted) after a session is terminated through API (not callback!). To
+ * be used in every test which terminates a session.
*
- * @param uid The ID of the client which should be deleted.
+ * @param clientId The ID of the client containing the session.
+ * @param sessionId The ID of the terminated session.
*/
- public void validateInternalClientInfoCleanedUp(int uid) throws Exception {
- WifiNanClientState client = getInternalClientState(mDut, uid);
- collector.checkThat("Client record not cleared up for uid=" + uid, client, nullValue());
+ private void validateInternalSessionInfoCleanedUp(int clientId, int sessionId)
+ throws Exception {
+ WifiNanClientState client = getInternalClientState(mDut, clientId);
+ collector.checkThat("Client record exists clientId=" + clientId, client, notNullValue());
+ WifiNanSessionState session = getInternalSessionState(client, sessionId);
+ collector.checkThat("Client record not cleaned-up for sessionId=" + sessionId, session,
+ nullValue());
+ }
+
+ /**
+ * Utility routine used to validate that the internal state is cleaned-up
+ * (deleted) correctly. Checks that a specific client has no sessions
+ * attached to it.
+ *
+ * @param clientId The ID of the client which we want to check.
+ */
+ private void validateInternalNoSessions(int clientId) throws Exception {
+ WifiNanClientState client = getInternalClientState(mDut, clientId);
+ collector.checkThat("Client record exists clientId=" + clientId, client, notNullValue());
+
+ Field field = WifiNanClientState.class.getDeclaredField("mSessions");
+ field.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ SparseArray<WifiNanSessionState> sessions = (SparseArray<WifiNanSessionState>) field
+ .get(client);
+
+ collector.checkThat("No sessions exist for clientId=" + clientId, sessions.size(),
+ equalTo(0));
+ }
+
+ /**
+ * Validates that the broadcast sent on NAN status change is correct.
+ *
+ * @param expectedEnabled The expected change status - i.e. are we expected
+ * to announce that NAN is enabled (true) or disabled (false).
+ */
+ private void validateCorrectNanStatusChangeBroadcast(InOrder inOrder, boolean expectedEnabled) {
+ ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+ inOrder.verify(mMockContext).sendBroadcastAsUser(intent.capture(), eq(UserHandle.ALL));
+
+ collector.checkThat("intent action", intent.getValue().getAction(),
+ equalTo(WifiNanManager.WIFI_NAN_STATE_CHANGED_ACTION));
+ collector.checkThat("intent contains wifi status key",
+ intent.getValue().getExtras().containsKey(WifiNanManager.EXTRA_WIFI_STATE),
+ equalTo(true));
+ collector.checkThat("intnent wifi status key value",
+ intent.getValue().getExtras().getInt(WifiNanManager.EXTRA_WIFI_STATE),
+ equalTo(expectedEnabled ? WifiNanManager.WIFI_NAN_STATE_ENABLED
+ : WifiNanManager.WIFI_NAN_STATE_DISABLED));
}
/*
* Utilities
*/
- private static WifiNanStateManager installNewNanStateManagerAndResetState() throws Exception {
+ private static WifiNanStateManager installNewNanStateManager()
+ throws Exception {
Constructor<WifiNanStateManager> ctr = WifiNanStateManager.class.getDeclaredConstructor();
ctr.setAccessible(true);
WifiNanStateManager nanStateManager = ctr.newInstance();
@@ -1118,30 +2318,95 @@
return WifiNanStateManager.getInstance();
}
+ private static void installMocksInStateManager(WifiNanStateManager nanStateManager,
+ WifiNanRttStateManager mockRtt, WifiNanDataPathStateManager mockDpMgr)
+ throws Exception {
+ Field field = WifiNanStateManager.class.getDeclaredField("mRtt");
+ field.setAccessible(true);
+ field.set(nanStateManager, mockRtt);
+
+ field = WifiNanStateManager.class.getDeclaredField("mDataPathMgr");
+ field.setAccessible(true);
+ field.set(nanStateManager, mockDpMgr);
+ }
+
private static void installMockWifiNanNative(WifiNanNative obj) throws Exception {
Field field = WifiNanNative.class.getDeclaredField("sWifiNanNativeSingleton");
field.setAccessible(true);
field.set(null, obj);
}
- private static Object getInternalPendingTransactionInfo(WifiNanStateManager dut,
- short transactionId) throws Exception {
- Field field = WifiNanStateManager.class.getDeclaredField("mPendingResponses");
- field.setAccessible(true);
- @SuppressWarnings("unchecked")
- SparseArray<Object> pendingResponses = (SparseArray<Object>) field.get(dut);
-
- return pendingResponses.get(transactionId);
- }
-
- private static WifiNanClientState getInternalClientState(WifiNanStateManager dut,
- int uid) throws Exception {
+ private static WifiNanClientState getInternalClientState(WifiNanStateManager dut, int clientId)
+ throws Exception {
Field field = WifiNanStateManager.class.getDeclaredField("mClients");
field.setAccessible(true);
@SuppressWarnings("unchecked")
SparseArray<WifiNanClientState> clients = (SparseArray<WifiNanClientState>) field.get(dut);
- return clients.get(uid);
+ return clients.get(clientId);
+ }
+
+ private static WifiNanSessionState getInternalSessionState(WifiNanClientState client,
+ int sessionId) throws Exception {
+ Field field = WifiNanClientState.class.getDeclaredField("mSessions");
+ field.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ SparseArray<WifiNanSessionState> sessions = (SparseArray<WifiNanSessionState>) field
+ .get(client);
+
+ return sessions.get(sessionId);
+ }
+
+ private void validateInternalSendMessageQueuesCleanedUp(int messageId) throws Exception {
+ Field field = WifiNanStateManager.class.getDeclaredField("mSm");
+ field.setAccessible(true);
+ WifiNanStateManager.WifiNanStateMachine sm =
+ (WifiNanStateManager.WifiNanStateMachine) field.get(mDut);
+
+ field = WifiNanStateManager.WifiNanStateMachine.class.getDeclaredField(
+ "mHostQueuedSendMessages");
+ field.setAccessible(true);
+ SparseArray<Message> hostQueuedSendMessages = (SparseArray<Message>) field.get(sm);
+
+ field = WifiNanStateManager.WifiNanStateMachine.class.getDeclaredField(
+ "mFwQueuedSendMessages");
+ field.setAccessible(true);
+ Map<Short, Message> fwQueuedSendMessages = (Map<Short, Message>) field.get(sm);
+
+ for (int i = 0; i < hostQueuedSendMessages.size(); ++i) {
+ Message msg = hostQueuedSendMessages.valueAt(i);
+ if (msg.getData().getInt("message_id") == messageId) {
+ collector.checkThat(
+ "Message not cleared-up from host queue. Message ID=" + messageId, msg,
+ nullValue());
+ }
+ }
+
+ for (Message msg: fwQueuedSendMessages.values()) {
+ if (msg.getData().getInt("message_id") == messageId) {
+ collector.checkThat(
+ "Message not cleared-up from firmware queue. Message ID=" + messageId, msg,
+ nullValue());
+ }
+ }
+ }
+
+ private static WifiNanNative.Capabilities getCapabilities() {
+ WifiNanNative.Capabilities cap = new WifiNanNative.Capabilities();
+ cap.maxConcurrentNanClusters = 1;
+ cap.maxPublishes = 2;
+ cap.maxSubscribes = 2;
+ cap.maxServiceNameLen = 255;
+ cap.maxMatchFilterLen = 255;
+ cap.maxTotalMatchFilterLen = 255;
+ cap.maxServiceSpecificInfoLen = 255;
+ cap.maxVsaDataLen = 255;
+ cap.maxMeshDataLen = 255;
+ cap.maxNdiInterfaces = 1;
+ cap.maxNdpSessions = 1;
+ cap.maxAppInfoLen = 255;
+ cap.maxQueuedTransmitMessages = 6;
+ return cap;
}
}
diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/BaseWifiScannerImplTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/BaseWifiScannerImplTest.java
index 1355961..cd3909e 100644
--- a/tests/wifitests/src/com/android/server/wifi/scanner/BaseWifiScannerImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/scanner/BaseWifiScannerImplTest.java
@@ -23,16 +23,16 @@
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
+import android.app.test.TestAlarmManager;
import android.content.Context;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiScanner;
import android.net.wifi.WifiScanner.ScanData;
import android.net.wifi.WifiSsid;
import android.os.SystemClock;
+import android.os.test.TestLooper;
import com.android.server.wifi.Clock;
-import com.android.server.wifi.MockAlarmManager;
-import com.android.server.wifi.MockLooper;
import com.android.server.wifi.MockResources;
import com.android.server.wifi.MockWifiMonitor;
import com.android.server.wifi.ScanDetail;
@@ -59,9 +59,9 @@
*/
public abstract class BaseWifiScannerImplTest {
@Mock Context mContext;
- MockAlarmManager mAlarmManager;
+ TestAlarmManager mAlarmManager;
MockWifiMonitor mWifiMonitor;
- MockLooper mLooper;
+ TestLooper mLooper;
@Mock WifiNative mWifiNative;
MockResources mResources;
@Mock Clock mClock;
@@ -75,8 +75,8 @@
public void setUpBase() throws Exception {
MockitoAnnotations.initMocks(this);
- mLooper = new MockLooper();
- mAlarmManager = new MockAlarmManager();
+ mLooper = new TestLooper();
+ mAlarmManager = new TestAlarmManager();
mWifiMonitor = new MockWifiMonitor();
mResources = new MockResources();
@@ -86,7 +86,7 @@
.thenReturn(mAlarmManager.getAlarmManager());
when(mContext.getResources()).thenReturn(mResources);
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime());
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime());
}
protected Set<Integer> expectedBandScanFreqs(int band) {
@@ -389,7 +389,7 @@
WifiScanner.WIFI_BAND_24_GHZ)
.build();
- long approxScanStartUs = mClock.elapsedRealtime() * 1000;
+ long approxScanStartUs = mClock.getElapsedSinceBootMillis() * 1000;
ArrayList<ScanDetail> rawResults = new ArrayList<>(Arrays.asList(
new ScanDetail(WifiSsid.createFromAsciiEncoded("TEST AP 1"),
"00:00:00:00:00:00", "", -70, 2450,
diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/SupplicantPnoScannerTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/SupplicantPnoScannerTest.java
index 39709f8..35e7ce3 100644
--- a/tests/wifitests/src/com/android/server/wifi/scanner/SupplicantPnoScannerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/scanner/SupplicantPnoScannerTest.java
@@ -22,16 +22,17 @@
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
+import android.app.test.TestAlarmManager;
import android.content.Context;
+import android.os.test.TestLooper;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiScanner;
import android.os.SystemClock;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.R;
+
import com.android.server.wifi.Clock;
-import com.android.server.wifi.MockAlarmManager;
-import com.android.server.wifi.MockLooper;
import com.android.server.wifi.MockResources;
import com.android.server.wifi.MockWifiMonitor;
import com.android.server.wifi.ScanResults;
@@ -56,9 +57,9 @@
public class SupplicantPnoScannerTest {
@Mock Context mContext;
- MockAlarmManager mAlarmManager;
+ TestAlarmManager mAlarmManager;
MockWifiMonitor mWifiMonitor;
- MockLooper mLooper;
+ TestLooper mLooper;
@Mock WifiNative mWifiNative;
MockResources mResources;
@Mock Clock mClock;
@@ -68,8 +69,8 @@
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
- mLooper = new MockLooper();
- mAlarmManager = new MockAlarmManager();
+ mLooper = new TestLooper();
+ mAlarmManager = new TestAlarmManager();
mWifiMonitor = new MockWifiMonitor();
mResources = new MockResources();
@@ -77,7 +78,7 @@
when(mContext.getSystemService(Context.ALARM_SERVICE))
.thenReturn(mAlarmManager.getAlarmManager());
when(mContext.getResources()).thenReturn(mResources);
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime());
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime());
}
/**
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 03a11dc..82d9e8d 100644
--- a/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java
@@ -22,6 +22,8 @@
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
+import android.app.test.TestAlarmManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;
@@ -35,17 +37,15 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.WorkSource;
+import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Pair;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
-import com.android.server.wifi.BidirectionalAsyncChannel;
+import com.android.internal.util.test.BidirectionalAsyncChannel;
import com.android.server.wifi.Clock;
-import com.android.server.wifi.MockAlarmManager;
-import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments;
-import com.android.server.wifi.MockLooper;
import com.android.server.wifi.ScanResults;
import com.android.server.wifi.TestUtil;
import com.android.server.wifi.WifiInjector;
@@ -78,14 +78,14 @@
public static final String TAG = "WifiScanningServiceTest";
@Mock Context mContext;
- MockAlarmManager mAlarmManager;
+ TestAlarmManager mAlarmManager;
@Mock WifiScannerImpl mWifiScannerImpl;
@Mock WifiScannerImpl.WifiScannerImplFactory mWifiScannerImplFactory;
@Mock IBatteryStats mBatteryStats;
@Mock WifiInjector mWifiInjector;
@Mock Clock mClock;
WifiMetrics mWifiMetrics;
- MockLooper mLooper;
+ TestLooper mLooper;
WifiScanningServiceImpl mWifiScanningServiceImpl;
@@ -93,7 +93,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mAlarmManager = new MockAlarmManager();
+ mAlarmManager = new TestAlarmManager();
when(mContext.getSystemService(Context.ALARM_SERVICE))
.thenReturn(mAlarmManager.getAlarmManager());
mWifiMetrics = new WifiMetrics(mClock);
@@ -103,7 +103,7 @@
new int[]{5150, 5175},
new int[]{5600, 5650, 5660});
- mLooper = new MockLooper();
+ mLooper = new TestLooper();
when(mWifiScannerImplFactory
.create(any(Context.class), any(Looper.class), any(Clock.class)))
.thenReturn(mWifiScannerImpl);
diff --git a/tests/wifitests/src/com/android/server/wifi/ByteArrayRingBufferTest.java b/tests/wifitests/src/com/android/server/wifi/util/ByteArrayRingBufferTest.java
similarity index 100%
rename from tests/wifitests/src/com/android/server/wifi/ByteArrayRingBufferTest.java
rename to tests/wifitests/src/com/android/server/wifi/util/ByteArrayRingBufferTest.java
diff --git a/tests/wifitests/src/com/android/server/wifi/util/ScanDetailUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java
similarity index 93%
rename from tests/wifitests/src/com/android/server/wifi/util/ScanDetailUtilTest.java
rename to tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java
index 07d2521..275a40b 100644
--- a/tests/wifitests/src/com/android/server/wifi/util/ScanDetailUtilTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java
@@ -33,10 +33,10 @@
import java.util.Arrays;
/**
- * Unit tests for {@link com.android.server.wifi.util.ScanDetailUtil}.
+ * Unit tests for {@link com.android.server.wifi.util.ScanResultUtil}.
*/
@SmallTest
-public class ScanDetailUtilTest {
+public class ScanResultUtilTest {
@Test
public void convertScanResult() {
@@ -49,7 +49,7 @@
createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8))
};
- ScanDetail output = ScanDetailUtil.toScanDetail(input);
+ ScanDetail output = ScanResultUtil.toScanDetail(input);
validateScanDetail(input, output);
}
@@ -66,7 +66,7 @@
};
input.anqpLines = Arrays.asList("LINE 1", "line 2", "Line 3");
- ScanDetail output = ScanDetailUtil.toScanDetail(input);
+ ScanDetail output = ScanResultUtil.toScanDetail(input);
validateScanDetail(input, output);
}
@@ -80,7 +80,7 @@
createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8))
};
- ScanDetail output = ScanDetailUtil.toScanDetail(input);
+ ScanDetail output = ScanResultUtil.toScanDetail(input);
validateScanDetail(input, output);
}
diff --git a/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java
new file mode 100644
index 0000000..7b06164
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java
@@ -0,0 +1,402 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wifi.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.*;
+
+import android.net.IpConfiguration;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+import com.android.server.wifi.WifiConfigurationTestUtil;
+import com.android.server.wifi.util.XmlUtil.IpConfigurationXmlUtil;
+import com.android.server.wifi.util.XmlUtil.NetworkSelectionStatusXmlUtil;
+import com.android.server.wifi.util.XmlUtil.WifiConfigurationXmlUtil;
+import com.android.server.wifi.util.XmlUtil.WifiEnterpriseConfigXmlUtil;
+
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.util.XmlUtil}.
+ */
+@SmallTest
+public class XmlUtilTest {
+
+ private static final String TEST_PACKAGE_NAME = "XmlUtilPackage";
+ private static final String TEST_STATIC_IP_GATEWAY_ADDRESS = "192.168.48.1";
+ private static final String TEST_DUMMY_CONFIG_KEY = "XmlUtilDummyConfigKey";
+ private static final String TEST_IDENTITY = "XmlUtilTestIdentity";
+ private static final String TEST_ANON_IDENTITY = "XmlUtilTestAnonIdentity";
+ private static final String TEST_PASSWORD = "XmlUtilTestPassword";
+ private static final String TEST_CLIENT_CERT = "XmlUtilTestClientCert";
+ private static final String TEST_CA_CERT = "XmlUtilTestCaCert";
+ private static final String TEST_SUBJECT_MATCH = "XmlUtilTestSubjectMatch";
+ private static final String TEST_ENGINE = "XmlUtilTestEngine";
+ private static final String TEST_ENGINE_ID = "XmlUtilTestEngineId";
+ private static final String TEST_PRIVATE_KEY_ID = "XmlUtilTestPrivateKeyId";
+ private static final String TEST_ALTSUBJECT_MATCH = "XmlUtilTestAltSubjectMatch";
+ private static final String TEST_DOM_SUFFIX_MATCH = "XmlUtilTestDomSuffixMatch";
+ private static final String TEST_CA_PATH = "XmlUtilTestCaPath";
+ private static final int TEST_EAP_METHOD = WifiEnterpriseConfig.Eap.PEAP;
+ private static final int TEST_PHASE2_METHOD = WifiEnterpriseConfig.Phase2.MSCHAPV2;
+ private final String mXmlDocHeader = "XmlUtilTest";
+
+ /**
+ * Verify that a open WifiConfiguration is serialized & deserialized correctly.
+ */
+ @Test
+ public void testOpenWifiConfigurationSerializeDeserialize()
+ throws IOException, XmlPullParserException {
+ serializeDeserializeWifiConfiguration(WifiConfigurationTestUtil.createOpenNetwork());
+ }
+
+ /**
+ * Verify that a open hidden WifiConfiguration is serialized & deserialized correctly.
+ */
+ @Test
+ public void testOpenHiddenWifiConfigurationSerializeDeserialize()
+ throws IOException, XmlPullParserException {
+ serializeDeserializeWifiConfiguration(WifiConfigurationTestUtil.createOpenHiddenNetwork());
+ }
+
+ /**
+ * Verify that a psk WifiConfiguration is serialized & deserialized correctly.
+ */
+ @Test
+ public void testPskWifiConfigurationSerializeDeserialize()
+ throws IOException, XmlPullParserException {
+ serializeDeserializeWifiConfiguration(WifiConfigurationTestUtil.createPskNetwork());
+ }
+
+ /**
+ * Verify that a psk hidden WifiConfiguration is serialized & deserialized correctly.
+ */
+ @Test
+ public void testPskHiddenWifiConfigurationSerializeDeserialize()
+ throws IOException, XmlPullParserException {
+ serializeDeserializeWifiConfiguration(WifiConfigurationTestUtil.createPskHiddenNetwork());
+ }
+
+ /**
+ * Verify that a WEP WifiConfiguration is serialized & deserialized correctly.
+ */
+ @Test
+ public void testWepWifiConfigurationSerializeDeserialize()
+ throws IOException, XmlPullParserException {
+ serializeDeserializeWifiConfiguration(WifiConfigurationTestUtil.createWepNetwork());
+ }
+
+ /**
+ * Verify that a EAP WifiConfiguration is serialized & deserialized correctly only for
+ * ConfigStore.
+ */
+ @Test
+ public void testEapWifiConfigurationSerializeDeserialize()
+ throws IOException, XmlPullParserException {
+ serializeDeserializeWifiConfigurationForConfigStore(
+ WifiConfigurationTestUtil.createEapNetwork());
+ }
+
+ /**
+ * Verify that a static IpConfiguration with PAC proxy is serialized & deserialized correctly.
+ */
+ @Test
+ public void testStaticIpConfigurationWithPacProxySerializeDeserialize()
+ throws IOException, XmlPullParserException {
+ serializeDeserializeIpConfiguration(
+ WifiConfigurationTestUtil.createStaticIpConfigurationWithPacProxy());
+ }
+
+ /**
+ * Verify that a static IpConfiguration with static proxy is serialized & deserialized correctly.
+ */
+ @Test
+ public void testStaticIpConfigurationWithStaticProxySerializeDeserialize()
+ throws IOException, XmlPullParserException {
+ serializeDeserializeIpConfiguration(
+ WifiConfigurationTestUtil.createStaticIpConfigurationWithStaticProxy());
+ }
+
+ /**
+ * Verify that a partial static IpConfiguration with PAC proxy is serialized & deserialized
+ * correctly.
+ */
+ @Test
+ public void testPartialStaticIpConfigurationWithPacProxySerializeDeserialize()
+ throws IOException, XmlPullParserException {
+ serializeDeserializeIpConfiguration(
+ WifiConfigurationTestUtil.createPartialStaticIpConfigurationWithPacProxy());
+ }
+
+ /**
+ * Verify that a DHCP IpConfiguration with PAC proxy is serialized & deserialized
+ * correctly.
+ */
+ @Test
+ public void testDHCPIpConfigurationWithPacProxySerializeDeserialize()
+ throws IOException, XmlPullParserException {
+ serializeDeserializeIpConfiguration(
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy());
+ }
+
+ /**
+ * Verify that a DHCP IpConfiguration with Static proxy is serialized & deserialized
+ * correctly.
+ */
+ @Test
+ public void testDHCPIpConfigurationWithStaticProxySerializeDeserialize()
+ throws IOException, XmlPullParserException {
+ serializeDeserializeIpConfiguration(
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithStaticProxy());
+ }
+
+ /**
+ * Verify that a EAP WifiConfiguration is serialized & deserialized correctly for config store.
+ * This basically exercises all the elements being serialized in config store.
+ */
+ @Test
+ public void testEapWifiConfigurationSerializeDeserializeForConfigStore()
+ throws IOException, XmlPullParserException {
+ WifiConfiguration configuration = WifiConfigurationTestUtil.createEapNetwork();
+ configuration.linkedConfigurations = new HashMap<>();
+ configuration.linkedConfigurations.put(TEST_DUMMY_CONFIG_KEY, Integer.valueOf(1));
+ configuration.defaultGwMacAddress = TEST_STATIC_IP_GATEWAY_ADDRESS;
+ configuration.validatedInternetAccess = true;
+ configuration.noInternetAccessExpected = true;
+ configuration.userApproved = WifiConfiguration.USER_UNSPECIFIED;
+ configuration.meteredHint = true;
+ configuration.useExternalScores = true;
+ configuration.numAssociation = 5;
+ configuration.lastUpdateUid = configuration.lastConnectUid = configuration.creatorUid;
+ configuration.creatorName = configuration.lastUpdateName = TEST_PACKAGE_NAME;
+ configuration.creationTime = "04-04-2016";
+
+ serializeDeserializeWifiConfigurationForConfigStore(configuration);
+ }
+
+ /**
+ * Verify that an enabled network selection status object is serialized & deserialized
+ * correctly.
+ */
+ @Test
+ public void testEnabledNetworkSelectionStatusSerializeDeserialize()
+ throws IOException, XmlPullParserException {
+ NetworkSelectionStatus status = new NetworkSelectionStatus();
+ status.setNetworkSelectionStatus(NetworkSelectionStatus.NETWORK_SELECTION_ENABLED);
+ status.setNetworkSelectionDisableReason(NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
+ status.setConnectChoice(TEST_DUMMY_CONFIG_KEY);
+ status.setConnectChoiceTimestamp(867889);
+ status.setHasEverConnected(true);
+ serializeDeserializeNetworkSelectionStatus(status);
+ }
+
+ /**
+ * Verify that a temporarily disabled network selection status object is serialized &
+ * deserialized correctly.
+ */
+ @Test
+ public void testTemporarilyDisabledNetworkSelectionStatusSerializeDeserialize()
+ throws IOException, XmlPullParserException {
+ NetworkSelectionStatus status = new NetworkSelectionStatus();
+ status.setNetworkSelectionStatus(
+ NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED);
+ status.setNetworkSelectionDisableReason(
+ NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION);
+ serializeDeserializeNetworkSelectionStatus(status);
+ }
+
+ /**
+ * Verify that a WifiEnterpriseConfig object is serialized & deserialized correctly.
+ */
+ @Test
+ public void testWifiEnterpriseConfigSerializeDeserialize()
+ throws IOException, XmlPullParserException {
+ WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+ config.setFieldValue(WifiEnterpriseConfig.IDENTITY_KEY, TEST_IDENTITY);
+ config.setFieldValue(WifiEnterpriseConfig.ANON_IDENTITY_KEY, TEST_ANON_IDENTITY);
+ config.setFieldValue(WifiEnterpriseConfig.PASSWORD_KEY, TEST_PASSWORD);
+ config.setFieldValue(WifiEnterpriseConfig.CLIENT_CERT_KEY, TEST_CLIENT_CERT);
+ config.setFieldValue(WifiEnterpriseConfig.CA_CERT_KEY, TEST_CA_CERT);
+ config.setFieldValue(WifiEnterpriseConfig.SUBJECT_MATCH_KEY, TEST_SUBJECT_MATCH);
+ config.setFieldValue(WifiEnterpriseConfig.ENGINE_KEY, TEST_ENGINE);
+ config.setFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, TEST_ENGINE_ID);
+ config.setFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, TEST_PRIVATE_KEY_ID);
+ config.setFieldValue(WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY, TEST_ALTSUBJECT_MATCH);
+ config.setFieldValue(WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY, TEST_DOM_SUFFIX_MATCH);
+ config.setFieldValue(WifiEnterpriseConfig.CA_PATH_KEY, TEST_CA_PATH);
+ config.setEapMethod(TEST_EAP_METHOD);
+ config.setPhase2Method(TEST_PHASE2_METHOD);
+ serializeDeserializeWifiEnterpriseConfig(config);
+ }
+
+ private byte[] serializeWifiConfigurationForBackup(WifiConfiguration configuration)
+ throws IOException, XmlPullParserException {
+ final XmlSerializer out = new FastXmlSerializer();
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+ XmlUtil.writeDocumentStart(out, mXmlDocHeader);
+ WifiConfigurationXmlUtil.writeToXmlForBackup(out, configuration);
+ XmlUtil.writeDocumentEnd(out, mXmlDocHeader);
+ return outputStream.toByteArray();
+ }
+
+ private byte[] serializeWifiConfigurationForConfigStore(
+ WifiConfiguration configuration)
+ throws IOException, XmlPullParserException {
+ final XmlSerializer out = new FastXmlSerializer();
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+ XmlUtil.writeDocumentStart(out, mXmlDocHeader);
+ WifiConfigurationXmlUtil.writeToXmlForConfigStore(out, configuration);
+ XmlUtil.writeDocumentEnd(out, mXmlDocHeader);
+ return outputStream.toByteArray();
+ }
+
+ private Pair<String, WifiConfiguration> deserializeWifiConfiguration(byte[] data)
+ throws IOException, XmlPullParserException {
+ // Deserialize the configuration object.
+ final XmlPullParser in = Xml.newPullParser();
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
+ in.setInput(inputStream, StandardCharsets.UTF_8.name());
+ XmlUtil.gotoDocumentStart(in, mXmlDocHeader);
+ return WifiConfigurationXmlUtil.parseFromXml(in, in.getDepth());
+ }
+
+ /**
+ * This helper method tests the serialization for backup/restore.
+ */
+ private void serializeDeserializeWifiConfigurationForBackupRestore(
+ WifiConfiguration configuration)
+ throws IOException, XmlPullParserException {
+ Pair<String, WifiConfiguration> retrieved;
+ // Test serialization/deserialization for config store.
+ retrieved =
+ deserializeWifiConfiguration(
+ serializeWifiConfigurationForBackup(configuration));
+ assertEquals(retrieved.first, retrieved.second.configKey());
+ WifiConfigurationTestUtil.assertConfigurationEqualForBackup(
+ configuration, retrieved.second);
+ }
+
+ /**
+ * This helper method tests the serialization for config store.
+ */
+ private void serializeDeserializeWifiConfigurationForConfigStore(
+ WifiConfiguration configuration)
+ throws IOException, XmlPullParserException {
+ // Reset enterprise config because this needs to be serialized/deserialized separately.
+ configuration.enterpriseConfig = new WifiEnterpriseConfig();
+ Pair<String, WifiConfiguration> retrieved;
+ // Test serialization/deserialization for config store.
+ retrieved =
+ deserializeWifiConfiguration(
+ serializeWifiConfigurationForConfigStore(configuration));
+ assertEquals(retrieved.first, retrieved.second.configKey());
+ WifiConfigurationTestUtil.assertConfigurationEqualForConfigStore(
+ configuration, retrieved.second);
+ }
+
+ /**
+ * This helper method tests both the serialization for backup/restore and config store.
+ */
+ private void serializeDeserializeWifiConfiguration(WifiConfiguration configuration)
+ throws IOException, XmlPullParserException {
+ Pair<String, WifiConfiguration> retrieved;
+ // Test serialization/deserialization for backup first.
+ serializeDeserializeWifiConfigurationForBackupRestore(configuration);
+
+ // Test serialization/deserialization for config store.
+ serializeDeserializeWifiConfigurationForConfigStore(configuration);
+ }
+
+ private void serializeDeserializeIpConfiguration(IpConfiguration configuration)
+ throws IOException, XmlPullParserException {
+ // Serialize the configuration object.
+ final XmlSerializer out = new FastXmlSerializer();
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+ XmlUtil.writeDocumentStart(out, mXmlDocHeader);
+ IpConfigurationXmlUtil.writeToXml(out, configuration);
+ XmlUtil.writeDocumentEnd(out, mXmlDocHeader);
+
+ // Deserialize the configuration object.
+ final XmlPullParser in = Xml.newPullParser();
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
+ in.setInput(inputStream, StandardCharsets.UTF_8.name());
+ XmlUtil.gotoDocumentStart(in, mXmlDocHeader);
+ IpConfiguration retrievedConfiguration =
+ IpConfigurationXmlUtil.parseFromXml(in, in.getDepth());
+ assertEquals(configuration, retrievedConfiguration);
+ }
+
+ private void serializeDeserializeNetworkSelectionStatus(NetworkSelectionStatus status)
+ throws IOException, XmlPullParserException {
+ // Serialize the configuration object.
+ final XmlSerializer out = new FastXmlSerializer();
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+ XmlUtil.writeDocumentStart(out, mXmlDocHeader);
+ NetworkSelectionStatusXmlUtil.writeToXml(out, status);
+ XmlUtil.writeDocumentEnd(out, mXmlDocHeader);
+
+ // Deserialize the configuration object.
+ final XmlPullParser in = Xml.newPullParser();
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
+ in.setInput(inputStream, StandardCharsets.UTF_8.name());
+ XmlUtil.gotoDocumentStart(in, mXmlDocHeader);
+ NetworkSelectionStatus retrievedStatus =
+ NetworkSelectionStatusXmlUtil.parseFromXml(in, in.getDepth());
+ WifiConfigurationTestUtil.assertNetworkSelectionStatusEqualForConfigStore(
+ status, retrievedStatus);
+ }
+
+ private void serializeDeserializeWifiEnterpriseConfig(WifiEnterpriseConfig config)
+ throws IOException, XmlPullParserException {
+ // Serialize the configuration object.
+ final XmlSerializer out = new FastXmlSerializer();
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+ XmlUtil.writeDocumentStart(out, mXmlDocHeader);
+ WifiEnterpriseConfigXmlUtil.writeToXml(out, config);
+ XmlUtil.writeDocumentEnd(out, mXmlDocHeader);
+
+ // Deserialize the configuration object.
+ final XmlPullParser in = Xml.newPullParser();
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
+ in.setInput(inputStream, StandardCharsets.UTF_8.name());
+ XmlUtil.gotoDocumentStart(in, mXmlDocHeader);
+ WifiEnterpriseConfig retrievedConfig =
+ WifiEnterpriseConfigXmlUtil.parseFromXml(in, in.getDepth());
+ WifiConfigurationTestUtil.assertWifiEnterpriseConfigEqualForConfigStore(
+ config, retrievedConfig);
+ }
+}